confuso sobre PSS em /proc/pid/maps

confuso sobre PSS em /proc/pid/maps

Encontrei uma ótima explicação sobre smaps em informações sobre smaps

No meu entendimento, pensei que

limpeza_compartilhada + sujeira_compartilhada + limpeza_privada + sujeira_privada = rss

Eu escrevi um programa para verificar isso:

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);
}

Mas para minha surpresa, /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

Alguma coisa errada com meu entendimento?


de acordo com a resposta de Mat,

As páginas dos 6M que você está apenas lendo não podem ser consideradas limpas. Uma página limpa é aquela que está sincronizada com seu armazenamento de apoio (seja lá o que for, swap, um arquivo, etc.).

.

Reescrevi os códigos usando mmap, desta vez o resultado é o esperado :)

crie um arquivo fictício primeiro:

time dd if=/dev/zero of=test.bin bs=30000000 count=1

novo Código:

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);
} 

gato /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

Responder1

Primeiro, seu código exibe um comportamento indefinido ( cnté usado sem ter sido inicializado, o mesmo para os 6M principais que você está lendo sem inicializar), portanto, certifique-se de que seu compilador realmente produza instruções que correspondam ao seu código: não é necessário. (Presumo que você tenha verificado isso.)

As páginas dos 6M que você está apenas lendo não podem ser consideradas limpas. Uma página limpa é aquela que está sincronizada com seu armazenamento de apoio (seja lá o que for, swap, um arquivo, etc.). Essas páginas não têm nada que as apóie.
Eles também não estão realmente sujos no sentido usual - afinal, eles não foram modificados.

Então, o que está acontecendo aqui? Todas as páginas no bloco de 6M que você está lendo apenas são mapeadas para a mesma página, e essa página é a "página zero" (ou seja, uma página compartilhada (pelo menos em x86) que contém 4k zero bytes).

Quando o kernel recebe uma falha de página em uma página anônima não mapeada, e essa falha é uma leitura, ele mapeia em uma página zero (a mesma página todas as vezes). (Isso está em do_anonymous_pagein mm/memory.c)
Este não é um mapeamento "normal" (no vm_normal_pagesentido) e não é contabilizado nos smapscampos como nada compartilhado ou privado ( smaps_pte_entryin fs/proc/task_mmu.cpula páginas "especiais" inteiramente). Porém, ele é contabilizado em RSS e tamanho: do ponto de vista do espaço de endereço, essas páginas virtuais existem e foram "usadas".
Se você começar a modificar (escrever em) qualquer página nessa área, ela obterá um mapeamento normal e adequado com uma página anônima (inicializada com zero neste caso específico, curiosamente - não será inicializada com zero se a anterior (não -normal/fake) o mapeamento não estava na página zero). (Veja do_wp_pageem mm/memory.c.) Nesse ponto, você verá smapso que espera.

Observe que nada em C, POSIX ou qualquer outra coisa garante que essas páginas contenham zeros, você não pode confiar nisso. (Na verdade, você também não pode confiar nisso no Linux - é assim que está implementado agora, mas pode mudar.)

informação relacionada