¿Quieres dar formación sobre rendimiento en Solaris/OpenSolaris a tus técnicos?

A raíz del interés mostrado en las últimas charlas he decidido ofrecer mis servicios para consultoría y formación a empresas.

Ponte en contacto conmigo para pedir información.

OpenSolaris: Page Scanner

Normalmente cuando leemos un manual de tunning en Solaris, en el apartado de memoria siempre se menciona que observes la columna sr de la salida del vmstat, si esta es mayor que 0 indica que el sistema tiene escasez de memoria física. Esta columna (scan rate) indica el número de veces que se ha ejecutado el Page Scanner en el intervalo de tiempo que hayamos especificado al vmstat. Pero como funciona ? en este articulo lo trataremos con algo más de profundidad.

El Page Scanner se encarga de robar páginas de memoria dejándolas libres para ser usadas y enviando a la swap su contenido. Si estas páginas son requeridas más adelante se producirá un page fault y volverán a ser copiadas a la memoria física. Lógicamente esto afecta al rendimiento de las aplicaciones que las estaban usando por ello se trata de robar aquellas que hace más tiempo que no han sido accedidas.

El Page Scanner controla que páginas han sido accedidas mediante la lectura de dos bits en el hardware de la memoria . Estos indican si la página ha sido leída o modificada desde la última vez que se pusieron a cero. Entra en acción cuando la cantidad de memoria libre es menor que lostfree que se calcula en el arranque del sistema y suele ser 1/64 de la memoria física disponible. Esta comprobación se realiza mediante la función schedpaging(). Existe una entrada en la tabla callout que la ejecuta 4 veces por segundo. También puede ser llamada por el page_allocator.

Para explicar su funcionamiento pensaremos en el como si se tratara de un reloj, las dos manillas son dos threads del kernel y las horas las distintas páginas de la memoria. A diferencia de un reloj normal, las dos manillas del nuestro giran a la misma velocidad manteniendo entre ellas un espacio y nunca se llegan a superponer.


gráficos1


El thread A pone a 0 los bits del hardware que indican si la memoria a sido modificada/leída, el thread B comprueba si estos bits han sido modificados, en caso negativo envía a la swap el contenido de la página y la marca como libre. Ambos threads corresponden al pid 2.

La velocidad de "rotación" los threads es inversamente proporcional a la cantidad de memoria libre del sistema, y el espacio entre ellos es determinado por el parámetro handsoreadpages que se calcula de forma dinámica. El parámetro pageout_new_spread indica el número máximo de páginas que pueden ser escaneadas en un segundo o dicho de otra forma fija la velocidad máxima de escaneo, es calculado la primera vez que el scanner se ejecuta y se almacena en la variable fastscan.

Algunas peculiaridades:

Existe una limitación para que el scanner no robe páginas de librerías compartidas, el número de mapeos que debe existir para no ser robada es almacenado en el parámetro po_share, inicialmente tiene un valor de 8 pero puede decrementarse en caso de que el scanner no encuentre páginas para liberar.

También existe una limitación del tiempo de cpu que puede ser consumido por los threads, hay dos parámetros el min_precent_cpu y el max_percent_cpu, cuando la memoria libre es igual al parámetro lostfree su consumo es el valor mínimo, cuando no hay memoria libre su valor es el máximo. Por defecto son el 4% y el 80% de una cpu respectivamente.

Viendo el código fuente.

Podemos echar un vistazo al código fuente en la web de opensolaris.org, concretamente en el fichero vm_pageout.c, voy a copiar algunos extractos para ilustrar el articulo pero esta sobradamente documentado por lo que su lectura completa puede resultar interesante.

Cálculo del parámetro lostfree:
    252 	/*
253 	 * Lotsfree is threshold where paging daemon turns on.
254 	 */
255 	if (init_lfree == 0 || init_lfree >= looppages)
256 		lotsfree = MAX(looppages / 64, btop(512 * 1024));
257 	else
258 		lotsfree = init_lfree;

Inicialización de fastscan:

416 	if (init_mfscan == 0) {
417 		if (pageout_new_spread != 0)
418 			maxfastscan = pageout_new_spread;
419 		else
420 			maxfastscan = MAXHANDSPREADPAGES;
421 	} else {
422 		maxfastscan = init_mfscan;
423 	}
424 	if (init_fscan == 0)
425 		fastscan =MIN(looppages / loopfraction,maxfastscan);
426 	else
427 		fastscan =init_fscan;
428 	if (fastscan >looppages / loopfraction)
429		fastscan = looppages /loopfraction
La función pageout_scanner es el scanner propiamente dicho, copio solo el comienzo debido a su longitud, visitad el enlace al fichero completo para verla entera:
731 pageout_scann(void)
732 {
733 	struct page *fronthand, *backhand;
734 	uint_t count;
735 	callb_cpr_t cprinfo;
736 	pgcnt_t	nscan_limit;
737 	pgcnt_t	pcount;
738 
739 	CALLB_CPR_INIT(&cprinfo,&pageout_mutex, callb_generic_cpr,"poscan");
740 	mutex_enter(&pageout_mutex);
[...]

Rastreando la actividad del Page Scanner en nuestro sistema

Como decía al principio del artículo la columna sr de la salida del vmstat es el rastro más evidente que deja el scanner al ejecutarse y la mayoría de manuales de tunning recomiendan revisarla para verificar si hay escasez de memoria física.

Sin embargo podemos obtener algo más de información del scanner en nuestro sistema. El comando "kstat -n system_pages" nos da los valores de algunos de los parámetros antes comentados.

module: unix                            instance: 0
name:   system_pages                    class:    pages
        availrmem                       5851
        crtime                          0
        desfree                         239
        desscan                         3825
	econtig                         4274888704
        fastscan                        15302
        freemem                         230
        kernelbase                      3556769792
        lotsfree                        478
        minfree                         119
        nalloc                          18895816
        nalloc_calls                    12648
        nfree                           17047790
        nfree_calls                     7993
        nscan                           3825
        pagesfree                       230
        pageslocked                     24754
        pagestotal                      30605
        physmem                         30606
        pp_kernel                       26396
        slowscan                        100
        snaptime                        5882.612731441

Podemos ver el lotsfree, el fastscan, slowscan, ... otro dato interesante es el nscan que indica el número de páginas que ha comprobado el scanner desde la última vez que se activó.

Con dtrace también podemos hacer varias comprobaciones, por ejemplo podemos ver el número de veces que se llama a la función pageout_scann():

dtrace -n 'vminfo::pageout_scanner: { @num[probefunc] = count
(); }'
dtrace: description 'vminfo::pageout_scanner: ' matched 3 probes

pageout_scanner					60997

Palabras finales

Como hemos podido ver la actividad del Page Scanner no es nunca lineal sino que su rendimiento es inversamente proporcional a la escasez de memoria detectada, sin embargo existen una serie de parámetros que limitad su actividad de manera que no pueda convertirse el mismo en el cuello de botella del sistema ocupando demasiados recursos.

Creative Commons License
Esta obra está bajo una licencia de Creative Commons.