Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
101 lines (80 sloc) 8.02 KB

avatar 作者:陈家黎

内存映射mmap详解

  • 操作系统读写文件流程
  • mmap内存映射
  • mmap的优点

什么是内存映射?

所谓内存映射,就是将文件的磁盘扇区映射到进程的虚拟内存空间的过程。

操作系统中的进程

  • 进程就是一个正在运行的应用程序
  • 每一个进程都是独立的,并且每一个进程都在一个独立的、受保护的空间内
  • 在Linux系统中,通常使用fork()方法来开启一个新的进程
  • 在iOS系统中,每一个进程都有自己的内存和磁盘空间,其他的进程是不被允许访问的

一、操作系统读写文件流程

读写操作的流程

1.进程发起一个读文件请求;

2.内核通过查找进程文件符表,定位到内核已打开的文件集上的文件信息,从而找到对应文件的inode;

3.inode在地址空间(address_space)上查找要请求的文件是否已经缓存在内核页的高速缓存中,如果存在,则直接放回该文件的内容;

4.如果文件不存在高速缓存中,则通过inode定位到文件的磁盘地址,将数据从磁盘复制到内核页高速缓存。之后再次范圣琦读页面的过程,将内核高速缓存中的数据发送给用户进程

什么是inode?

全称为index node,既存储文件元信息的区域,中文译名“索引节点”。
例如包含:文件权限、文件拥有者的UID、文件的大小等等。

操作系统读写的特点

1.系统在read/write的时候是很耗时的,例如在读文件的时候,将文件内容从硬盘拷贝到内核空间的一个缓冲区,然后再将这些数据拷贝到用户空间,实际上完成了两次数据拷贝
2.同理,写入操作同样耗时,待写入的buffer在内核空间不能直接访问,必须要先拷贝至内核空间对应的主存,再写回磁盘中(延迟写回),也是需要两次数据拷贝
3.如果两个进程都对磁盘中的一个文件内容进行访问,那么这个内容在物理内存中有三份:进程A的地址空间 + 进程B的地址空间 + 内核页高速缓冲空间;

此时我们找到了文件读取的痛点:两次拷贝导致效率过低

二、mmap内存映射

映射

“映射”这个词,就和数学课上说的“一一映射”是一个意思,就是建立一种一一对应关系,在这里主要是指硬盘上文件 的位置与进程逻辑地址空间 中一块大小相同的区域之间的一一对应

注意:这种对应关系纯属是逻辑上的概念,物理上是不存在的,原因是进程的逻辑地址空间本身就是不存在的。

具体到代码,就是建立并初始化了相关的数据结构(struct address_space),这个过程有系统调用mmap()实现,所以建立内存映射的效率很高。

内存映射过程

1.通过ptr指针获取逻辑地址:
mmap()会返回一个指针ptr,它指向进程逻辑地址空间中的一个地址,这样以后,进程无需再调用read或write对文件进行读写,而只需要通过ptr就能够操作文件;
2.将逻辑地址转换成物理地:
但是ptr所指向的是一个逻辑地址,要操作其中的数据,必须通过MMU将逻辑地址转换成物理地址;
3.产生缺页中断:
建立内存映射并没有实际拷贝数据,这时,MMU在地址映射表中是无法找到与ptr相对应的物理地址的,也就是MMU失败,将产生一个缺页中断,缺页中断的中断响应函数会在swap中寻找相对应的页面,如果找不到(也就是该文件从来没有被读入内存的情况),则会通过mmap()建立的映射关系,从硬盘上将文件读取到物理内存中;
4.物理页面交换到硬盘上:
如果在拷贝数据时,发现物理内存不够用,则会通过虚拟内存机制(swap)将暂时不用的物理页面交换到硬盘上;

MMU是Memory Management Unit的缩写,中文名是内存管理单元,它是中央处理器(CPU)中用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权,多用户多进程操作系统。

mmap内存映射的实现过程,总的来说可以分为三个阶段:
1.进程启动映射过程,并在虚拟地址空间中为映射创建虚拟映射区域;
2.调用内核空间的系统调用函数mmap(不同于用户空间函数),实现文件物理地址和进程虚拟地址的一一映射关系;
3.进程发起对这片映射空间的访问,引发缺页异常,实现文件内容到物理内存(主存)的拷贝;

映射过程核心

前两个阶段仅在于创建虚拟区间并完成地址映射,但是并没有将任何文件数据的拷贝至主存。真正的文件读取是当进程发起读或写操作时

  • 进程的读或写操作访问虚拟地址空间这一段映射地址,通过查询页表,发现这一段地址并不在物理页面上。因为目前只建立了地址映射,真正的硬盘数据还没有拷贝到内存中,因此引发缺页异常
  • 缺页异常进行一系列判断,确定无非法操作后,内核发起请求调页过程
  • 调页过程先在交换缓存空间(swap cache)中寻找需要访问的内存页,如果没有则调用nopage函数把所缺的页从磁盘装入到主存中。
  • 之后进程即可直接对这片主存进行读或者写的操作。

效率

常规文件操作

之前说过,常规文件操作为了提高读写效率和保护磁盘,使用了页缓存机制,由于页缓存处在内核空间,不能被用户进程直接寻址,这样就出现了两次拷贝的过程,这也是常规文件操作的性能限制。

内存映射

使用mmap操作文件中,创建新的虚拟内存区域和建立文件磁盘地址和虚拟内存区域映射这两步,没有任何文件拷贝操作
之后访问数据时发现内存中并无数据而发起的缺页异常过程,可以通过已经建立好的映射关系,只使用一次数据拷贝,就从磁盘中将数据传入内存的用户空间中,供进程使用。

结论

  • 常规文件操作需要从磁盘到页缓存再到用户主存的两次数据拷贝。
  • 而mmap操控文件,只需要从磁盘到用户主存的一次数据拷贝过程。mmap的关键点是实现了用户空间和内核空间的数据直接交互而省去了空间不同数据不通的繁琐过程。因此mmap效率更高。

mmap的例子

对硬盘上一个名为“mmap_test”的文件进行操作,文件中存有10000个整数,程序两次使用不同的方法将它们读出,加1,再写回硬盘。

gettimeofday( &tv1, NULL );
fd = open( "mmap_test", O_RDWR );
array = mmap( NULL, sizeof(int)*MAX, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 );
for( i=0; i<MAX; ++i )
 
++array[ i ];
munmap( array, sizeof(int)*MAX );
msync( array, sizeof(int)*MAX, MS_SYNC );
free( array );
close( fd );
gettimeofday( &tv2, NULL );

三、mmap的优点

  • 对文件的读取操作跨过了页缓存,减少了数据的拷贝次数,用内存读写取代I/O读写,提高了文件读取效率。
  • 实现了用户空间和内核空间的高效交互方式。两空间的各自修改操作可以直接反映在映射的区域内,从而被对方空间及时捕捉。
  • 可用于实现高效的大规模数据传输。内存空间不足,是制约大数据操作的一个方面,解决方案往往是借助硬盘空间协助操作,补充内存的不足。但是进一步会造成大量的文件I/O操作,极大影响效率。这个问题可以通过mmap映射很好的解决。换句话说,但凡是需要用磁盘空间代替内存的时候,mmap都可以发挥其功效。
You can’t perform that action at this time.