对 /proc/pid/maps 中的 PSS 感到困惑

对 /proc/pid/maps 中的 PSS 感到困惑

我找到了一个关于 smaps 的很好的解释 有关 smap 的信息

据我了解,我认为

共享_clean + 共享_dirty + 私有_clean + 私有_dirty = rss

我写了一个程序来验证一下:

void sa();
int main(int argc,char *argv[])
{
    sa();
    sleep(1000);
}

void sa()
{
   char *pi=new char[1024*1024*10];
   for(int i=0;i<4;++i) {   //dirty should be 4M
        for(int j=0;j<1024*1024;++j){
                *pi='o';
                pi++;
        }
   }
   int cnt;
   for(int i=0;i<6;++i) {   //clean should be 6M
        for(int j=0;j<1024*1024;++j){
                cnt+=*pi;
                pi++;
        }
   }
   printf("%d",cnt);
}

但令我惊讶的/proc/pid/smaps是:

09b69000-09b8c000 rw-p 00000000 00:00 0 [heap]
...
Size:           10252 kB
Rss:            10252 kB
Pss:             4108 kB //<----I thought it should be 10M
Shared_Clean:       0 kB
Shared_Dirty:       0 kB
Private_Clean:      0 kB //<----I thought it should be 6M
Private_Dirty:   4108 kB
Referenced:      4108 kB
Swap:               0 kB
KernelPageSize:     4 kB
MMUPageSize:        4 kB

我的理解有什么问题吗?


根据马特的回答,

你仅仅阅读的6M页面并不能真正被认为是干净的。干净页面是与其后备存储(无论是交换、文件等)同步的页面。

我用mmap重写了代码,这次结果符合预期:)

首先创建一个虚拟文件:

time dd if=/dev/zero of=test.bin bs=30000000 count=1

新代码:

void sa(char *pi)
{
   for(int i=0;i<4;++i) {
        for(int j=0;j<1024*1024;++j){
                *pi='a';
                pi++;
        }
   }
   //just to use it to avoid the following code will not optimized off by the compiler 
   int dummycnt=0;
   for(int i=0;i<6;++i) {
        for(int j=0;j<1024*1024;++j){
                dummycnt+=*pi;
                pi++;
        }
   }
   printf("%d",dummycnt);
}


int main()
{
       int fd  = open("./test.bin",O_RDWR);
       char *p = (char *)mmap(0,
                      1024*1024*10, //10M
                      PROT_READ|PROT_WRITE,
                      MAP_SHARED,
                      fd,
                      0);
       sa(p);
       sleep(10000);
} 

猫/ proc / pid / smaps:

b6eae000-b78ae000 rw-s 00000000 08:05 134424     ..../test.bin
Size:              10240 kB
Rss:               10240 kB
Pss:               10240 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:      6144 kB
Private_Dirty:      4096 kB
Referenced:        10240 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB

答案1

首先,您的代码表现出未定义的行为(cnt在未初始化的情况下使用,与您在未初始化的情况下阅读的前 6M 内容相同),因此请确保您的编译器实际上输出与您的代码匹配的指令:但不一定如此。 (我假设你已经检查过了。)

你仅仅阅读的6M页面并不能真正被认为是干净的。干净页面是与其后备存储(无论是交换、文件等)同步的页面。这些页面没有任何支持它们的内容。
从通常的意义上来说,它们也不是很脏——毕竟,它们没有被修改过。

那么这里发生了什么?您仅读取的 6M 块中的所有页面都映射到同一页面,该页面是“零页面”(即包含 4k 零字节的共享(至少在 x86 上)页面)。

当内核在未映射的匿名页面上发生页面错误并且该错误是读取时,它会映射到零页面(每次都是同一页面)。 (这是在do_anonymous_pagemm/memory.c
这不是一个“正常”映射(在某种意义上vm_normal_page),并且不会在smaps字段中被视为共享或私有任何内容(smaps_pte_entry在 中fs/proc/task_mmu.c完全跳过“特殊”页面)。不过,它确实在 RSS 和大小中得到了考虑:从地址空间的角度来看,这些虚拟页面存在并且已被“使用”。
如果您开始修改(写入)该区域中的任何页面,它将获得一个正确的、带有匿名页面的正常映射(在这种特定情况下为零初始化,有趣的是 - 如果前一个(非-正常/假)映射不是到零页)。 (参见do_wp_pagemm/memory.c)此时您将看到smaps显示您所期望的内容。

请注意,无论是 C、POSIX 还是其他任何东西都不能保证这些页面包含零,您不能依赖这一点。(您实际上也不能在 Linux 上依赖这一点 - 这是目前的实现方式,但可以想象它可能会改变。)

相关内容