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

什么时候使用其中一种比另一种更好?他们有哪些独特的优势值得包括两个接口?

有帮助吗?

解决方案

如果你有多个进程从同一个文件以只读方式访问数据,那么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这样的东西。

其他提示

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

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

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

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

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

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

除了其他很好的答案之外,还引用了 Linux系统编程 由 Google 专家 Robert Love 撰写:

优点 mmap( )

通过操作文件 mmap( ) 比标准具有少数优势 read( )write( ) 系统调用。其中包括:

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

  • 除了任何潜在的页面故障之外,从读取并写入内存映射的文件都不会引起任何系统调用或上下文开关开销。就像访问内存一样简单。

  • 当多个进程将同一个对象映射到内存时,数据在所有进程之间共享。只读和共享的可写映射将全部共享;私人写作映射具有他们的尚未共享的尚未共享的(抄写)页面。

  • 查找映射涉及到简单的指针操作。没有必要 lseek( ) 系统调用。

由于这些原因, mmap( ) 对于许多应用来说是一个明智的选择。

缺点 mmap( )

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

  • 内存映射的大小始终是整数页。因此,衬板文件的大小和整数页面之间的差异被“浪费”为松弛空间。对于小文件,可能会浪费很大比例的映射。例如,使用4 kb页,一个7个字节映射废物4,089字节。

  • 内存映射必须适合进程的地址空间。有了32位地址空间,大量各种大小的映射可能会导致地址空间碎片化,从而难以找到大型的自由连续区域。当然,对于64位地址空间,这个问题的看法要少得多。

  • 在内核内部创建和维护内存映射和关联数据结构会产生开销。消除了上一节中提到的双复制,特别是对于较大且经常访问的文件,通常可以消除该开销。

由于这些原因,好处 mmap( ) 当映射文件很大时(因此,浪费的空间是总映射的一小部分),或者当映射的文件的总大小均匀地被页面大小排除(因此没有浪费的空间, )。

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top