mmap/munmap接口是用户空间的最常用的一个系统调用接口,无论是在用户程序中分配内存、读写大文件,链接动态库文件,还是多进程间共享内存,都可以看到mmap/munmap的身影。

mmap说明

mmap系统调用并不是完全为了用于共享内存而设计的。它本身提供了不同于一般对普通文件的访问方式,进程可以像读写内存一样对普通文件的操作。而Posix或系统V的共享内存IPC则纯粹用于共享目的,当然mmap()实现共享内存也是其主要应用之一。

mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。

mmap/munmap函数声明如下:

#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
int munmap(void *addr, size_t length);

addr

addr用于指定映射到进程空间的起始地址,为了应用程序的可移植性,一般设置为NULL,让内核来选择一个合适的地址。

mmap函数就是为了寻找一个可用的地址空间,将文件描述符fd对应的文件从偏移量offset开始,copy文件的length个字符进入用户地址空间。将起始地址返回。为什么第一个参数还要传用户地址空间的指针呢?这不是骑驴找驴吗?

这个addr参数可以NULL,这种情况我们比较容易理解。当addr不为NULL的时候,这其实是个建议查找地址,用来指导内存区定位的线索,是个用户指定的经验值。

  • 如果flags带上了MAP_FIXED标志,表示用户王八吃秤砣,铁了心要addr这个地址作为映射起始地址,直接把addr返回。
  • 如果没带上MAP_FIXED标志,则首先在建议地址addr附近寻找合适的区域。
  • addr为NULL,让内核自己选择。

length、fd、offset

将fd对应的文件,从offset位置开始,长为len的内容映射到内存地址空间。

prot

prot参数通常表示映射页面的的读写权限,可以有如下参数组合:

  • PROT_EXEC:表示映射的页面是可以执行的。
  • PROT_READ:表示映射的页面是可以读取的。
  • PROT_WRITE:表示映射的页面是可以写入的。
  • PROT_NONE:表示映射的页面是不可访问的。

flags

flags参数也是一个重要的参数,有如下常见的参数:

  • MAP_SHARED:创建一个共享映射的区域。多个进程可以通过共享映射方式来映射一个文件,这样其他进程也可以看到映射内容的改变,修改后的内容会同步到磁盘文件中。
  • MAP_PRIVATE:创建一个私有的写时复制的映射。多个进程可以通过私有映射的方式来映射一个文件,这样其他进程不会看到映射内容的改变,修改后的内容也不会同步到磁盘文件中。
  • MAP_ANONYMOUS:创建一个匿名映射,即没有关联到文件的映射。
  • MAP_FIXED:使用参数addr创建映射,如果内核无法映射指定地址addr,那么mmap会返回失败,参数addr要求按页对齐。如果addr和length指定的进程地址空间和已有的VMA区域重叠,那么内核会调用do_munmap()函数把这段重叠区域销毁,然后重新映射新的内容。
  • MAP_POPULATE:对于文件映射来说,会提前预读文件内容到映射区域,该特性只支持私用映射。

mmap映射类型

参数fd可以看出mmap映射是否和文件相关联,因此Linux内核中映射可以分为匿名映射和文件映射。

  • 匿名映射:没有映射对应的相关文件,这种映射的内存区域的内容会被初始化为0。
  • 文件映射:映射和实际文件相关联,通常是把文件的内容映射到进程地址空间,这样应用程序就可以像操作进程地址空间一样读写文件。

最后根据文件关联性和映射区域是否共享等属性,又可以分为如下4种:

私有映射 共享映射
匿名映射 私有匿名映射-通常用于内存分配 共享匿名映射-通常用于进程间共享内存
文件映射 私有文件映射-通常用于加载动态库 共享文件映射-通常用于内存映射IO,进程间通信

私有匿名映射

当使用参数fd=-1且flags=MAP_ANONYMOUS | MAP_PRIVATE时,创建的mmap映射是私有匿名映射。私有匿名映射最常见的用途是在glibc分配大块内存中,当需要的分配的内存大于MMAP_THREASHOLD(128KB)时,glibc会默认使用mmap代替brk来分配内存。

