запутанный вопрос о PSS в /proc/pid/maps

запутанный вопрос о PSS в /proc/pid/maps

Я нашел одно замечательное объяснение по поводу 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:

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используется без инициализации, то же самое касается верхних 6 МБ, которые вы читаете без инициализации), поэтому убедитесь, что ваш компилятор действительно выводит инструкции, соответствующие вашему коду: это не обязательно. (Я предполагаю, что вы это проверили.)

Страницы в 6M, которые вы только читаете, не могут считаться чистыми. Чистая страница — это та, которая синхронизирована со своим резервным хранилищем (что бы это ни было, своп, файл и т. д.). Эти страницы не имеют ничего, что их поддерживает.
Они также не являются грязными в обычном смысле — в конце концов, они не были изменены.

Так что же здесь происходит? Все страницы в блоке 6 МБ, который вы только читаете, отображаются на одну и ту же страницу, и эта страница является «нулевой страницей» (т. е. общей (по крайней мере на x86) страницей, которая содержит 4 Кб нулевых байтов).

Когда ядро ​​получает ошибку страницы на неотображенной анонимной странице, и эта ошибка является чтением, оно отображает нулевую страницу (одну и ту же страницу каждый раз). (Это в в do_anonymous_page) mm/memory.cЭто
не «нормальное» отображение (в том vm_normal_pageсмысле), и не учитывается в smapsполях как что-либо общее или частное ( smaps_pte_entryв fs/proc/task_mmu.cполностью пропускает «специальные» страницы). Однако оно учитывается в RSS и Size: с точки зрения адресного пространства эти виртуальные страницы существуют и «использовались».
Если вы начнете изменять (записывать) любую страницу в этой области, оно получит правильное, нормальное отображение с анонимной страницей (инициализированной нулем в этом конкретном случае, что интересно — она не будет инициализирована нулем, если предыдущее (ненормальное/поддельное) отображение не было на нулевую страницу). (См. do_wp_pageв mm/memory.c.) В этот момент вы увидите smapsто, что ожидаете.

Обратите внимание, что ни в C, ни в POSIX, ни где-либо еще нет гарантий, что эти страницы будут содержать нули, на это нельзя полагаться. (На самом деле, на это нельзя полагаться и в Linux — сейчас это реализовано именно так, но, возможно, ситуация изменится.)

Связанный контент