LINUX 中的mmap浅析

原创LINUX系统编程水平有限,参考UNIX系统编程手册

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做私有匿名映射来完成一个小的线程间同步问题程序,用这片
内存区域来做线程间通信

点击(此处)折叠或打开

  1. #include<iostream>
  2. #include<sys/mman.h>
  3. #include<pthread.h>
  4. #include<string.h>
  5. #define uint unsigned int
  6. #define MMSIZE (uint)(1<<23)
  7. #define MSIZE (uint)(1<<20)
  8. #define MPRT (uint)(1<<16)
  9. using namespace std;
  10. class tc
  11. {
  12.         private:
  13.                 uint a;
  14.         public:
  15.                 tc():a(1)
  16.         {
  17.                 ;
  18.         }
  19.                 ~tc()
  20.                 {
  21.                         ;
  22.                 }
  23.                 void add()
  24.                 {
  25.                         a=a+1;
  26.                 }
  27.                 void set()
  28.                 {
  29.                         a=1;
  30.                 }
  31.                 void prt(int i)
  32.                 {
  33.                         if(!(i%(MPRT)))
  34.                         {
  35.                                 cout<<":"<<a;
  36.                         }
  37.                 }
  38. };
  39. struct tt
  40. {
  41.         tc* p1;
  42.         pthread_mutex_t* p2;
  43. };
  44. void* test(void* arg)
  45. {
  46.         int i = 0;
  47.         tt* s = NULL;
  48.         s = (tt*)arg;
  49.         int maxloop = 50;
  50.         while(maxloop--)
  51.         {
  52.                 i = MSIZE;
  53.                 pthread_mutex_lock(s->p2);//MUTEX保护临界区
  54.                 cout<<"Thread:"<<pthread_self()<<" work now!!!\n";
  55.                 for(;i--;)
  56.                 {
  57.                         (s->p1+i)->prt(i);
  58.                         (s->p1+i)->add();
  59.                 }
  60.                 cout<<"\n";
  61.                 pthread_mutex_unlock(s->p2);//解锁
  62.         }
  63. }
  64. int main(void)
  65. {
  66.         pthread_t tid[3];
  67.         pthread_mutex_t pmut;
  68.         tt s1;
  69.         tc* p = (tc*)mmap(NULL,MMSIZE,PROT_READ|PROT_WRITE,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);//MMAP分配一个匿名私有虚拟内存用于线程间通信
  70.         pthread_mutex_init(&pmut,NULL);
  71.         s1.p1 = p;
  72.         s1.p2 = &pmut;
  73.         int i = MSIZE+1;
  74.         for(;i--;)
  75.         {
  76.                 (p+i)->set();//初始化所有的a=1
  77.         }
  78.         for(i=0;i<3;i++)
  79.         {
  80.                 pthread_create(tid+i,NULL,test,(void*)&s1);//建立3个线程
  81.         }
  82.         for(i = 0;i<3;i++)
  83.         {
  84.                 pthread_join( *(tid+i) , NULL);//堵塞回收线程
  85.         }
  86.         pthread_mutex_destroy(&pmut);
  87.         munmap(p,MMSIZE);
  88. }

同时我们也观察到了线程由于失去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

作者微信:

时间: 2024-10-29 00:35:26

LINUX 中的mmap浅析的相关文章

Linux中brk()系统调用,sbrk(),mmap(),malloc(),calloc()的异同【转】

转自:http://blog.csdn.net/kobbee9/article/details/7397010 brk和sbrk主要的工作是实现虚拟内存到内存的映射.在GNUC中,内存分配是这样的:       每个进程可访问的虚拟内存空间为3G,但在程序编译时,不可能也没必要为程序分配这么大的空间,只分配并不大的数据段空间,程序中动态分配的空间就是从这一块分配的.如果这块空间不够,malloc函数族(realloc,calloc等)就调用sbrk函数将数据段的下界移动,sbrk函数在内核的管理

浅析Linux中的时间编程和实现原理(一) Linux应用层的时间编程

引子 我们都生活在时间中,但却无法去思考它.什么是时间呢?似乎这是一个永远也不能被回答的问题.然而作为一个程序员,在工作中,总有那么几次我必须思考什么是时间.比如,需要知道一段代码运行了多久:要在 log 文件中记录事件发生时的时间戳:再比如需要一个定时器以便能够定期做某些计算机操作.我发现,在计算机世界中,时间在不同场合也往往有不同的含义,让试图思考它的人感到迷茫.但值得庆幸的是,Linux 中的时间终究是可以理解的.因此我打算讨论一下有关时间的话题,尝试着深入理解 Linux 系统中 C 语

Linux中如何查找或删除正在使用某文件的进程?

Linux中如何查找或删除正在使用某文件的进程? 我们可以使用fuser命令,示例如下: fuser filehttp://www.aliyun.com/zixun/aggregation/11696.html">namefuser -k filename fuser命令是用来显示所有正在使用着指定的file, file system 或者 sockets的进程信息. fuser的其他有用的参数 -a Show all files specified on the command line

Linux中select poll和epoll的区别

select的本质是采用32个整数的32位,即32*32= 1024来标识,fd值为1-1024.当fd的值超过1024限制时,就必须修改FD_SETSIZE的大小.这个时候就可以标识32*max值范围的fd. 对于单进程多线程,每个线程处理多个fd的情况,select是不适合的. 1.所有的线程均是从1-32*max进行扫描,每个线程处理的均是一段fd值,这样做有点浪费 2.1024上限问题,一个处理多个用户的进程,fd值远远大于1024 所以这个时候应该采用poll, poll传递的是数组头

linux中PHP dirname(

  在php 中dirname() 函数返回路径中的目录部分,__FILE__而当前运行文件的完整路径和文件名.如果用在被包含文件中,则返回被包含的文件名.这是一个魔法变量(预定义常量),在windows中没有问题但在linux中路径出现的问题,下面我们一起来看看路径问题解决方法. 近期在给wordpress开发模板功能时发现,直接使用include("文件名")的形式调用其他php代码片段时会出现路径错误.之前服务器环境一直都是iis,未曾出现过类似的BUG,但换成linux服务器后

linux中的&amp;quot;瑞士军刀&amp;quot;

linux中的"瑞士军刀" busybox 俗称linux中的瑞士军刀,它类似于linux系统中bash 的一个缩微版,常用于嵌入式设备中,例如你的android手机中等等.busybox作为一个开源的应用,它的解析命令行的应用是值得学习的 http://busybox.net/

Linux中让显示器不休眠?

Linux中让显示器不休眠? 我们可以使用setterm命令: 操作如下: setterm -blank 0setterm -blank n (n为等待时间) setterm命令的其它选项: setterm: Argument error, usage setterm&http://www.aliyun.com/zixun/aggregation/37954.html">nbsp; [ -term terminal_name ]  [ -reset ]  [ -initialize

Linux中如何运行多个X窗口?

Linux中如何运行多个X窗口? startx默认以display :0.0起第一个X,通过传递参数给Xserver可以起多个X: # startx -- :1.0# startx -- :2.0... 然后用Ctrl-Alt-F7/F8...切换

linux 中配置apache 网站 编码设置为gb2312 浏览乱码

问题描述 linux 中配置apache 网站 编码设置为gb2312 浏览乱码 httpd.conf 添加了 AddDefaultCharset GB2312? 网页添加了 浏览器浏览的时候默认编码格式还是utf-8 显示乱码 解决方案 将环境变量LANG设置LANG="zh_CN GBK"