LINUX 中的mmap浅析

一、mmap基本原理和分类

在LINUX中我们可以使用mmap用来在进程虚拟地址空间中分配创建一片虚拟内存地址映射

其可以是

1、文件映射

使用文件内容初始化内存

2、匿名映射

初始化全为0的内存空间(calloc也可以)

下面配图来自UNIX系统编程手册

0655f6a3f63e9dd8aad2ecd59632d361.png

而对于是否共享又分为

1、私有映射(MAP_PRIVATE)

多进程间数据共享,修改不反应到磁盘实际文件,

私有写时复制实现

2、共享映射(MAP_SHARED)

多进程间数据共享,修改反应到磁盘实际文件中。

那么总结起来有4种组合

1、私有文件映射

多个进程使用同样的物理内存页进行初始化,但是各个进程

对内存文件的修改不会共享,也不会反应到物理文件中,比如

我们LINUX .so动态库文件就采用这种方式映射到各个进程虚拟

地址空间中

2、私有匿名映射

mmap会创建一个新的映射,各个进程不共享,这种使用主要用于

分配内存(malloc分配大内存会调用mmap)。

3、共享文件映射

多个进程通过虚拟内存技术共享同样的物理内存空间,对内存文件

的修改会反应到实际物理文件中,他也是进程间通信(IPC)的一种机制

4、共享匿名映射

这种机制在进行fork的时候不会采用写时复制,父子进程完全共享

同样的物理内存页,这也就实现了父子进程通信(IPC).

下面也是UNIX系统编程手册截图

dac2ffbca6c4f9733217bf4ead849c3d.png在/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系统编程手册如下描述

a20c96038b453669165116020a8881ee.png   

二、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

#include

#include

#include

#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<

}

}

};

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<

for(;i--;)

{

(s->p1+i)->prt(i);

(s->p1+i)->add();

}

cout<

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