内存映射IO
文章目录
本文总结内存映射相关接口的用法和注意事项。
1.1 内存映射IO的实现方法
内存映射IO(memory-mapped I/O)将一个磁盘中的文件映射到内存的一个缓冲区中,从而可以通过操作内存中的缓冲区达到操作文件的目的。例如读取映射在内存中的缓冲区,就等同于读取对应的磁盘文件。
1.1.1 内存映射IO的优缺点
优点:
- 读写文件直接编程操作缓冲区,更加简单;
- 对于两个文件之间的读写操作而言,直接进行文件读写操作的话,read需要先将内核缓冲区中的内容读到应用缓冲区(read_buffer),然后write再将应用缓冲区(write_buffer)的数据拷贝到内核缓冲区。若使用内存映射IO,则直接将数据从内核的一个缓冲区拷贝到另外一个缓冲区即可,开销很小(开销来自于可能发生缺页中断)。
1.2 内存映射IO相关的接口
1.2.1 内存映射IO相关的函数声明
|
|
1.2.2 mmap函数
mmap函数参数说明:
- addr参数用于指定映射内存区的起始地址。通常设置为NULL,表示由系统选择该映射区的首地址。
- length参数用于指定要映射的内存区域的长度。length必须是虚拟内存页面的整数倍。
- prot用于指定内存映射区域的保护方式:
- PROT_READ:映射区可读
- PROT_WRITE:映射区可写
- PROT_EXEC:映射区可执行
- PROT_NONE:映射区不可访问
- prot可以设置为PROT_NONE,也可以设置为PROT_READ,PROT_WRITE, PROT_EXEC一个或者多个组合按位或。
- 对指定内存映射区的保护不能超过被映射文件打开时的访问权限。例如文件是只读打开的,那么对映射内存区就不能指定PROT_WRITE属性。
- 当prot设置为PROT_NONE时:这块映射的缓冲区不可读写,读写时会触发SIGSEGV。 用于实现对特定区域的保护,例如可以检测内存越界。典型应用:pthread_attr_setguardsize/pthread_attr_getguardsize.
- prot的属性PROT_EXEC的作用是:将内存映射区域的内容当作CPU可以执行的的机器指令进行执行。
-
flag参数:用于设置映射区域的属性。
- MAP_SHARED:该属性表示共享当前映射的内存区。映射区域的更新对其它映射了同一文件区域的进程可见。更新映射区域就会更新对应的映射文件。
- MAP_PRIVATE: 该属性表示创建一个私有的映射副本。映射区域的更新对其它映射了同一文件区域的进程不可见。更新映射区域也不会更新对应的映射文件。任何修改都只影响映射文件的副本,而不影响源文件。
- 必须指定MAP_SHARED或者MAP_PRIVATE其中的一个,但是不能同时指定。
-
fd参数:指定要被映射的文件描述符。注意,在进行文件映射之前,文件必须先打开。但是关闭文件描述符并不会解除映射区。
-
offset参数:offset为被映射的文件中偏移,offset必须是虚拟内存页面的整数倍。虚拟内存页面的大小通过
sysconf(_SC_PAGE_SIZE)获取。- 若offset或者length不是内存页面大小的整数倍,操作系统如何处理?例如文件长度96字节,系统页面大小4096字节,则系统会提供4096字节的内存映射区,其中4000字节会被设置为0。虽然可以在映射区中修改4000字节区域,但是修改不会影响到被映射的文件。
1.2.3 mprotect()函数
函数参数说明:
- addr必须是内存页面的整数倍。
- mprotect()函数用于修改调用它的进程的访问指定页面中数据的访问权限。如果调用mprotect()函数的进程尝试违反设置的保护方式访问指定的内存区域,则操作系统会给进程返回一个SIGSEGV。利用这特性,我们可以用来定位踩内存问题。
- 可以通过mprotect()函数来修改内存映射区的保护属性。prot参数和mmap中的参数相同。
1.2.4 msync()函数
函数参数和功能说明:
- addr必须是内存页面的整数倍。
- flags参数说明:
- MS_ASYNC: 函数执行更新操作后立刻返回。
- MS_SYNC: 函数需要等待写操作完成才返回。函数必须指定M_ASYNC或M_SYNC中的一个。
- MS_INVALIDATE:
msync()函数的用途说明:
- 如果是用MAP_SHARED标识进行的内存映射,当映射区更新后,修改并不会立刻写会到被映射文件中。而何时将修改的脏页写会磁盘取决于操作系统内核的策略。
- 脏页写回的策略是:只要某个页面有一个字节被修改,整个页面都会被写回。
- 如果内存映射区被修改,可以调用msync()函数将脏页下刷到被映射的文件中。
1.2.5 munmap()函数
函数功能说明: munmap()用于解除内存和文件的映射关系。
相关特性说明:
- 当进程退出时会自动解除内存映射。
- 关闭内存映射时的文件描述符并不能解除和文件的内存映射关系。
- 调用munmap()函数并不会将内存映射区的内容更新到被映射的文件呢中。
- 当内存映射区解除映射后,映射时设置为MAP_PRIVATE的内存区的修改会被丢弃。
1.2.6 映射区的一些特性
-
与映射区相关的信号
SIGSEGV:信号SIGSEGV通常在进程访问到它不可用的内存区时产生。例如映射区被设置为只读时,进程若尝试修改这个映射区是就会触发SIGSEGV。或者试图访问设置了PROT_NONE的映射区域。
SIGBUS:如果映射区的某部分在访问时不存在,会触发SIGBUS信号。
-
子进程可以通过fork基础父进程的内存映射区。但是不能通过exec继承映射区。
1.3 内存映射IO的使用示例
|
|