mmap真的会将数据复制到内存中吗?

时间:2012-09-12 08:18:57

标签: c linux

据说mmap()将文件映射到内存,并且它花费在调用进程的虚拟地址空间内存中。它是否真的将数据复制到内存中,或者数据是否仍存在于磁盘中? mmap()read()更快吗?

4 个答案:

答案 0 :(得分:24)

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

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

但是,mmap将使用虚拟地址空间,就像malloc和其他类似的功能(主要在幕后使用mmapsbrk,这基本上是mmap的特殊版本。使用mmap读取文件和read读取文件的主要区别在于mmap区域中未修改的页面不会对整体内存压力造成影响,它们几乎是“免费”的记忆方面,只要它们没有被使用。相比之下,使用read函数读取的文件将始终导致内存压力,无论它们是否被使用,以及它们是否已被修改。

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

作为建议,我可以说你要扫描的任何大文件通常都应该在64位系统上用mmap完整阅读,你可以mmap以块为单位在虚拟内存不太可用的32位系统上。

另请参阅:mmap() vs. reading blocks

另见(感谢James):When should I use mmap for file access?

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:1)

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

文件的内存映射:

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

  2. 可以是(,并且可能是,如果你在某些条件下有足够的可用物理内存,取决于我们谈论的数据量和操作系统的物理内存量让我们比简单的"文件读/写更快地使用mmap"由于上述原因,并且因为您避免了"文件读取的用户和内核模式之间的转换"系统调用涉及。剩下的唯一转换是映射当前未映射的特定页面的转换,它们由页面错误(= CPU异常)启动,这些错误在内核中处理。只要映射了您需要的所有内容,访问文件数据时就没有用户内核转换。

答案 3 :(得分:1)

进程的“虚拟内存”是可用的地址范围。要在内存中创建一些内容,您需要保留一系列地址,因此mmap()会占用一些虚拟内存。

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

实际访问时仅从磁盘读取数据。在mmap()情况下,它意味着当您实际处理内存时,在read()中它是您缓冲区的副本,因此在read()调用内。

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