mmap概念
mmap是一种内存映射文件的方法,用来将一个文件或其它对象映射到进程的地址空间,实现磁盘地址和进程虚拟地址空间的一一对应关系。使用mmap进行地址映射之后,进程就可以通过读写这一段内存(进程虚拟地址空间)来达到读写对应的磁盘地址(系统会自动的进行脏页写会操作)。使用mmap之后可以不必再去使用read、write等文件读写的系统调用。同样的也可以通过这种方式来实现不同进程之间的文件共享。
从上图可以看出与Linux地址空间中的其它段一样,内存映射也占用了一个独立的虚拟内存区域,处于栈和堆之间。linux内核使用vm_area_struct结构来表示一个独立的虚拟内存区域,由于每个不同的虚拟内存区域功能和内部机制都不同,因此一个进程使用多个vm_area_struct结构来分别表示不同类型的虚拟内存区域。各个vm_area_struct结构使用链表或者树形结构链接,方便进程快速访问,如下图所示:
mmap内存映射原理
mmap内存映射的实现过程,总的来说可以分为三个阶段:
-
进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域
进程通过调用mmap函数来实现虚拟区域的分配 -
调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟地址的一一映射关系
-
进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝
mmap和常规文件读取的比较
首先看一下常规文件的读取流程:
- 进程发起读文件请求
- 内核查找进程文件符表,定位到内核已经打开文件集上的文件信息,找到文件的inode
- inode在address_space上查找要请求的文件页是否已经缓存在页缓冲中,存在的话直接返回即可
- 不存在的话,通过inode定位文件磁盘地址,将数据从磁盘复制到页缓存,之后再将页缓存中的数据发送给进程
常规的文件读取操作为了提高效率,使用了页缓存机制,这种方式就造成了文件的读取要先从磁盘拷贝到页缓冲(内核空间),然后再拷贝到用户空间中,这样就会有两次的拷贝操作,写操作也是类似。
而使用mmap操作,创建新的虚拟内存区域和建立文件磁盘地址和虚拟内存区域映射这两步,没有任何文件拷贝操作。而之后访问数据时发现内存中并无数据而发起的缺页异常过程,可以通过已经建立好的映射关系,只使用一次数据拷贝,就从磁盘中将数据传入内存的用户空间中,供进程使用。
总结下来就是,常规文件操作需要从磁盘到页缓存再到用户主存的两次数据拷贝。而mmap操控文件,只需要从磁盘到用户主存的一次数据拷贝过程。