mmap是否真的将数据复制到内存?

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

Solutions Collecting From Web of "mmap是否真的将数据复制到内存?"

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

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

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

最后, mmap的速度比read的用例要快 – 随机访问和页面重用。 为了线性遍历一个文件,特别是一个小文件, read通常会更快,因为它不需要修改页表,并且占用很少的系统调用。

作为一个建议,我可以说任何你要扫描的大文件都应该在64位系统上用mmap完整地读取,你可以在32位系统上将它们mmap为虚拟内存不多的块。

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

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

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

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

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

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

内存映射文件:

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

  2. 可以是( 也许是,如果在某些条件下有足够的空闲物理内存 ,取决于我们正在讨论的数据量以及操作系统允许我们用于mmap的物理内存量)比简单的“文件读取/写“因为上述,因为你避免了”文件读取“系统调用涉及的用户和内核模式之间的转换。 剩下的唯一转换是映射当前未映射的特定页面的转换,它们是由页面错误(= CPU异常)启动的,这些页面错误在内核中处理。 只要你需要的东西被映射,在访问文件数据的时候就没有用户内核的转换。

进程的“虚拟内存”是可用的地址范围。 为了在内存中提供一些东西,你需要保留一个地址范围,所以mmap()占用一些虚拟内存。

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

数据只在实际访问时从磁盘读取。 在mmap()情况下,它意味着当你实际地址的内存,在read()它是复制到你的缓冲区,所以在read()调用。

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