假设地址空间可以覆盖文件,在我看来mmap只是分配一块与要读取的文件一样大的内存块,并在它们相应的块之间创建一对一的关系.但是,为什么这样做会加快文件读取速度?似乎为了实际获取文件的内容,您仍然必须转到磁盘,并读取它上面的所有字节.
与malloc相同大小的内存并手动将整个文件读入malloc区域相比,它有何不同?
解决方法:
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,然后系统在Nk之前释放任何页面)
当您执行read(2)时,无法告诉系统不再需要给定的内核FS页面缓冲区.它们会一直徘徊,直到物理RAM填满[或超过给定的限制],这给整个内存系统增加了压力.
实际上,使用read,我已经看到在应用程序移动到文件的不同部分或完全不同的文件之后,用于FS缓冲区的内存量仍然很长.事实上,我已经看到一个I / O密集型应用程序使用如此多的缓冲区,导致无关的[idle]进程将其页面被盗并刷新到分页磁盘.当我停止I / O应用程序时,firefox需要花费几分钟来重新登录并再次响应.
我为常规阅读vs mmap做了一些广泛的基准测试.通过它们,mmap可以提高某些应用程序的速度.
请在此处查看我的答案:read line by line in the most efficient way *platform specific*
在我这样做之前,我对mmap的好处持怀疑态度,但基准测试表明mmap是一个胜利者.
此外,如果您正在读取(2)(针对速度)与fgets,如果给定的行跨越读取缓冲区边界(即缓冲区的最后50个字符),则可能会因缓冲区移位而陷入困境拥有80个char行的前50个字节).
请注意,在此链接页面的评论中,还有另一个指向pastebin的链接到我的基准程序的更高版本,结果太大,无法在上述SO答案中发布基准并比较各种madvise选项