为什么mmap文件导致使用比文件大小更多的内存?

问题描述:

我与mmap试验,并与下面的示例代码来:为什么mmap文件导致使用比文件大小更多的内存?

int main() { 

    int fd; 
    char *filename = "/home/manu/file"; 
    struct stat statbuf; 
    int i = 0; 
    char c = *(filename); 

    // Get file descriptor and file length 
    fd = open(filename, O_RDONLY); 
    if (fd == -1) { 
     perror("fopen error"); 
    } 
    if (fstat(fd, &statbuf) < 0) { 
     perror("fstat error"); 
    } 
    printf("File size is %ld\n", statbuf.st_size); 

    // Map the file 
    char* mmapA = (char*) mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, 
      fd, 0); 
    if (mmapA == MAP_FAILED) { 
     perror("mmap error"); 
     return 1; 
    } 

    // Touch all the mapped pages 
    while (i < statbuf.st_size) { 
     c = mmapA[i]; 
     i++; 
    } 
    c++; 

    // Close file descriptor 
    if (close(fd) == -1) { 
     perror("close"); 
     return 1; 
    } 

    //Unmap file 
    munmap(mmapA, statbuf.st_size); 

    return EXIT_SUCCESS; 
} 

的文件大小为137948个字节= 134,7千字节。 检查程序的内存我正在使用top,主要是RES和VIRT列。

  1. 只是
  2. 只是mmap电话
  3. mmap调用之前读取所有映射内存有该文件的有效加载到主内存后(:我在三个不同的地方寻找这些值之后页面错误)

由最高记录的数值是

  1. VIRT = 1828个RES = 244
  2. VIRT = 1964和RES = 248
  3. VIRT = 1964和RES = 508

1964年至1828年= 136,我想在千字节,因此完美地匹配文件的大小。

但我不明白RES的差异508 - 248 = 260 ..为什么它不同于虚拟内存大小和文件大小?

+0

操作系统被编码为最常用的最佳用途,或者呼叫声最大的客户的典型用途 - 它做了很多事情,可能对您的使用没有意义。你真的在意吗? – 2012-04-18 20:44:05

+0

@MartinBeckett不,我根本不在乎;-)这只是为了学习的目的,我很好奇。 – 2012-04-18 20:47:49

+0

好吧,您可以随时阅读源代码;-)我的猜测是,它总是保留mmap以上的特定内存以防扩展 - 即使在这里您只能打开只读。但我不知道,所以我没有添加这个作为答案 – 2012-04-18 20:53:16

有一点是肯定的:结果取决于系统的状态,而不仅仅取决于正在运行的应用程序。在我的机器上,在运行程序的前两次,RES的增加量为136 kB,但随后的运行根本不涉及任何增加 - 可能操作系统已将整个文件放入缓存中。有趣的是,运行之间的价值自身差异很大。在第一次运行中,RES的跳跃从344到480kB,但后者运行的RES值始终为348kB。 SHR也有类似的变化:第一次跳136KB,稍后没有变化。

在运行应用程序之前,我可以随意使用dd覆盖之后用零映射的文件来强制原始情况(使用136 kB跳转)。

我看着pmaps输出,但在两种情况下完全一样,在致电mmap()后没有改变。

我不能在这里再现超大的RES跳跃,但这里是你可以做的。假设你的二进制文件编译为a.out。在mmap()之后插入10秒钟的睡眠时间,然后在munmap()之前再睡10秒钟。这给出了一个时间窗口来转储有趣的信息。我们将从/proc中读取哪些文件驻留在内存中。为了做到这一点,在你的终端中的其他选项卡中打开了两个选项卡,在一个运行

./a.out 

,然后立即:

for ((i=0;i<4;i++)); do cat /proc/$(ps -fe | egrep '[a]\.out' | awk '{print $2}')/smaps > smaps.$i; sleep 5; done 

这将创建4个快照程序的地图状态四个单独的文件。连续编号的快照之间的差异应显示在RES大小激增期间发生了哪些变化。在样品运行期间,我的机器,差异快照1和2之间,以及更改[注意我改变映射文件的名称,但在这里并不重要]:

[email protected]:~$ diff -u smaps.{1,2} 
--- smaps.1  2012-04-19 00:01:46.000000000 +0200 
+++ smaps.2  2012-04-19 00:01:51.000000000 +0200 
@@ -84,13 +84,13 @@ 
MMUPageSize:   4 kB 
b782f000-b7851000 r--p 00000000 08:05 429102  /tmp/tempfile 
Size:    136 kB 
-Rss:     0 kB 
-Pss:     0 kB 
+Rss:     136 kB 
+Pss:     136 kB 
Shared_Clean:   0 kB 
Shared_Dirty:   0 kB 
-Private_Clean:   0 kB 
+Private_Clean:  136 kB 
Private_Dirty:   0 kB 
-Referenced:   0 kB 
+Referenced:   136 kB 
Swap:     0 kB 
KernelPageSize:  4 kB 
MMUPageSize:   4 kB 

会发生什么事是什么应该:映射文件最初根本不驻留,稍后驻留136 kB。

在您的系统上,diff应该引导您进入RES中额外更改的来源 - 您应该能够找出Rss值更改的其他文件的名称。有些条目不是文件,而是其他内存区域,例如,您可能会发现诸如[heap][stack]之类的标记。这也应该证明或反驳nos关于系统库被加载和堆栈使用增长的建议。

+0

非常感谢这个完整的答案。至于你使用smaps,我在内存映射文件的RSS中只有136kb的差异。因此,我知道想知道顶部或ps(我以前使用的)报告究竟在RSS标签下面。 – 2012-04-19 19:39:24

+0

'top'是['procps'](http://sourceforge.net/projects/procps/)包的一部分,它也从'/ proc'获取数据,但显然来自与smaps不同的地方'。如果您深入研究[code](http://procps.cvs.sourceforge.net/viewvc/procps/procps/),您可能会在'top'中找到RES列的确切来源。 – 2012-04-19 20:54:50