linux memory-management linux-kernel page-fault

preinicialización de la pila en Linux: fallas únicas o múltiples necesarias



memory-management linux-kernel (2)

Con este código:

int main() { int *a = alloca(100); /* some useful data */ int *b = alloca(50*4096); /* skip 49 pages */ int *c = alloca(100); int i; #if TOUCH > 0 a[0] = 1; // [1] #endif #if TOUCH > 1 c[0] = 1; // [2] #endif #if TOUCH > 2 for (i=0; i<25; i++) // [3] b[i*1024] = 1; #endif #if TOUCH > 3 for (i=25; i<50; i++) // [4] b[i*1024] = 1; #endif return 0; }

Y este guion:

for i in 1 2 3 4; do gcc d.c -DTOUCH=$i echo "Upto [$i]" $(perf stat ./a.out 2>&1 | grep page-faults) done

La salida:

Upto [1] 105 page-faults # 0.410 M/sec Upto [2] 106 page-faults # 0.246 M/sec Upto [3] 130 page-faults # 0.279 M/sec Upto [4] 154 page-faults # 0.290 M/sec

En Linux, cuando el proceso solicita alguna memoria (virtual) del sistema, simplemente se registra en vma (descriptor de la memoria virtual del proceso) pero la página física para cada virtual no se reserva al momento de la llamada. Más adelante, cuando el proceso acceda a esta página, se producirá una falla (el acceso generará una interrupción de la página) y el controlador de PF # asignará la página física y la tabla de la página del proceso de actualización.

Hay dos casos: error cuando la lectura puede convertirse en un enlace a la página cero (página global especial pre-puesta a cero) que está protegida contra escritura; y la falla en la escritura (tanto en la página de cero páginas como en las páginas asignadas que no están físicamente asignadas) dará como resultado la asignación real de páginas físicas privadas.

Para mmaps (y brk / sbrk, que también es internamente mmap), este método es por página; todas las regiones mmaped se registran como un todo en vma (tienen direcciones de inicio y final). Pero la pila se maneja de otra manera, ya que solo tiene una dirección de inicio (una más alta en la plataforma típica, crecer hasta direcciones más bajas).

La pregunta es:

Cuando accedo a la memoria nueva no asignada cerca de la pila, obtendrá PF # y crecerá. ¿Cómo se maneja este crecimiento, si accedo no a la página siguiente a la pila, sino a la página que está a 10 o 100 páginas de la pila?

P.ej

int main() { int *a = alloca(100); /* some useful data */ int *b = alloca(50*4096); /* skip 49 pages */ int *c = alloca(100); a[0]=1; /* no accesses to b - this is untouched hole of 49 pages */ c[0]=1; }

¿Este programa obtendrá 2 o 50 páginas físicas privadas asignadas para la pila?

Creo que puede ser rentable pedirle al núcleo que asigne decenas de páginas físicas en una sola página, luego decenas de páginas predeterminadas asignando página por página (1 interrupción + 1 cambio de contexto + bucle simple, caché en N solicitudes de asignación de página versus N interrupciones + N conmutadores de contexto + N asignaciones de página, cuando el código mm puede ser desalojado de Icache).


El crecimiento automático de la pila se puede considerar como llamadas automáticas a mremap para redimensionar la región de direcciones virtuales que cuenta como "pila". Una vez que se maneja, las fallas de página en el área de la pila o en una región mmap vainilla se manejan de la misma manera, es decir, una página a la vez.

Por lo tanto, debería terminar con ~ 2 páginas asignadas, no ~ 51. La respuesta empírica de @perreal valida esto ...

Hasta la última parte de la pregunta, el costo de las fallas de página contiguas es uno de los factores que conducen al desarrollo de "páginas grandes". No creo que haya otras formas en Linux para manejar el error de la página "por lotes". Tal vez madvise podría hacer algo, pero sospecho que principalmente está optimizando la parte realmente cara de las fallas de página que está buscando las páginas de respaldo en el almacenamiento). Las fallas de página de apilamiento que se asignan a cero páginas son relativamente livianas en comparación.