据说mmap()
将文件映射到内存,并且它花费在调用进程的虚拟地址空间内存中。它是否真的将数据复制到内存中,或者数据是否仍存在于磁盘中? mmap()
比read()
更快吗?
答案 0 :(得分:24)
mmap
函数唯一真正做的是更改一些内核数据结构,可能还有页表。它根本没有把任何东西放到物理内存中。调用mmap
后,分配的区域可能甚至没有指向物理内存:访问它会导致页面错误。这种页面错误是由内核透明处理的,事实上,这是内核的主要职责之一。
mmap
会发生什么,数据仍保留在磁盘上,并在进程读取时从磁盘复制到内存。它也可以推测性地复制到物理内存。当您的流程被换出时,mmap
区域中的页面不必写入交换,因为它们已经被长期存储支持 - 当然,除非您已经修改过它们。
但是,mmap
将使用虚拟地址空间,就像malloc
和其他类似的功能(主要在幕后使用mmap
或sbrk
,这基本上是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可能会更具攻击性,导致性能不佳。
文件的内存映射:
使用较少的物理内存和虚拟地址空间而不是简单的文件读/写"因为这里和那里没有文件缓冲区(在操作系统中,在C标准库和程序中),并且它们之间没有不必要的复制。
可以是(,并且可能是,如果你在某些条件下有足够的可用物理内存,取决于我们谈论的数据量和操作系统的物理内存量让我们比简单的"文件读/写更快地使用mmap"由于上述原因,并且因为您避免了"文件读取的用户和内核模式之间的转换"系统调用涉及。剩下的唯一转换是映射当前未映射的特定页面的转换,它们由页面错误(= CPU异常)启动,这些错误在内核中处理。只要映射了您需要的所有内容,访问文件数据时就没有用户内核转换。
答案 3 :(得分:1)
mmap()
会占用一些虚拟内存。
在Linux下(许多其他系统可能使用类似的机制),在读取文件时,内容首先被读入内核分配的内存中(在Linux中这是“页面缓存”)。与使用mmap()
相比,通过在该进程的地址空间中为其分配一些地址,可以使该进程可用。如果使用read()
,进程会分配一个缓冲区,该缓冲区需要地址(虚拟内存)和居住地(物理内存),并且数据从页面缓存复制到该缓冲区(需要更多物理内存) )。
实际访问时仅从磁盘读取数据。在mmap()
情况下,它意味着当您实际处理内存时,在read()
中它是您缓冲区的副本,因此在read()
调用内。
因此mmap()
对大文件更有效,特别是对于随机访问。缺点是它只能用于文件而不是文件类对象(管道,套接字,设备,/ proc文件等),并且在页面错误期间检测到IO故障,它们很难处理(它发送SIGBUS信号),而读取可以返回错误,应用程序可以尝试恢复(大多数都不会)。后者主要关注网络文件系统,其中IO故障可能是由于连接丢失。