免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1309 | 回复: 4
打印 上一主题 下一主题

[内核入门] mmap() [复制链接]

论坛徽章:
13
15-16赛季CBA联赛之八一
日期:2016-07-08 21:00:1415-16赛季CBA联赛之同曦
日期:2017-02-15 14:26:1515-16赛季CBA联赛之佛山
日期:2017-02-20 14:19:2615-16赛季CBA联赛之青岛
日期:2017-05-07 16:49:1115-16赛季CBA联赛之广夏
日期:2017-07-30 09:13:1215-16赛季CBA联赛之广东
日期:2018-07-05 22:34:3615-16赛季CBA联赛之江苏
日期:2018-09-03 12:10:2115-16赛季CBA联赛之上海
日期:2018-09-25 03:49:2215-16赛季CBA联赛之广东
日期:2018-09-25 04:09:12
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2016-10-12 13:10 |只看该作者 |倒序浏览
本帖最后由 _nosay 于 2016-10-12 13:18 编辑

  • mmap()用途
    可以说如果对“虚拟地址-物理地址”以及文件系统相关内容没有一定程度的理解,光是通过man手册的使用说明,是不可能完全理解这个函数的。
    至少,很多人对mmap()肯定存在以下误解:
      ① 进程间通信时才会想到它
      ② 跟文件有关,涉及磁盘操作,一定很慢

  • 用于进程间通信
    想想日常的通信,不管是古代还是现在,都会“约到同一个地方”,比如一起到茶馆喝茶、写信、电话,都会有一个通道,将通信的双方联系起来。
    对于程序来讲,不同进程的用户空间独立,用相同的虚拟地址,根本走不到同一个地方(比如看新闻时,发现习大大走到沙发旁了,你也走到你家的沙发旁,你们就能通信了么)。那么不同的用户进程怎么可以通信呢?

    磁盘等硬件,都是由内核直接操作,比如读完文件,先是在内核空间有文件的内容,然后用户空间才有机会访问到:
    方式①:直接将这块实际的内存,映射到用户空间的虚拟地址(这样就像在个人家里,放了一个控制公共场所的遥控器)
    方式②:在实际内存中复制一份,并将复制的那份映射到用户空间(这样就纯粹属于个人了)
   

    mmap()相对于上图只不过多了一道映射,即将用户空间的虚拟地址与文件信息建立映射,而内核中所操作该文件相关的内存只有一份,从而间接达到不同进程中的虚拟地址,访问到相同实际内存地址的目的。
   

  • 效率
    通过以上分析可以看出,进程1通过往建立映射的虚拟区间写,进程2就可以通过相应的虚拟区间访问进程1写的内容,因为在实际内存对应的都是同一块。
    那么进程1写的时候,会触发磁盘的写操作吗?
    内核只会将写的页标志为脏,到需要的时候才写回磁盘(《Linux内核源代码情景分析》第5章:文件系统),所以对mmap()映射的内存操作,几乎不涉及磁盘操作,更甚至父子进程之间利用mmap()进行通信,连打开文件都不用,即“匿名映射”,这跟Linux内核创建进程使用的机制有关(详见《Linux内核源代码情况分析》4.2节:进程的创建)。
    另外,相比于read(),它有两点不同:
    ① 在实际内存中少了一次复制
    ② 在读一个大文件时,mmap()调用完成,并不表示内容就真的从磁盘全部读到内存了,其实道理和交换文件一样,虚拟页面没有对应的物理页面映射,当访问到时,才分配物理页面,并根据映射指向的磁盘位置,从磁盘读入内容
    这两点往往也算是优点,不过也要根据具体的使用场合来看。

    父子进程通过匿名映射通信示例代码:
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <string.h>
  4. #include <sys/mman.h>

  5. int main()
  6. {
  7.     char *p = NULL;
  8.     int size = 1024;
  9.     pid_t pid;

  10.     // 父进程建立映射,指针p保存返回的虚拟地址
  11.     p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
  12.     if (p == (void *)-1) {
  13.         printf("mmap() failed\n");
  14.         goto err;
  15.     }

  16.     memcpy(p, "shared\0", 6);

  17.     // 创建子进程很多资源都是从父进程复制的,比如mm_struct结构以及映射关系,所以用指针p访问到和父进程同样的实际内存
  18.     if ((pid = fork()) < 0) {
  19.         printf("fork() failed\n");
  20.         goto err;
  21.     }

  22.     if (pid == 0) {
  23.         while (1) {
  24.             printf("child: %s(%p)\n", p, p);
  25.             sleep(1);
  26.         }
  27.     } else {
  28.         while (1) {
  29.             printf("parent: %s(%p)\n", p, p);
  30.             sleep(1);
  31.         }
  32.     }

  33.     munmap(p, size);
  34.     return 0;
  35. err:
  36.     if (p != NULL)
  37.         munmap(p, size);
  38.     return -1;
  39. }
复制代码

  • 内核中的mmap()实现
    结合《Linux内核源代码情景分析》的说明,看代码。


    mmap()是《Linux内核源代码情景分析》这本书对内存管理的最后一节说明,学完之后对Linux内存管理的奥秘就应该有比较深的理解了。
    但整个内存管理一章,还涉及到中断、进程调度、文件系统以及系统引导启动过程中的一些道理,所以如果初学,到目前为止,肯定还不能完全理解,需要后面积累到新知识后再回来理解。

论坛徽章:
0
2 [报告]
发表于 2016-10-13 14:58 |只看该作者
看到前两行就晕掉了,不知道谁会这么认为mmap................太有趣了

论坛徽章:
13
15-16赛季CBA联赛之八一
日期:2016-07-08 21:00:1415-16赛季CBA联赛之同曦
日期:2017-02-15 14:26:1515-16赛季CBA联赛之佛山
日期:2017-02-20 14:19:2615-16赛季CBA联赛之青岛
日期:2017-05-07 16:49:1115-16赛季CBA联赛之广夏
日期:2017-07-30 09:13:1215-16赛季CBA联赛之广东
日期:2018-07-05 22:34:3615-16赛季CBA联赛之江苏
日期:2018-09-03 12:10:2115-16赛季CBA联赛之上海
日期:2018-09-25 03:49:2215-16赛季CBA联赛之广东
日期:2018-09-25 04:09:12
3 [报告]
发表于 2016-10-13 15:38 |只看该作者
回复 2# hnwyllmm


大神,请想好再嘲讽,场面被你搞的好尴尬

论坛徽章:
0
4 [报告]
发表于 2016-10-16 11:36 |只看该作者
内存映射,我的简单理解就是“文件内存映射”,就是把文件映射到进程地址空间的一个线性区,文件内容在内核物理内存中只有一份。映射可以是共享的,也可以是私有的;物理内存中的数据可以是和磁盘上的文件对应,也可以是特殊文件系统中的文件对应(如shm,共享内存使用),楼主描述的没有发现问题

论坛徽章:
0
5 [报告]
发表于 2016-10-18 18:51 |只看该作者
回复 4# zzuedu2000

之所以这么说是因为我们用mmap都是做共享内存,还有tcmalloc中使用mmap代替sbrk申请内存。我前面的意思就是说楼主的引子,太武断了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP