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

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

我找到了一個關於 smap 的很好的解釋 有關 smap 的信息

據我了解,我認為

共用清潔 + 共用清潔 + 私人清潔 + 私人清潔 = 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 上依賴它——這就是它現在的實現方式,但可以想像它可能會改變。)

相關內容