共享匿名映射

当使用参数fd=-1且flags=MAP_ANONYMOUS | MAP_SHARED。在这种情况下,创建共享匿名映射。共享匿名映射让相关进程共享一块内存区域,通常用于父子进程的之间通信。

创建共享匿名映射有如下两种方式:

  1. fd=-1且flags= MAP_ANONYMOUS|MAP_SHARED。在这种情况下,do_mmap_pgoff()->mmap()函数最终调用shmem_zero_setup()来打开一个"/dev/zero"特殊的设备文件。
  2. 另外一个是直接打开"/dev/zero"设备文件,然后使用这个文件句柄来创建mmap。

私有文件映射

私有文件映射时flags的标志位被设置为MAP_PRIVATE,那么就会创建私有文件映射。

私有文件映射的最常用的场景是加载动态共享库。

共享文件映射

创建文件映射时flags的标志位被设置为MAP_SHARED,那么就会创建共享文件映射。如果prot参数指定了PROT_WRITE,那么打开文件需要制定O_RDWR标志位。共享文件映射通常有如下场景:

  • 读写文件:把文件内容映射到进程地址空间,同时对映射的内容做了修改,内核的回写机制(writeback)最终会把修改的内容同步到磁盘中。
  • 进程间通信:进程之间的进程地址空间相互隔离,一个进程不能访问到另外一个进程的地址空间。如果多个进程都同时映射到一个相同的文件,就实现了多进程间的共享内存的通信。如果一个进程对映射内容做了修改,那么另外的进程是可以看到的。

mmap的使用

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/syscall.h>int main() {int fd = open("/root/demo/aa", O_CREAT | O_RDWR, 777);size_t length = 1024;ftruncate(fd, length); // 一定要这句,否则会报Bus errorchar *addr = mmap(NULL, length, PROT_READ | PROT_WRITE,MAP_SHARED, fd, 0);char* data = "hello";memcpy(addr, data, 5);char result[5];memcpy(result, addr, 5);printf("%s\n", result);sleep(100);munmap(addr, length);close(fd);return 0;
}

对一个空的新文件进行mmap前,需要往里面写入一点内容,否则会报错Bus error。发生错误的原因是因为mmap不能去扩展一个内容为空的新文件,因为大小为0,所有本没有与之对应的合法的物理页,不能扩展。

解决方案有2个:

  • 一个就是上面的链接里的方案: 只需要在新创建的空文件中先写入一些数据即可;
  • 另外一个是通过ftruncate对新建立的文件进行扩展后再映射修改。

查询进程所有的mmap映射:

# cat /proc/1078/maps
5636aadb5000-5636aadb6000 r--p 00000000 08:10 41881                      /root/demo/a.out
5636aadb6000-5636aadb7000 r-xp 00001000 08:10 41881                      /root/demo/a.out
5636aadb7000-5636aadb8000 r--p 00002000 08:10 41881                      /root/demo/a.out
5636aadb8000-5636aadb9000 r--p 00002000 08:10 41881                      /root/demo/a.out
5636aadb9000-5636aadba000 rw-p 00003000 08:10 41881                      /root/demo/a.out
5636abae0000-5636abb01000 rw-p 00000000 00:00 0                          [heap]
7f9f4e83f000-7f9f4e861000 r--p 00000000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f9f4e861000-7f9f4e9d9000 r-xp 00022000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f9f4e9d9000-7f9f4ea27000 r--p 0019a000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f9f4ea27000-7f9f4ea2b000 r--p 001e7000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f9f4ea2b000-7f9f4ea2d000 rw-p 001eb000 08:10 12367                      /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f9f4ea2d000-7f9f4ea33000 rw-p 00000000 00:00 0
7f9f4ea3b000-7f9f4ea3c000 r--p 00000000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f9f4ea3c000-7f9f4ea5f000 r-xp 00001000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f9f4ea5f000-7f9f4ea67000 r--p 00024000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f9f4ea67000-7f9f4ea68000 rw-s 00000000 08:10 768                        /root/demo/aa
7f9f4ea68000-7f9f4ea69000 r--p 0002c000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f9f4ea69000-7f9f4ea6a000 rw-p 0002d000 08:10 12234                      /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f9f4ea6a000-7f9f4ea6b000 rw-p 00000000 00:00 0
7ffe68bf2000-7ffe68c13000 rw-p 00000000 00:00 0                          [stack]
7ffe68dd5000-7ffe68dd8000 r--p 00000000 00:00 0                          [vvar]
7ffe68dd8000-7ffe68dd9000 r-xp 00000000 00:00 0                          [vdso]

