10

据说mmap()将文件映射到内存,它消耗调用进程的虚拟地址空间内存。它是真的将数据复制到内存中,还是数据仍然存在于磁盘中?mmap()比 快吗read()

4

4 回答 4

35

这个mmap函数真正做的唯一一件事就是改变一些内核数据结构,可能还有页表。它实际上根本没有将任何东西放入物理内存中。调用后mmap,分配的区域可能甚至不指向物理内存:访问它会导致页面错误。这种页面错误是由内核透明地处理的,实际上这是内核的主要职责之一。

发生的情况mmap是数据保留在磁盘上,并在您的进程读取它时从磁盘复制到内存。它也可以推测性地复制到物理内存中。当您的进程被换出时,mmap不必将区域中的页面写入交换,因为它们已经由长期存储支持——当然,除非您修改了它们。

但是,mmap会消耗虚拟地址空间,就像malloc和其他类似的功能一样(主要mmap在幕后使用,或者sbrk,它基本上是一个特殊版本mmap)。mmap使用读取文件和读取文件之间的主要区别在于,区域read中未修改的页面mmap不会对整体内存压力产生影响,只要它们不被使用,它们几乎是“空闲”的,内存明智的。相比之下,使用该read函数读取的文件无论是否正在使用,是否已被修改,都会造成内存压力。

最后,mmap它比read仅在它喜欢的用例中更快——随机访问和页面重用。对于线性遍历文件,尤其是小文件,read通常会更快,因为它不需要修改页表,并且需要更少的系统调用。

作为建议,我可以说您将要扫描的任何大文件通常应该mmap在 64 位系统上完整读取,并且您可以mmap在虚拟内存较少的 32 位系统上以块的形式读取。

另请参阅:mmap() 与读取块

另请参阅(感谢 James):我什么时候应该使用 mmap 进行文件访问?

于 2012-09-12T08:34:47.530 回答
2

数据仍然存在于磁盘上。操作系统分配一些物理内存并将文件数据复制到其中,以便您访问那里的文件内容(当您尝试访问文件数据时会发生这种情况)。该物理内存被映射到进程的虚拟地址空间。操作系统可以取消映射文件中长时间未使用的部分,并在需要时将它们映射回来。如果可用物理内存很少,取消映射可能会更加激进,从而导致性能下降。

文件的内存映射:

  1. 比简单的“文件读/写”使用更少的物理内存和虚拟地址空间,因为这里和那里(在操作系统、C 标准库和您的程序中)没有文件缓冲区,并且它们之间没有不必要的复制。

  2. 可以比简单的“文件读取/由于上述原因,并且您避免了“文件读取”系统调用所涉及的用户模式和内核模式之间的转换。剩下的唯一转换是那些映射当前未映射的特定页面的转换,它们由页面错误(= CPU 异常)启动,在内核中处理。只要映射了您需要的所有内容,访问文件数据时就没有用户内核转换。

于 2012-09-12T08:34:59.167 回答
2

进程的“虚拟内存”是它可用的地址范围。为了使某些东西在内存中可用,您需要保留一定范围的地址,因此mmap()会占用一些虚拟内存。

在 Linux 下(以及许多其他系统可能使用类似的机制),在读取文件时,首先将内容读入内核分配的内存中(在 Linux 中这是“页面缓存”)。如果您使用mmap(),则只需通过在该进程的地址空间中为其分配一些地址,即可使该内存可供该进程使用。如果使用read(),则该进程分配一个缓冲区,该缓冲区需要地址(虚拟内存)和居住地(物理内存),并且数据从页面缓存复制到该缓冲区(需要更多物理内存)。

数据仅在实际访问时从磁盘读取。如果mmap()这意味着当你实际寻址内存时,read()它就是你的缓冲区的副本,所以在read()调用内部。

因此mmap()对于大文件更有效,尤其是对于随机访问。缺点是它只能用于文件而不是类似文件的对象(管道、套接字、设备、/proc 文件等),并且在页面错误期间检测到 IO 故障,它们很难处理(它发送 SIGBUS 信号),而 read 可以返回错误并且应用程序可以尝试恢复(大多数都不会)。后者主要关注网络文件系统,其中 IO 失败可能是由于连接丢失。

于 2012-09-12T08:35:39.780 回答
1

复制并不意味着原件被销毁。

它将磁盘的内容映射到内存中,所以当然在某些时候必须复制这些位,是的。

由于这意味着它需要地址空间,因此占用了进程的部分虚拟地址空间。

于 2012-09-12T08:20:26.990 回答