mmap概述
mmap/munmap接口是用户空间的最常用的一个系统调用接口,无论是在用户程序中分配内存、读写大文件,链接动态库文件,还是多进程间共享内存,都可以看到mmap/munmap的身影。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:用于指定映射到进程空间的起始地址,为了应用程序的可移植性,一般设置为NULL,让内核来选择一个合适的地址。
- length:表示映射到进程地址空间的大小
- prot:用于设置内核映射区域的读写属性等。
- flags:用于设置内存映射的属性,例如共享映射、私有映射等。
- fd:表示这个是一个文件映射,fd是打开文件的句柄。
- offset:在文件映射时,表示文件的偏移量。
prot参数通常表示映射页面的的读写权限,可以有如下参数组合:
- PROT_EXEC:表示映射的页面是可以执行的。
- PROT_READ:表示映射的页面是可以读取的。
- PROT_WRITE:表示映射的页面是可以写入的。
- PROT_NONE:表示映射的页面是不可访问的。
flags参数也是一个重要的参数,有如下常见的参数:
- MAP_SHARED:创建一个共享映射的区域。多个进程可以通过共享映射方式来映射一个文件,这样其他进程也可以看到映射内容的改变,修改后的内容会同步到磁盘文件中。
- MAP_PRIVATE:创建一个私有的写时复制的映射。多个进程可以通过私有映射的方式来映射一个文件,这样其他进程不会看到映射内容的改变,修改后的内容也不会同步到磁盘文件中。
- MAP_ANONYMOUS:创建一个匿名映射,即没有关联到文件的映射。
- MAP_FIXED:使用参数addr创建映射,如果内核无法映射指定地址addr,那么mmap会返回失败,参数addr要求按页对齐。如果addr和length指定的进程地址空间和已有的VMA区域重叠,那么内核会调用do_munmap()函数把这段重叠区域销毁,然后重新映射新的内容。
- MAP_POPULATE:对于文件映射来说,会提前预读文件内容到映射区域,该特性只支持私用映射。
参数fd可以看出mmap映射是否和文件相关联,因此Linux内核中映射可以分为匿名映射和文件映射。
- 匿名映射:没有映射对应的相关文件,这种映射的内存区域的内容会被初始化为0。
- 文件映射:映射和实际文件相关联,通常是把文件的内容映射到进程地址空间,这样应用程序就可以像操作进程地址空间一样读写文件。
最后根据文件关联性和映射区域是否共享等属性,又可以分为如下4种,见表2.1。
mmap映射类型
映射类型 |
映射类型 |
|
---|---|---|
私有映射 |
共享映射 |
|
匿名映射 |
私有匿名映射-通常用于内存分配 |
共享匿名映射-通常用于进程间共享内存 |
文件映射 |
私有文件映射-通常用于加载动态库 |
共享文件映射-通常用于内存映射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标志位。共享文件映射通常有如下场景:
(1)读写文件:
把文件内容映射到进程地址空间,同时对映射的内容做了修改,内核的回写机制(writeback)最终会把修改的内容同步到磁盘中。
(2)进程间通信:
进程之间的进程地址空间相互隔离,一个进程不能访问到另外一个进程的地址空间。如果多个进程都同时映射到一个相同的文件,就实现了多进程间的共享内存的通信。如果一个进程对映射内容做了修改,那么另外的进程是可以看到的。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- vsftpd配置虚拟用户登录的方法
- Linux 删除特殊字符文件名或目录的方法
- 详解Linux环境变量配置全攻略
- Linux lsof命令使用详解
- Ubuntu删除多余内核的方法
- 详解Linux下crontab的使用与注意事项
- Linux内核设备驱动之Linux内核基础笔记整理
- Ubuntu18.04 Server版安装及使用(图文)
- Ubuntu18.04安装vsftpd的实现代码
- ubuntu系统theano和keras的安装方法
- Linux安装Jenkins步骤及各种问题解决(页面访问初始化密码)
- 解决Ubuntu19 安装Theano问题
- centos7 esxi6.7模板实际应用详解
- Centos8搭建本地Web服务器的实现步骤
- 总结Linux 6种日志查看方法