查看内存映射的区域地址范围:

# ll /proc/1078/map_files/
total 0
dr-x------ 2 root root  0 Jul 12 11:02 ./
dr-xr-xr-x 9 root root  0 Jul 12 11:02 ../
lr-------- 1 root root 64 Jul 12 11:02 5636aadb5000-5636aadb6000 -> /root/demo/a.out*
lr-------- 1 root root 64 Jul 12 11:02 5636aadb6000-5636aadb7000 -> /root/demo/a.out*
lr-------- 1 root root 64 Jul 12 11:02 5636aadb7000-5636aadb8000 -> /root/demo/a.out*
lr-------- 1 root root 64 Jul 12 11:02 5636aadb8000-5636aadb9000 -> /root/demo/a.out*
lr-------- 1 root root 64 Jul 12 11:02 5636aadb9000-5636aadba000 -> /root/demo/a.out*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4e83f000-7f9f4e861000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4e861000-7f9f4e9d9000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4e9d9000-7f9f4ea27000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea27000-7f9f4ea2b000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea2b000-7f9f4ea2d000 -> /usr/lib/x86_64-linux-gnu/libc-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea3b000-7f9f4ea3c000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea3c000-7f9f4ea5f000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea5f000-7f9f4ea67000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*
lrw------- 1 root root 64 Jul 12 11:02 7f9f4ea67000-7f9f4ea68000 -> /root/demo/aa*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea68000-7f9f4ea69000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*
lr-------- 1 root root 64 Jul 12 11:02 7f9f4ea69000-7f9f4ea6a000 -> /usr/lib/x86_64-linux-gnu/ld-2.31.so*

查看内存映射地址范围的内容:

# cat /proc/1078/map_files/7f9f4ea67000-7f9f4ea68000
hello

我们看到/root/demo/aa文件已经映射到内存中取了,占用空间0x7f9f4ea67000-0x7f9f4ea68000=4K,虽然申请的是1K,但是映射的分配是以页面为单位分配的,即最小分配4K。

