mmap 的工作方式不同。它是可预见的并适应程序的访问模式。此外,还可以通过madvise 设置具体政策,以进一步微调使用情况。
要更深入地讨论mmap 在按需分页环境中的工作原理,请在此处查看我的回答:Which segments are affected by a copy-on-write?,因为它还讨论了mmap 的使用
mmap 是通过execve 等执行程序的命脉。人。所以,你可以打赌它很快。附带说明一下,具有讽刺意味的是,malloc 实际上也使用匿名 mmap。
但是,对于此处的讨论,请特别注意带有 mmap 的文件与使用 malloc 和 read(2) 的文件的“后备存储”(即分页磁盘)
对于mmap,内存区域的后备存储是文件本身。该区域将页面直接映射到内核的文件系统缓冲区页面[它们已经统一了很长时间]。因此,不需要像 read(2) 那样从内核文件系统缓冲区页面到应用程序页面的 [浪费] 复制。
当您执行malloc/read 时,您仍然拥有上述页面,但是,现在 malloc 的区域在分页/交换磁盘上有一个后备存储。因此,页面缓冲区的数量是mmap 的两倍。正如我所提到的,读取完成后必须将数据复制到该区域中。
此外,就性能而言,进行大量读取不是最佳选择。建议的大小约为 64 KB,一个块 [取决于文件系统]。
当您进行大量读取时,您的程序在完成之前无法启动。如果文件的大小大于物理内存,系统会读入你的 malloc 区域,并且会浪费性地开始将较早的页面刷新到分页磁盘,以便为接近文件末尾的页面腾出空间,直到整个文件被阅读。
换句话说,当这个大的预读发生时,应用程序正在等待[并且什么都不做]。对于[比如说] 60 GB 的文件,启动时间会很明显。
如果您的文件确实足够大,您甚至会用完分页磁盘上的空间(即malloc 返回 NULL)。
对于mmap,不存在此类问题。当您映射一个文件时,您可以立即开始使用它。它将直接从该区域的后备存储[再次是文件系统中的文件]按需“故障”。而且,如果您有 [比如说] 1 TB 的文件,mmap 就可以很好地处理。
此外,您可以通过madvise(2) 和posix_madvise(2) 逐页或任何页面范围(包括整个文件)控制映射策略。 madvise 系统调用相对轻量级,因此可以大量使用。这是一个提示,但不会执行会延迟应用程序的 I/O。如果 I/O 开始预读提示,它是由内核作为后台活动完成的。
您甚至可以告诉系统很快将需要给定页面 [系统将此作为预取它的提示],或者您可以告诉系统不再需要该页面 [并且系统将释放页缓冲内存]。
您可以对整个文件说“顺序访问”之类的话,这意味着系统将知道自动进行预读,以及释放不再需要的页面(即,如果您当前正在访问第 N 页,则系统释放 N-k 之前的任何页面)
当您执行read(2) 时,无法告诉系统不再需要给定的内核 FS 页面缓冲区。它们会一直徘徊,直到物理 RAM 填满 [或超出给定限制],这会增加整个内存系统的压力。
在实践中,使用read,我发现在应用程序移动到文件的不同部分或完全不同的文件后,用于 FS 缓冲区的内存量仍然很高。事实上,我已经看到一个 I/O 密集型应用程序使用了如此多的缓冲区,以至于导致不相关的 [空闲] 进程的页面被盗并刷新到页面磁盘。当我停止 I/O 应用程序时,firefox 需要几分钟时间才能将自身分页并再次响应。
我为常规读取与 mmap 做了一些广泛的基准测试。通过它们,mmap 可以提高某些应用程序的速度。
在这里查看我的答案:read line by line in the most efficient way *platform specific*
在我这样做之前,我对 mmap 的好处持怀疑态度,但基准测试表明 mmap 是赢家。
此外,如果您正在使用read(2)(速度)与fgets,如果给定行跨越读取缓冲区边界(即最后 50缓冲区的字符具有 80 字符行的前 50 个字节)。
请注意,在此链接页面的 cmets 中,还有另一个指向 pastebin 的链接,指向我的基准程序的更高版本,结果太大而无法在上述 SO 答案上发布,该答案对各种 madvise 选项进行基准测试和比较