【发布时间】:2012-09-12 08:18:57
【问题描述】:
据说mmap()将文件映射到内存,它消耗了调用进程的虚拟地址空间内存。它是真的将数据复制到内存中,还是数据仍然存在于磁盘中? mmap() 比 read() 快吗?
【问题讨论】:
-
什么意思。当然数据驻留在磁盘上,当然数据必须复制到内存中才能被您的程序访问(使用 mmap 或其他方法)。
据说mmap()将文件映射到内存,它消耗了调用进程的虚拟地址空间内存。它是真的将数据复制到内存中,还是数据仍然存在于磁盘中? mmap() 比 read() 快吗?
【问题讨论】:
mmap 函数真正做的唯一一件事就是改变一些内核数据结构,可能还有页表。它实际上根本没有将任何东西放入物理内存中。调用mmap 后,分配的区域可能甚至不指向物理内存:访问它会导致页面错误。这种页面错误是由内核透明地处理的,实际上这是内核的主要职责之一。
mmap 的情况是数据保留在磁盘上,并在您的进程读取数据时从磁盘复制到内存。它也可以推测性地复制到物理内存中。当你的进程被换出时,mmap 区域中的页面不必被写入交换,因为它们已经由长期存储支持——当然,除非你修改了它们。
但是mmap会消耗虚拟地址空间,就像malloc和其他类似的功能一样(大多在后台使用mmap,或者sbrk,基本上是mmap的特殊版本) .使用mmap 读取文件和使用read 读取文件之间的主要区别在于mmap 区域中未修改的页面不会造成整体内存压力,它们几乎是“空闲”的,内存明智,只要因为它们没有被使用。相比之下,使用read 函数读取的文件无论是否被使用,无论是否被修改,都会造成内存压力。
最后,mmap 比read 快,只是在它喜欢的用例上——随机访问和页面重用。对于线性遍历文件,尤其是小文件,read 通常会更快,因为它不需要修改页表,并且需要更少的系统调用。
作为建议,我可以说,您将要扫描的任何大文件通常应该在 64 位系统上使用 mmap 完整读取,而您可以在 32 位系统上以块的形式使用 mmap虚拟内存较少的系统。
另请参阅:mmap() vs. reading blocks
另请参阅(感谢 James):When should I use mmap for file access?
【讨论】:
数据仍然存在于磁盘上。操作系统分配一些物理内存并将文件数据复制到其中,以便您访问那里的文件内容(当您尝试访问文件数据时会发生这种情况)。该物理内存被映射到进程的虚拟地址空间。操作系统可以取消映射文件中长时间未使用的部分,并在需要时将它们映射回来。如果可用物理内存很少,取消映射可能会更加激进,从而导致性能下降。
文件的内存映射:
比简单的“文件读/写”使用更少的物理内存和虚拟地址空间,因为这里和那里(在操作系统、C 标准库和您的程序中)没有文件缓冲区,并且没有不必要的在它们之间复制。
可以是(并且可能是,如果你有足够的可用物理内存在某些条件下,这取决于我们正在谈论的数据量以及操作系统允许我们使用多少物理内存由于上述原因,并且您避免了“文件读取”系统调用所涉及的用户模式和内核模式之间的转换,因此用于 mmap'ing)比简单的“文件读取/写入”更快。剩下的唯一转换是那些映射当前未映射的特定页面的转换,它们由页面错误(= CPU 异常)启动,在内核中处理。只要映射了您需要的所有内容,访问文件数据时就不会发生用户内核转换。
【讨论】:
mmap 读取文件的方法仅在某些情况下更快,“可能”肯定是太强的断言。与read 不同,mmap 需要更重的系统调用、更改页表,并且需要生成页面错误才能读取数据。使用read,如果数据已经在磁盘缓存中,系统调用几乎可以立即返回,而不会在内核和用户空间之间“来回”。
进程的“虚拟内存”是它可用的地址范围。为了使某些东西在内存中可用,您需要保留一个地址范围,因此mmap() 会占用一些虚拟内存。
在 Linux 下(以及许多其他系统可能使用类似的机制),在读取文件时,首先将内容读入内核分配的内存(在 Linux 中这是“页面缓存”)。如果您使用mmap(),则只需通过在该进程的地址空间中为其分配一些地址,即可使该内存可供该进程使用。如果您使用read(),则该进程分配一个缓冲区,该缓冲区需要地址(虚拟内存)和居住地(物理内存),并且数据从页面缓存复制到该缓冲区(需要更多物理内存)。
只有在实际访问时才从磁盘读取数据。在mmap() 的情况下,这意味着当您实际寻址内存时,在read() 中它是到缓冲区的副本,因此在read() 调用中。
因此mmap() 对于大文件更有效,尤其是对于随机访问。缺点是它只能用于文件而不是类似文件的对象(管道、套接字、设备、/proc 文件等),并且在页面错误期间检测到 IO 故障,它们很难处理(它发送 SIGBUS 信号),而 read 可以返回错误并且应用程序可以尝试恢复(大多数都不会)。后者主要关注网络文件系统,其中 IO 失败可能是由于失去连接。
【讨论】:
mmap 读取文件的方法仅在某些情况下更有效,这是由于修改页表、使缓冲区无效以及用户空间和内核空间之间的额外上下文切换的开销。此外,如果您使用SIGBUS 的默认信号处理程序,从mmap 返回的区域上的失败 IO 只会导致程序崩溃——处理SIGBUS 允许您从与mmap 相关的 IO 故障中恢复。
复制并不意味着原件被销毁。
它将磁盘的内容映射到内存中,所以当然在某些时候必须复制这些位,是的。
因为这意味着它需要地址空间,所以占用了进程的部分虚拟地址空间。
【讨论】: