En este artículo veremos cómo MDB nos puede ayudar a conocer el uso que está haciendo el kernel de la memoria del sistema, cuantas páginas de memoria está ocupando un proceso, el tamaño de los segmentos de una espacio de direcciones, las páginas de memoria asociadas a un vnodes, etc.
Antes de continuar, debemos aclarar algunos conceptos y nomenclaturas propias de Solaris, para poder interpretar toda la información que vamos a obtener con mdb.
Páginas
Como ya sabemos, todos los procesos, disponen de su propio espacio de direcciones de memoria, los espacios de direcciones están formados por segmentos de memoria, un espacio de direcciones tiene al menos 4 segmentos de memoria, el de datos, el de texto, el de heap y el de stack. Cada segmento de memoria está formado por un número determinado de páginas de memoria. La página la podemos definir como un bloque de información que puede ser almacenada en un marco de página en memoria física o en un almacenamiento secundario.
NOTA – Marco de página, se denomina así al bloque de memoria física en el que se almacena una página.
Las páginas tienen un tamaño fijo, suelen ser de 8K, con el comando pagesize podemos ver qué tamaños de página podemos utilizar en nuestro sistema y cual es el tamaño por defecto. Utilizando el ! podemos ejecutar comandos del sistema operativo dentro de mdb.
(root@huelva)# mdb -k Loading modules: [ unix krtld genunix ip usba ipc random nfs ptm ] > > ! pagesize 8192 > > ! pagesize -a 8192 65536 524288 4194304 >
El tamaño de página por defecto es 8K, aunque podemos disponer de páginas de 8K,64K, 512K y 4MB, el tamaño de página se define en el momento del arranque, por lo que no se puede cambiar en caliente, más adelante veremos una característica disponible desde Solaris 9, que permite que un proceso utilice páginas con distinto tamaño. La relación entre el tamaño de página y el rendimiento del sistema depende de la gestión que se realice de las caches, aunque entraremos más en detalle en otro artículo, podemos adelantar que algunas aplicaciones trabajan mejor con tamaños de página grandes, ya que los accesos a memoria provocarán menos fallos en las caches que si utilizasen páginas de menor tamaño. Siento dejar este tema tan interesante aquí, pero prometo que dentro de poco veremos como afecta el tamaño de las páginas en el rendimiento de las distintas caches del sistema.
En Solaris, si no realizamos ninguna modificación en el sistema, solo existen dos casos en los que las páginas son de tamaño distinto a 8K, uno son las páginas que se utilizan para mapear los segmentos de datos y texto del propio kernel, el otro caso es el de las páginas asignadas a los segmentos de memoria compartida.
Todas las páginas de memoria se representan mediante la estructura de datos page_t, que podemos encontrar en el fichero de cabecera /usr/include/vm/page.h, el kernel organiza las páginas físicas en varias listas, dependiendo de el uso que se les esté dando:
-Hash list, existe un array llamado page_hash el cual es utilizado para realizar búsquedas rápidas sobre las distintas lista.
-Free list, lista de todas las páginas que no están siendo utilizadas por ningún proceso, una página se denomina libre cuando tiene el bit p_free activo en el campo p_state de la estructura page_t.
-Cache list, esta lista la forma las páginas que podemos considerar libres, pero que aun están referenciadas por un vnode y por lo tanto aún contienen información, aunque el sistema las utilizará como páginas libre cuando las necesite, esta operación se conoce como reclamación de página.
-Vnode list, es la lista de todas las páginas que están asociadas a un vnode.
IMPORTANTE!! – En Solaris, la memoria libre del sistema la forman las página de la Free list y las páginas de la Cache list
Páginas anónimas
Todas las páginas de memoria están asociadas a un fichero del sistema de archivos mediante la tupla vnode:desplazamiento, la cual define el fichero al que hace referencia la página y el desplazamiento dentro del fichero, pero existe un tipo de página, las llamadas páginas anónimas, las cuales no tienen asociado un fichero sino que están asociadas al espacio de swap directamente. Las páginas anónimas corresponde a la memoria que se solicita de forma dinámica durante la vida de un proceso, por ejemplo cuando utilizamos la llamada de sistema malloc().
Page Cache
Podemos definir el Page Cache como las páginas que el kernel cachea en memoria cuando se solicita una página del sistema ficheros, es decir, cuando un proceso abre un fichero para leer datos de él, el kernel cachea dichos datos en páginas de memoria, de esta forma si se vuelve a solicitar datos del fichero que correspondan con páginas que se encuentran cacheadas en memoria, el kernel no necesita acceder al dispositivo físico, con la consiguiente perdida de rendimiento, sino que trae el dato de la página cacheada en memoria, disminuyendo considerablemente los tiempos de accesos.
Solaris procurará utilizar toda la memoria disponible en el sistema para el Page Cache liberando dicha memoria cuando la cantidad de memoria caiga por debajo de una serie de umbrales.
NOTA – El espacio ocupado por el contenido del directorio /tmp/ utiliza el Page Cache, por lo que tenemos que tener cuidado en no llenar dicho directorio con ficheros basura, ya que estos ficheros están ocupando memoria del sistema.
El comando de mdb ::memstat nos devuelve un sencillo informe sobre el uso que se está haciendo de la memoria del sistema.
(root@huelva)# mdb -k Loading modules: [ unix krtld genunix ip usba ipc random nfs ptm ] > > ::memstat Page Summary Pages MB %Tot ------------ ---------------- ---------------- ---- Kernel 32775 256 13% Anon 12230 95 5% Exec and libs 12196 95 5% Page cache 153886 1202 62% Free (cachelist) 31933 249 13% Free (freelist) 6483 50 3% Total 249503 1949 >
De la información que hemos obtenido, podemos ver la cantidad de memoria que está utilizando el kernel, la cantidad de memoria asignada a páginas anónimas, la cantidad de memoria asignada a las páginas de los ficheros ejecutables y librerías, la memoria que está siendo utilizada por Page Cache y la memoria que conforman las dos listas de páginas libres.
Podemos hacer la prueba de modificar el tamaño el directorio /tmp, bien creando ficheros de gran tamaño, bien borrando ficheros que hayamos dejado y estén ocupando espacio, en el momento en el que estoy escribiendo este artículo, el /tmp de mi máquina está a un 32% de capacidad, como podemos ver en la siguiente salida.
(root@huelva)# df -k
Filesystem kbytes used avail capacity Mounted on
/dev/vx/dsk/bootdg/rootvol
10327372 4791380 5432719 47% /
/proc 0 0 0 0% /proc
mnttab 0 0 0 0% /etc/mnttab
fd 0 0 0 0% /dev/fd
swap 2292600 112 2292488 1% /var/run
dmpfs 2292488 0 2292488 0% /dev/vx/dmp
dmpfs 2292488 0 2292488 0% /dev/vx/rdmp
swap 3367080 1074592 2292488 32% /tmp
...
Voy a borrar algunos de los ficheros que tengo en /tmp, una vez que los he borrado, vamos a comprobar con mdb cual es la distribución de memoria.
(root@huelva)# mdb -k Loading modules: [ unix krtld genunix ip usba ipc random nfs ptm ] > > ::memstat Page Summary Pages MB %Tot ------------ ---------------- ---------------- ---- Kernel 32833 256 13% Anon 15016 117 6% Exec and libs 12207 95 5% Page cache 76079 594 30% Free (cachelist) 31570 246 13% Free (freelist) 81798 639 33% Total 249503 1949 >
Podemos comprobar como la cantidad de memoria asignada a Page cache se ha reducido hasta el 30% y la memoria Free (freelist) a aumentado hasta el 33%.
Segmentos de memoria
Como ya hemos comentado anteriormente los segmentos son grupos de páginas, todos los procesos disponen de al menos 4 segmentos de memoria.
| Segmento | Descripción |
| Texto | Contiene el una imagen del programa que está ejecutando el proceso. |
| Datos | Son los datos que han sido declarados en el programa. |
| Heap | Este segmento de memoria se utilizará para almacenar los datos que se creen de forma dinámica, con llamadas de tipo malloc() |
| Pila | Se utiliza para almacenar las llamadas a las distintas funciones. |
Vamos a ver como mdb nos ayuda para conocer los distintos segmentos que están asignados al espacio de direcciones de un proceso. Para nuestro ejemplo crearemos un nuevo proceso lanzando el comando sleep 50000 en background.
(root@huelva)# sleep 50000 &
[1] 1788
(root@huelva)#
(root@huelva)# mdb -k
Loading modules: [ unix krtld genunix ip usba ipc random nfs ptm ]
>
>
> 0t1788::pid2proc |::print proc_t p_as |::walk seg |::seg
SEG BASE SIZE DATA OPS
3007d839f80 10000 2000 3000863d598 segvn_ops
300881d6d38 20000 2000 3578600afe8 segvn_ops
3015579b128 22000 2000 3007d8469c0 segvn_ops
3578db66828 ff280000 ac000 3578608a8a0 segvn_ops
3578db4eb40 ff33c000 8000 36ccb9b5a18 segvn_ops
3000360e240 ff380000 2000 357860796c0 segvn_ops
300595aebd0 ff390000 2000 300db327938 segvn_ops
300036af290 ff39a000 2000 362e5007db8 segvn_ops
35785fea3a8 ff3a0000 2000 3578601aa38 segvn_ops
3007d8661b0 ff3b0000 2e000 3578dae20e8 segvn_ops
3007d842120 ff3ee000 2000 301b3697bb8 segvn_ops
30008602678 ff3f0000 2000 3578da37e90 segvn_ops
3578da9d5a8 ffbfe000 2000 3000369f590 segvn_ops
>
Hemos ejecutado el comando sleep en background y hemos obtenido el PID del proceso, para este ejemplo es 1788, después hemos arrancado mdb, una vez dentro del debugger hemos convertido el PID 1788 en la dirección del proceso, se le ha pasado la dirección al comando ::print para que nos imprima el contenido del campo p_as, el cual contiene la dirección del espacio de direcciones del proceso, utilizamos el walker seg, que nos devuelve la dirección de todos los segmento que pertenecen a un espacio de direcciones y por último pasamos esta salida al comando ::seg, el cual imprime cierta información del segmento, como su tamaño, su dirección base o el tipo de operaciones del segmento. Esta salida es parecida a la que podemos obtener con el comando del SO pmap.
Aunque no vayamos a profundizar mucho más, si vamos a continuar buceando algo en las estructuras de datos que mantienen la información sobre los distintos segmentos, para ello vamos a utilizar el comando ::print para que nos represente la estructura de datos struct seg de un segmento.
> 3007d839f80::print -t "struct seg"
{
caddr_t s_base = 0x10000
size_t s_size = 0x2000
uint_t s_szc = 0
struct as *s_as = 0x35785ff6578
avl_node_t s_tree = {
struct avl_node * [2] avl_child = [ 0, 0 ]
uintptr_t avl_pcb = 0x300881d6d59
}
struct seg_ops *s_ops = segvn_ops
void *s_data = 0x3000863d598
}
>
Esta es la información de un segmento, podemos destacar, el puntero s_as con la dirección del espacio de direcciones al que pertenece el segmento, el tamaño del segmento s_size, un puntero a las operaciones permitidas sobre este segmento s_ops, la información sobre el árbol AVL de los segmentos del espacio de direcciones s_tree y el campo s_data que contiene la información especifica para este tipo de segmento.
En el sistema existen varios tipos de segmentos de memoria, dependiendo de la funcionalidad para la que se crearon, así podemos encontrar segmentos de tipo seg_map, seg_kmem, seg_dev o seg_vn, entre otros. Todos los segmentos son de tipo struct seg y lo que los diferencia entre ellos, es el tipo de operaciones y el tipo de dato al que apunta el puntero del campo s_data, por esta razón, este campo es un puntero void. Los ficheros de cabecera donde se encuentran todas las definiciones de los distintos tipos de operaciones y datos en los segmentos, los podemos encontrar en el directorio /usr/include/vm/. Es recomendable ojear los ficheros de cabecera, para familiarizarnos con las distintas estructuras de datos.
En nuestro ejemplo, podemos identificar el tipo de segmento por el campo s_ops = segvn_ops, por lo tanto, el campo s_data a punta a una estructura de tipo struct segvn_data, la cual está definida en el fichero /usr/include/vm/seg_vn.h
> 0x3000863d598::print -t "struct segvn_data"
{
krwlock_t lock = {
void * [1] _opaque = [ 0 ]
}
kmutex_t segp_slock = {
void * [1] _opaque = [ 0 ]
}
uchar_t pageprot = 0
uchar_t prot = 0xd
uchar_t maxprot = 0xf
uchar_t type = 0x2
u_offset_t offset = 0
struct vnode *vp = 0x300055f76b0
ulong_t anon_index = 0
struct anon_map *amp = 0
struct vpage *vpage = 0
struct cred *cred = 0x362b6a64cd8
size_t swresv = 0
uchar_t advice = 0
uchar_t pageadvice = 0
ushort_t flags = 0x10
ssize_t softlockcnt = 0
lgrp_mem_policy_info_t policy_info = {
int mem_policy = 0x1
int mem_reserved = 0
}
}
>
Uno de los campos más interesantes es vnode *vp = 0x300055f76b0, porque contiene la dirección del vnode al que está asociado la página de memoria.
> 0x3000863d598::print -t "struct segvn_data" vp |::print -t vnode_t v_pages
struct page *v_pages = 0x7000289c040
>
> 0x7000289c040::print -t page_t
{
struct vnode *p_vnode = 0x300055f76b0
struct page *p_hash = 0
struct page *p_vpnext = 0x7000289c040
struct page *p_vpprev = 0x7000289c040
struct page *p_next = 0x7000289c040
struct page *p_prev = 0x7000289c040
u_offset_t p_offset = 0
selock_t p_selock = 0
ushort_t p_lckcnt = 0
ushort_t p_cowcnt = 0
kcondvar_t p_cv = {
ushort_t _opaque = 0
}
kcondvar_t p_io_cv = {
ushort_t _opaque = 0
}
uchar_t p_iolock_state = 0
uchar_t p_szc = 0
uchar_t p_fsdata = 0
uchar_t p_state = 0
void * [3] p_msresv = [ 0, 0, 0 ]
}
La salida de los comandos no ha devuelto la información sobre la página de memoria que está asociada al vnode.
El comando ::page
Con el comando ::page podemos obtener la lista de todas las páginas de memoria y a qué vnode está asociada cada página. La salida que obtendremos de la ejecución del comando será parecida a la siguiente.
>
> ::page
PAGE VNODE OFFSET SELOCK LCT COW IO FS ST
00000700017ce2e0 300cf3e4688 0 0 0 0 0 0 0
00000700023e1b40 300ce979690 0 0 0 0 0 0 0
0000070002b09d00 300cf06b690 0 0 0 0 0 0 80
0000070002f25650 30000283dd8 3005d0a8000 0 0 0 0 0 10
0000070001cd9348 35785fc0410 357987f8000 0 0 0 0 0 10
0000070002edd080 30059937300 0 0 0 0 0 0 80
00000700019c1de0 304bc34a628 0 0 0 0 0 0 0
0000070002cf0fd8 30000283b08 300dc188000 0 0 0 0 0 10
0000070002ed4098 304bc34a628 2000 0 0 0 0 0 0
0000070003019250 300cfc536d8 0 0 0 0 0 0 0
0000070001db2260 30155cf0b10 0 0 0 0 0 0 0
000007000268e0c8 35780f80c60 2000 0 0 0 0 0 80
0000070001ea9088 3578127ac70 2000 0 0 0 0 0 80
0000070001dd35f0 300a3a62590 0 0 0 0 0 0 80
000007000298f970 30135e75a28 0 0 0 0 0 0 80
0000070001776950 300a3e5a598 0 0 0 0 0 0 0
000007000296d1b8 35780f80c60 6000 0 0 0 0 0 80
0000070002f255d8 30000283dd8 3005d0c0000 0 0 0 0 0 10
0000070002a52178 3578127ac70 6000 0 0 0 0 0 80
...
Si no utilizamos el comando con una dirección, devolverá la lista de todas las páginas de memoria. La salida está formada por varias columnas, algunas de las cuales son autoexplicativas, como VNODE con las direcciones de los vnodes a los que están asociadas las distintas páginas, OFFSET define el desplazamiento dentro del vnode, LCT define el número de bloqueos de la página, COW contiene el número de bloqueos Copy-on-Write, ST define el estado de la página mediante una serie de flags. La siguiente tabla muestra los posibles flags que definen el estado de la página.
| Flag | Valor | Descripción |
| P_FREE | 0×80 | La página está libre |
| P_NORELOC | 0×40 | La página no es relocable |
| P_MIGRATE | 0×20 | La página se puede migrar |
| P_SWAP | 0×10 | La página está en SWAP |
Solaris: Buceando en el Kernel con mdb (II)<< | >>
Technorati Tag(s) : Solaris OpenSolaris MDB
Algunos derechos reservados. Licencia Creative Commons
Comentarios Recientes
Sobre
Esta plantilla a sido creada con la validacion de CSS y XHTML, por N.Design Studio.Los iconos usados son de Web 2 Mini pack.