confuso acerca de PSS en /proc/pid/maps

confuso acerca de PSS en /proc/pid/maps

Encontré una gran explicación sobre smaps de información sobre smaps

A mi entender, pensé que

limpieza_compartida + suciedad_compartida + limpieza_privada + suciedad_privada = rss

Escribí un programa para verificarlo:

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

Pero para mi sorpresa, el /proc/pid/smapses:

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

¿Hay algún problema con mi comprensión?


según la respuesta de Mat,

Las páginas del 6M que estás leyendo no pueden considerarse realmente limpias. Una página limpia es aquella que está sincronizada con su almacén de respaldo (lo que sea, un intercambio, un archivo, etc.).

.

Reescribo los códigos usando mmap, esta vez el resultado es el esperado :)

Primero cree un archivo ficticio:

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

nuevo 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

Respuesta1

En primer lugar, su código muestra un comportamiento indefinido ( cntse usa sin haber sido inicializado, lo mismo para los 6M principales que está leyendo sin inicializar), así que asegúrese de que su compilador realmente genere instrucciones que coincidan con su código: no es necesario. (Supongo que lo has comprobado).

Las páginas del 6M que estás leyendo no pueden considerarse realmente limpias. Una página limpia es aquella que está sincronizada con su almacén de respaldo (lo que sea, un intercambio, un archivo, etc.). Esas páginas no tienen nada que las respalde.
Tampoco están realmente sucios en el sentido habitual; después de todo, no han sido modificados.

Entonces, ¿qué está pasando aquí? Todas las páginas del bloque de 6M que solo estás leyendo están asignadas a la misma página, y esa página es la "página cero" (es decir, una página compartida (al menos en x86) que contiene 4k cero bytes).

Cuando el kernel recibe un error de página en una página anónima no asignada, y ese error es una lectura, se asigna en una página cero (la misma página cada vez). (Esto está en do_anonymous_pagein mm/memory.c)
Este no es un mapeo "normal" (en el vm_normal_pagesentido), y no se contabiliza en los smapscampos como nada compartido o privado ( smaps_pte_entryin fs/proc/task_mmu.comite las páginas "especiales" por completo). Sin embargo, se contabiliza en RSS y Tamaño: desde la perspectiva del espacio de direcciones, estas páginas virtuales existen y han sido "usadas".
Si comienza a modificar (escribir en) cualquier página en esa área, obtendrá una asignación normal y adecuada con una página anónima (inicializada en cero en este caso específico, curiosamente, no se inicializará en cero si la página anterior (no -normal/falso) el mapeo no estaba en la página cero). (Ver do_wp_pageen mm/memory.c.) En ese momento verás smapsmostrar lo que esperas.

Tenga en cuenta que nada en C, POSIX ni ningún otro elemento garantiza que estas páginas contengan ceros, no puede confiar en eso. (En realidad, tampoco se puede confiar en eso en Linux; así es como está implementado en este momento, pero posiblemente podría cambiar).

información relacionada