![对 /proc/pid/maps 中的 PSS 感到困惑](https://linux22.com/image/22047/%E5%AF%B9%20%2Fproc%2Fpid%2Fmaps%20%E4%B8%AD%E7%9A%84%20PSS%20%E6%84%9F%E5%88%B0%E5%9B%B0%E6%83%91.png)
我找到了一个关于 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_page
中mm/memory.c
)
这不是一个“正常”映射(在某种意义上vm_normal_page
),并且不会在smaps
字段中被视为共享或私有任何内容(smaps_pte_entry
在 中fs/proc/task_mmu.c
完全跳过“特殊”页面)。不过,它确实在 RSS 和大小中得到了考虑:从地址空间的角度来看,这些虚拟页面存在并且已被“使用”。
如果您开始修改(写入)该区域中的任何页面,它将获得一个正确的、带有匿名页面的正常映射(在这种特定情况下为零初始化,有趣的是 - 如果前一个(非-正常/假)映射不是到零页)。 (参见do_wp_page
。mm/memory.c
)此时您将看到smaps
显示您所期望的内容。
请注意,无论是 C、POSIX 还是其他任何东西都不能保证这些页面包含零,您不能依赖这一点。(您实际上也不能在 Linux 上依赖这一点 - 这是目前的实现方式,但可以想象它可能会改变。)