Linux中的mmap映射(读写文件数据的另一种方式)

Linux 中的mmap映射(读写文件数据的另一种方式)

mmap功能

  • Linux除了通过对read,write函数的调用实现数据的读写,还提供了一种方式,对文件数据进行读写,即利用mmap函数。
    例如:用户想要从磁盘上读取8192个字节的数据,首先把这8192个字节的数据拷贝到内存,作为page cache(方便以后快速读写操作),用户进程可以通过指针操作直接读写page cache,不再需要系统调用和内存拷贝。
  • 一个文件可能被多个进程通过mmap映射后访问并修改,根据所做的修改是否对其他进程可见,mmap可分为共享映射和私有映射两种。
  • 共享映射:修改对所有进程可见,也就是说,如果进程A修改了其中某个page上的数据,进程B之后读取这个page得到的就是修改后的内容。有共享就有竞态(race condition),mmap本身并没有提供互斥机制,需要调用者在使用的过程中自己加锁。
  • 私有映射:进程A的修改对进程B是不可见的,都是同一份数据,这是如何做到的呢?这里利用的是 Copy On Write(COW)机制。当进程A试图修改某个page上的数据时,内核会将这个page的内容拷贝一份,之后A的写操作实际是在这个拷贝的page上进行的(进程A中对应这个page的页表项也需要被修改,以指向新拷贝的page),这样进程B看到的这个page还是原来未经改动的。这种修改只会存在于内存中,不会同步到外部的磁盘文件上(事实上也没法同步,因为不同进程所做的修改是不同的)。私有文件映射最典型的应用就是进程对动态链接库(比如libc.so)的使用。

mmap函数

函数原型:

#include 
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
  • 其中fd, offset和length都是用来描述要映射的文件区域的,fd是文件描述符,对于匿名映射,fd应该是-1(如果是通过打开/dev/zero这个特殊的文件来创建匿名映射,则它也是有不为-1的正常fd值的)。offset是文件中映射的起始位置,length是映射的长度。如果访问了超出映射的区域,则有可能触发SIGSEGV异常(segmentation fault)。(对于这个异常如何触发在参考链接中给了详细的介绍)。
  • prot是protection的意思,表示的是对内存映射区域的保护,包括PROT_READ(可读),PROT_WRITE(可写)和PROT_EXEC(可执行)。还有一个很特殊的PROT_NONE,就是既不可读也不可写更不可执行,啥操作都不可以,那映射出来干吗?
    注:prot属性是可以通过mprotect()动态修改的(mprotect并不局限于操作由mmap映射的内存区域,它可以操作任意区域的内存)
  • flags用于指定映射是基于文件的还是匿名(MAP_ANONYMOUS)的,是共享的(MAP_SHARED)还是私有的(MAP_PRIVATE)。
  • addr用于指定映射到的VMA的起始地址,这个地址也必须按page size对齐。映射是由内核完成的,但进程可以通过addr参数建议一个它认为的最佳地址(没有这种要求就设置addr为NULL),毕竟进程最了解它自身的应用场景嘛。

参考

以上内容都是从这两个博客中摘的,里边写的很详细,好理解,想要更进一步了解的可看:

  • Linux中的mmap映射(一)
  • Linux中的mmap映射(二)
  • Linux内存管理 (9)mmap(补充) 这篇写的也很好,可以加深理解。

你可能感兴趣的:(Linux)