/proc/pid/mapsのPSSについて混乱している

/proc/pid/mapsのPSSについて混乱している

スマップの良い説明が見つかりました スマップに関する情報

私が理解する限り、私の考えでは

shared_clean + shared_dirty + private_clean + private_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

私の理解に問題がありますか?


Mattの答えに基づいて

読んだばかりの600万ページは、実際にきれいだと見逃すことはできません。クリーンページは、バックアップストア(スワップ、ファイルなど)と同期されるページです。

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と同じ)を示すため、コンパイラが実際にコード一致ディレクティブと同じ出力を出力することを確認してください。しかし、必ずしもそうではありません。 (この部分は確認されたようです。)

読んだばかりの600万ページは、実際にきれいだと見逃すことはできません。クリーンページは、バックアップストア(スワップ、ファイルなど)と同期されるページです。このページにはこれをサポートするコンテンツはありません。
一般的な意味でも汚れません。最終的には修正されませんでした。

では、ここで何が起こっているのでしょうか?読み取った6Mブロックのすべてのページは、同じページ、つまり「ゼロページ」(つまり、4k 0バイトを含む共有(少なくともx86)ページ)にマップされます。

カーネルがマップされていない匿名ページからページエラーを受け取り、エラーが読み取りの場合は、ページ0(毎回同じページ)にマップされます。 (これはにありますdo_anonymous_pagemm/memory.cこれは
「一般」マッピングではなく(ある意味ではvm_normal_page)、smapsフィールドのどれも共有または個人用とは見なされません(「特殊」ページを完全にスキップするsmaps_pte_entry場合)。fs/proc/task_mmu.cただし、RSSとサイズは考慮されます。アドレス空間の観点から見ると、これらの仮想ページが存在し、「使用」されます。
その領域のページの変更(書き込み)を開始すると、匿名ページ(この特別な場合は0に初期化されます。興味深いことに、古い(非正規/偽)マッピングがページ0以外の場合)の適切な一般マッピングを取得します。 )。 (参照do_wp_page)この時点で予想した内容が表示されることがmm/memory.cわかります。smaps

C、POSIX、または他のものは、これらのページにゼロが含まれることを保証せず、それに依存することはできません。 (実際にはLinuxでもこれに依存することはできません。これは現在実装されている方法ですが、おそらく変更される可能性があります。)

おすすめ記事