您好,登錄后才能下訂單哦!
本篇內容主要講解“LINUX中的mmap是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“LINUX中的mmap是什么”吧!
LINUX 中的mmap淺析
一、mmap基本原理和分類
在LINUX中我們可以使用mmap用來在進程虛擬地址空間中分配創建一片虛擬內存地址映射
其可以是
1、文件映射
使用文件內容初始化內存
2、匿名映射
初始化全為0的內存空間(calloc也可以)
下面配圖來自UNIX系統編程手冊
而對于是否共享又分為
1、私有映射(MAP_PRIVATE)
多進程間數據共享,修改不反應到磁盤實際文件,
私有寫時復制實現
2、共享映射(MAP_SHARED)
多進程間數據共享,修改反應到磁盤實際文件中。
那么總結起來有4種組合
1、私有文件映射
多個進程使用同樣的物理內存頁進行初始化,但是各個進程
對內存文件的修改不會共享,也不會反應到物理文件中,比如
我們LINUX .so動態庫文件就采用這種方式映射到各個進程虛擬
地址空間中
2、私有匿名映射
mmap會創建一個新的映射,各個進程不共享,這種使用主要用于
分配內存(malloc分配大內存會調用mmap)。
3、共享文件映射
多個進程通過虛擬內存技術共享同樣的物理內存空間,對內存文件
的修改會反應到實際物理文件中,他也是進程間通信(IPC)的一種機制
4、共享匿名映射
這種機制在進行fork的時候不會采用寫時復制,父子進程完全共享
同樣的物理內存頁,這也就實現了父子進程通信(IPC).
下面也是UNIX系統編程手冊截圖
在/proc/PID/maps下我們可以找到一個當前進程使用mmap創建的映射比如:
379a000000-379a016000 r-xp 00000000 08:03 12320771 /lib64/libgcc_s-4.4.7-20120601.so.1
379a016000-379a215000 ---p 00016000 08:03 12320771 /lib64/libgcc_s-4.4.7-20120601.so.1
379a215000-379a216000 rw-p 00015000 08:03 12320771 /lib64/libgcc_s-4.4.7-20120601.so.1
379a400000-379a4e8000 r-xp 00000000 08:03 9700201 /usr/lib64/libstdc++.so.6.0.13
379a4e8000-379a6e8000 ---p 000e8000 08:03 9700201 /usr/lib64/libstdc++.so.6.0.13
379a6e8000-379a6ef000 r--p 000e8000 08:03 9700201 /usr/lib64/libstdc++.so.6.0.13
379a6ef000-379a6f1000 rw-p 000ef000 08:03 9700201 /usr/lib64/libstdc++.so.6.0.13
對于解釋可以參考UNIX系統編程手冊如下描述
二、mmap函數原型
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
參數有點多
addr:映射放到哪里(虛擬地址),一般傳NULL,讓內核自己決定
length:映射的大小(直接),最小是系統頁的整數倍(4K)
port:位圖掩碼
PROT_NONE 不能訪問
PROT_READ 可讀取
PROT_WRITE 可修改
PROT_EXEC 可執行
非法訪問或報SIGSEGV段錯誤信號
flags:位圖掩碼
MAP_ANONYMOUS:創建一個匿名映射
MAP_PRIVATE:私有映射
MAP_SHARED:共享映射(注意并不能保證一定實際寫入物理磁盤(MSYNC))
MAP_FIXED:addr必須是頁對齊地址
其他標示不做解釋
fd:映射文件的文件描述符
offset:從文件的哪個位置開始映射,必須是系統頁的整數倍(4K)
返回值:
成功返回映射的虛擬內存地址的起始地址,失敗返回MAP_FAILED
三、建立匿名映射
1、指針MAP_ANONYMOUS,并且fd指定為0
2、打開/dev/zero文件將文件描述符傳遞給mmap()
匿名映射會分配初始化全為0的虛擬內存空間
四、其他函數
int msync(void *addr, size_t length, int flags);
用于將kener buffer的數據同步到磁盤
int munmap(void *addr, size_t length);
用于解除映射
五、程序實例
下面我們通過mmap做私有匿名映射來完成一個小的線程間同步問題程序,用這片
內存區域來做線程間通信
點擊(此處)折疊或打開
#include<iostream>
#include<sys/mman.h>
#include<pthread.h>
#include<string.h>
#define uint unsigned int
#define MMSIZE (uint)(1<<23)
#define MSIZE (uint)(1<<20)
#define MPRT (uint)(1<<16)
using namespace std;
class tc
{
private:
uint a;
public:
tc():a(1)
{
;
}
~tc()
{
;
}
void add()
{
a=a+1;
}
void set()
{
a=1;
}
void prt(int i)
{
if(!(i%(MPRT)))
{
cout<<":"<<a;
}
}
};
struct tt
{
tc* p1;
pthread_mutex_t* p2;
};
void* test(void* arg)
{
int i = 0;
tt* s = NULL;
s = (tt*)arg;
int maxloop = 50;
while(maxloop--)
{
i = MSIZE;
pthread_mutex_lock(s->p2);//MUTEX保護臨界區
cout<<"Thread:"<<pthread_self()<<" work now!!!\n";
for(;i--;)
{
(s->p1+i)->prt(i);
(s->p1+i)->add();
}
cout<<"\n";
pthread_mutex_unlock(s->p2);//解鎖
}
}
int main(void)
{
pthread_t tid[3];
pthread_mutex_t pmut;
tt s1;
tc* p = (tc*)mmap(NULL,MMSIZE,PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);//MMAP分配一個匿名私有虛擬內存用于線程間通信
pthread_mutex_init(&pmut,NULL);
s1.p1 = p;
s1.p2 = &pmut;
int i = MSIZE+1;
for(;i--;)
{
(p+i)->set();//初始化所有的a=1
}
for(i=0;i<3;i++)
{
pthread_create(tid+i,NULL,test,(void*)&s1);//建立3個線程
}
for(i = 0;i<3;i++)
{
pthread_join( *(tid+i) , NULL);//堵塞回收線程
}
pthread_mutex_destroy(&pmut);
munmap(p,MMSIZE);
}
同時我們也觀察到了線程由于失去CPU而放棄執行其他線程得到CPU繼續執行,由于
我們使用MUTEX保護臨界區這個數數還是正常進行。最后正常數到了150
Thread:140545405572864 work now!!!
:32:32:32:32:32:32:32:32:32:32:32:32:32:32:32:32
Thread:140545397180160 work now!!! (線程140545405572864 失去CPU線程140545397180160執行)
:33:33:33:33:33:33:33:33:33:33:33:33:33:33:33:33
..................
Thread:140545397180160 work now!!!
:58:58:58:58:58:58:58:58:58:58:58:58:58:58:58:58
Thread:140545405572864 work now!!!(線程140545405572864重新獲得CPU)
:59:59:59:59:59:59:59:59:59:59:59:59:59:59:59:59
............
Thread:140545388787456 work now!!!
:150:150:150:150:150:150:150:150:150:150:150:150:150:150:150:150
到此,相信大家對“LINUX中的mmap是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。