我什么时候应该使用mmap进行文件访问?

时间:2008-11-03 07:56:03

标签: c file-io posix mmap

POSIX环境提供至少两种访问文件的方式。标准系统调用open()read()write()和朋友,但也可以选择使用mmap()将文件映射到虚拟内存。

何时优先使用一个而不是另一个?他们的个人优势是什么,包括两个接口?

6 个答案:

答案 0 :(得分:280)

如果你有多个进程从同一个文件以只读方式访问数据,那么mmap很棒,这在我编写的服务器系统类型中很常见。 mmap允许所有这些进程共享相同的物理内存页面,从而节省了大量内存。

mmap还允许操作系统优化分页操作。例如,考虑两个程序;程序A将1MB文件读入使用malloc创建的缓冲区,程序B将1MB文件格式化为内存。如果操作系统必须将A的一部分内存交换掉,它必须将缓冲区的内容写入交换,然后才能重用内存。在B的情况下,任何未修改的mmap页面都可以立即重用,因为操作系统知道如何从他们mmap的现有文件中恢复它们。 (操作系统可以通过最初将可写mmap的页面标记为只读并捕获seg错误来检测哪些页面未修改,类似于写入时复制策略)。

mmap对于进程间通信也很有用。您可以在需要通信的进程中将文件映射为读/写,然后在mmap'd区域中使用同步原语(这是MAP_HASSEMAPHORE标志的用途)。

如果您需要在32位计算机上使用非常大的文件,那么mmap可能很麻烦。这是因为mmap必须在进程的地址空间中找到一个连续的地址块,该地址空间足够大,以适应所映射文件的整个范围。如果您的地址空间变得碎片化,这可能会成为一个问题,您可能有2 GB的地址空间可用,但没有单独的范围可以适合1 GB的文件映射。在这种情况下,您可能必须将文件映射到比您想要的更小的块中。

mmap作为读/写替代品的另一个潜在尴尬是你必须在页面大小的偏移量上开始映射。如果您只想在偏移X处获得一些数据,则需要修复该偏移量,以便与mmap兼容。

最后,读/写是 处理某些类型文件的唯一方法。 mmap不能用于像pipe和ttys这样的东西。

答案 1 :(得分:61)

我发现mmap()没有优势的一个领域是读取小文件(16K以下)。与仅执行单个read()系统调用相比,读取整个文件的页面错误开销非常高。这是因为内核有时可以在您的时间片中完全满足读取,这意味着您的代码不会被切换掉。由于页面错误,似乎更有可能安排另一个程序,使文件操作具有更高的延迟。

答案 2 :(得分:43)

当您对大文件进行随机访问时,

mmap具有优势。另一个优点是您可以通过内存操作(memcpy,指针算术)访问它,而无需担心缓冲。当结构大于缓冲区时,使用缓冲区时,正常I / O有时会非常困难。处理它的代码通常很难正确,mmap通常更容易。这就是说,使用mmap时存在某些陷阱。 正如人们已经提到的那样,mmap设置起来非常昂贵,因此仅适用于给定大小(从机器到机器)不同。

对于对文件的纯顺序访问,它并不总是更好的解决方案,尽管对madvise的适当调用可以缓解这个问题。

您必须小心架构的对齐限制(SPARC,itanium),使用读/写IO缓冲区通常是正确对齐的,并且在取消引用转换指针时不会陷阱。

您还必须小心不要在地图外访问。如果在地图上使用字符串函数,并且文件末尾不包含\ 0,则很容易发生。大多数情况下,当文件大小不是页面大小的倍数时,它将起作用,因为最后一页填充为0(映射区域总是大小为页面大小的倍数)。

答案 3 :(得分:17)

除了其他不错的答案,谷歌专家罗伯特·洛夫撰写的Linux system programming引用:

  

mmap( )

的优点      

通过mmap( )操纵文件有一些优势   标准read( )write( )系统调用。其中包括:

     
      
  • 读取和写入内存映射文件可以避免使用   使用read( )write( )系统时发生的无关副本   调用,必须在用户空间缓冲区中复制数据。

  •   
  • 除了任何潜在的页面错误之外,读取和写入内存映射文件不会导致任何系统调用或上下文切换   高架。它就像访问内存一样简单。

  •   
  • 当多个进程将同一对象映射到内存时,数据在所有进程之间共享。只读和共享可写   映射是完整的;私有可写映射   他们尚未COW(写时复制)页面共享。

  •   
  • 寻找映射涉及琐碎的指针操作。无需lseek( )系统调用。

  •   
     

由于这些原因,mmap( )是许多应用程序的明智选择。

     

mmap( )

的缺点      

使用mmap( )时需要注意几点:

     
      
  • 内存映射的大小始终为整数页。因此,支持文件的大小与之间的差异   整数页是"浪费"作为松弛的空间。对于小文件,a   可能浪费了很大比例的映射。例如,用   4 KB页面,7字节映射浪费4,089字节。

  •   
  • 内存映射必须符合流程'地址空间。具有32位地址空间,大量不同大小的映射   可能导致地址空间碎片化,使其变得困难   找到大的免费连续区域。当然,这个问题很多   64位地址空间不太明显。

  •   
  • 在内核中创建和维护内存映射和相关数据结构会产生开销。这个开销是   通常通过消除中提到的双重副本来避免   上一节,特别是对于较大且经常访问的部分   文件。

  •   
     

由于这些原因,mmap( )的好处最为明显   当映射文件很大时(因此任何浪费的空间都很小)   总映射的百分比),或映射的总大小   文件可以被页面大小整除(因此没有浪费   空间)。

答案 4 :(得分:10)

与传统IO相比,内存映射具有巨大的速度优势。它允许操作系统在触摸内存映射文件中的页面时从源文件中读取数据。这可以通过创建错误页面来实现,操作系统会检测这些页面,然后操作系统会自动从文件中加载相应的数据。

这与分页机制的工作方式相同,通常通过读取系统页面边界和大小(通常为4K)上的数据来优化高速I / O,这是大多数文件系统缓存优化的大小。

答案 5 :(得分:1)

mmap()能够将只读映射保持为 clean 页面的能力尚未列出。如果一个人在进程的地址空间中分配了一个缓冲区,然后使用read()从文件中填充缓冲区,则与该缓冲区相对应的内存页由于已被写入,因此现在为 dirty

内核无法从RAM中删除脏页。如果有交换空间,则可以将它们调出页面进行交换。但这是昂贵的,并且在某些系统(例如仅具有闪存的小型嵌入式设备)上根本没有交换。在这种情况下,缓冲区将一直停留在RAM中,直到进程退出,或者可能以madvise()的形式将其返回。

未写入mmap()页的内容是干净的。如果内核需要RAM,则可以简单地将其删除并使用页面所在的RAM。如果具有映射的进程再次访问它,则将导致页面错误,内核会从它们最初来自的文件中重新加载页面。 。首先采用相同的方式填充它们。

使用映射文件不需要多个过程,这是一个优势。