/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_page) mm/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 또는 다른 어떤 것도 이러한 페이지에 0이 포함된다는 것을 보장하지 않으며 이에 의존할 수 없습니다. (실제로 Linux에서도 이에 의존할 수 없습니다. 이것이 현재 구현된 방식이지만 아마도 변경될 수 있습니다.)

관련 정보