为什么fopen/fgets使用mmap和read系统调用来访问数据?

bdk*_*bdk 10 c linux mmap file

我有一个小的示例程序,它只是fopen一个文件并用于fgets读取它.使用strace,我注意到第一次调用fgets运行mmap系统调用,然后读取系统调用用于实际读取文件的内容.在fclose,文件munmap编辑.如果我改为打开直接读取文件,这显然不会发生.我很好奇这mmap是什么目的,以及它正在实现什么.

在我的基于Linux 2.6.31的系统上,当需要大量虚拟内存时,这些mmaps有时会挂起几秒钟,在我看来是不必要的.

示例代码:

#include <stdlib.h>
#include <stdio.h>
int main ()
{
   FILE *f;
   if ( NULL == ( f=fopen( "foo.txt","r" )))
   {
     printf ("Fail to open\n");
   }
   char buf[256];
   fgets(buf,256,f);
   fclose(f);
}
Run Code Online (Sandbox Code Playgroud)

以上是运行上述代码时的相关strace输出:

open("foo.txt", O_RDONLY)               = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=9, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb8039000
read(3, "foo\nbar\n\n"..., 4096)        = 9
close(3)                                = 0
munmap(0xb8039000, 4096)                = 0
Run Code Online (Sandbox Code Playgroud)

Bla*_*iev 15

这不是mmap'ed' 文件- 在这种情况下mmap是匿名使用的(不是在文件上),可能是为后续读取将使用的缓冲区分配内存.

malloc实际上是这样的召唤mmap.同样,它munmap对应于一个调用free.

  • @bdk:是的,标准库中的文件函数(不是系统调用)确实保留了自己的缓冲区,这样当你在一个循环中连续调用`fgets(buf,1,f)`时,就不会产生数百个`read`系统调用.`malloc`在用户空间中没有更多可用空间时会产生`mmap` - 例如,第一个`malloc(8)`可能会导致`mmap(4096)`,结果是`malloc(8) )``将返回指向已分配区域的指针,直到它耗尽为止. (5认同)

R..*_*R.. 5

mmap未映射文件; 相反,它为stdio FILE缓冲分配内存.通常malloc不会用于mmap服务这么小的分配,但似乎glibc的stdio实现是mmap直接使用来获取缓冲区.这可能是为了确保页面对齐(虽然posix_memalign可以实现相同的目的)和/或确保关闭文件将缓冲区内存返回给内核.我质疑页面对齐缓冲区的用处.据推测它是为了性能,但我看不出任何方式它会有所帮助,除非您正在读取的文件偏移也是页面对齐的,即便如此,它似乎是一个可疑的微优化.