【计算机网络】零拷贝之MMAP相关推荐

  1. Java 两种zero-copy零拷贝技术mmap和sendfile的介绍

    详细介绍了两种zero-copy零拷贝技术mmap和sendfile的概念和基本原理. 文章目录 1 标准IO 2 零拷贝 2.1 sendfile调用 2.1 mmap调用 2.2 MQ中的应用 1 ...

  2. 什么是零拷贝?mmap与sendFile的区别是什么?

    零拷贝这三个字,一直是服务器网络编程的关键字,任何性能优化都离不开.在 Java 程序员的世界,常用的零拷贝有 mmap 和 sendFile.那么,他们在 OS 里,到底是怎么样的一个的设计?本文将 ...

  3. 消息中间件零拷贝?mmap与sendFile的区别是什么?

    什么是零拷贝? 在这个方法里面,我们没有在内存层面去"复制(Copy)"数据,所以这个方法,也被称之为零拷贝(Zero-Copy).IBM Developer Works里面有一篇 ...

  4. 零拷贝、mmap、sendfile

    目录 零拷贝 mmap sendFile 总结 零拷贝 要了解零拷贝,首先得先了解一下传统 IO 的执行流程,这里举个例子,通过传统的 IO 进行网络传输来传输一个文件. 先上一张图,这张图就代表了传 ...

  5. Linux网络处理“零拷贝”技术mmap()内核进程间通信设计8086分页管理——摆在一起来谈谈...

    Jack:最近听说了网络处理的"零拷贝"技术,觉得非常神奇,在网上查阅了很多资料.不过,并不是太明白--知其然,而不知其所以然.你能通俗地解释一下吗? 我:这是一个相对比较复杂的话 ...

  6. 零拷贝、MMAP、堆外内存,傻傻搞不明白...

    要搞清这些概念前,需要先了解以下概念: 虚拟内存:将用户逻辑内存与物理内存分开. 内核态(Kernal Mode):非特权区域, 在该区域执行的代码就不能直接访问硬件设备.用户进程所在区域. 用户态( ...

  7. 零拷贝:mmap和sendFile 的区别

    首先,一起来看看传统IO 的执行流程: 当我们用程序读取文件的时候,会从用户态切换为内核态,同时基于 DMA 引擎将磁盘文件拷贝到内核缓冲区. 那么问题又来了,什么是用户态和内核态,什么是 DMA 拷 ...

  8. 零拷贝之mmap和sendfile

    零拷贝:https://strikefreedom.top/linux-io-and-zero-copy 内存映射文件 从硬盘上将文件读入内存,都要经过文件系统进行数据拷贝,并且数据拷贝操作是由文件系 ...

  9. 解读Linux零拷贝之mmap

    文章目录 0x01 内核态mmap 0x02 用户态mmap 0x03 映射I/O内存关闭Cache 0x04 利用/dev/mem进行mmap 0x01 内核态mmap 内核态mmap函数如下,vm ...

  10. 【Netty】mmap 和 sendFile 零拷贝原理

    文章目录 一. 零拷贝 简介 二. 传统 BIO 数据拷贝分析 ( 4拷贝 4切换 ) 三. mmap 内存映射 ( 3拷贝 4切换 ) 四. sendFile 函数 ( Linux 2.1 优化 ) ...

最新文章

  1. Visual Studio 2022编译x86 或者 win32程序时的错误MSB6006
  2. About me 留言板
  3. 关于C++指针的理解
  4. 文本模式下的分辨率对照表
  5. 在Windows上使用Docker运行.NetCore
  6. Visual Studio 2008 C++添加 链接库
  7. 通过SharedPreferences方式存储复杂数据
  8. 《Python核心编程》第二版第308页第十一章练习 续一 -Python核心编程答案-自己做的-...
  9. 栈空间不够会报错吗_c++如何解决大数组栈内存不够的问题
  10. Java 多线程(二)启动线程的三种方式和源码分析
  11. 配置pytoch版人脸关键点网络pfld docker环境
  12. VMware ESXi下虚拟机的嗅探设置
  13. Savitzky-Golay 滤波器详解及matlab语言程序设计
  14. 8. OSPF的NSSA详解
  15. python太极代码_旋转的太极图动画源代码
  16. Golang源码探索----GC的实现原理(3)
  17. 中国传感器制造行业发展态势与应用前景预测报告2022-2028年
  18. matlab蒙特卡罗变量变换的分布,使用 copula 仿真相关随机变量
  19. 第1篇 初识IPP(Integrated Performance Primitives)
  20. AG9311功能应用和结构设计框图参考

热门文章

  1. 简约竞聘个人简历自我介绍PPT模板
  2. TUIO学习笔记1-TUIO 1.1 Protocol Specification协议规范/标准
  3. ocr文字识别html,在线OCR 随时随地轻松搞定文字识别
  4. 第二届广东省大学生网络攻防大赛 simple_re
  5. 【广东大学生网络攻防大赛-WriteUp(非官方)】Pwn | jmp_rsp
  6. 新世纪版五笔字根背诵
  7. PSCAD建立高压直流输电线路雷击模型--相关建模问题
  8. 各自然带代表植被_十种常见自然带所对应的植被
  9. C语言如何设置随机数
  10. 运用spss modeler运用支持向量机_使用支持向量回归进行Facebook股票预测