smapsについての素晴らしい説明を見つけました smapsに関する情報
私の理解では、
共有クリーン + 共有ダーティ + プライベートクリーン + プライベートダーティ = 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 を cat します:
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
考慮されることはありません(は「特別な」ページを完全にスキップします)。ただし、RSS と Size には考慮されます。アドレス空間の観点から、これらの仮想ページは存在し、「使用」されています。 その領域でページを変更 (書き込み) し始めると、匿名ページとの適切な通常のマッピングが行われます (この特定のケースではゼロ初期化されますが、興味深いことに、以前の (非通常の/偽の) マッピングがゼロ ページへのものでなかった場合はゼロ初期化されません)。( を参照してください。 ) その時点で、予想どおりの表示が表示されます。smaps
smaps_pte_entry
fs/proc/task_mmu.c
do_wp_page
mm/memory.c
smaps
C、POSIX、その他のいずれにおいても、これらのページにゼロが含まれることを保証するものは何もないので、それに頼ることはできません。(Linux でも実際にそれに頼ることはできません。現在はこのように実装されていますが、変更される可能性もあります。)