Tabla de contenidos
Descubrir cuánta RAM usa un proceso de Linux no es un asunto simple, especialmente cuando se necesita considerar la memoria compartida. Afortunadamente, el pmap
comando te ayuda a darle sentido a todo.
Mapeo de memoria
En los sistemas operativos modernos , cada proceso vive en su propia región asignada de memoria o espacio de asignación . Los límites de la región asignada no se asignan directamente a las direcciones de hardware físico. El sistema operativo crea un espacio de memoria virtual para cada proceso y actúa como una capa de abstracción que asigna la memoria virtual a la memoria física.
El núcleo mantiene una tabla de traducción para cada proceso, y la CPU accede a ella . Cuando el kernel cambia el proceso que se ejecuta en un núcleo de CPU en particular , actualiza la tabla de traducción que une los procesos y los núcleos de CPU.
Los beneficios de la abstracción
Hay beneficios en este esquema. El uso de la memoria está algo encapsulado y aislado para cada proceso en el área de usuario. Un proceso solo «ve» la memoria en términos de las direcciones de memoria virtual. Esto significa que solo puede funcionar con la memoria que le ha dado el sistema operativo. A menos que tenga acceso a alguna memoria compartida, no conoce ni tiene acceso a la memoria asignada a otros procesos.
La abstracción de la memoria física basada en hardware en direcciones de memoria virtual permite que el kernel cambie la dirección física a la que se asigna alguna memoria virtual. Puede cambiar la memoria al disco cambiando la dirección real a la que apunta una región de memoria virtual. También puede diferir el suministro de memoria física hasta que realmente se requiera.
Siempre que las solicitudes para leer o escribir en la memoria se atiendan a medida que se solicitan, el núcleo es libre de hacer malabarismos con la tabla de mapeo como mejor le parezca.
RAM bajo demanda
La tabla de mapeo y el concepto de “RAM bajo demanda” abren la posibilidad de memoria compartida . El núcleo intentará evitar cargar lo mismo en la memoria más de una vez. Por ejemplo, cargará una biblioteca compartida en la memoria una vez y la asignará a los diferentes procesos que necesitan usarla. Cada uno de los procesos tendrá su propia dirección única para la biblioteca compartida, pero todos apuntarán a la misma ubicación real.
Si se puede escribir en la región compartida de la memoria, el kernel usa un esquema llamado copia en escritura. Si un proceso escribe en la memoria compartida y se supone que los otros procesos que comparten esa memoria no deben ver los cambios, se crea una copia de la memoria compartida en el punto de la solicitud de escritura.
El kernel de Linux 2.6.32, lanzado en diciembre de 2009, le dio a Linux una función llamada «Kernel SamePage Merging». Esto significa que Linux puede detectar regiones idénticas de datos en diferentes espacios de direcciones. Imagine una serie de máquinas virtuales que se ejecutan en una sola computadora y todas las máquinas virtuales ejecutan el mismo sistema operativo. Usando un modelo de memoria compartida y copia en escritura, la sobrecarga en la computadora host puede reducirse drásticamente.
Todo lo cual hace que el manejo de la memoria en Linux sea sofisticado y óptimo. Pero esa sofisticación hace que sea difícil observar un proceso y saber cuál es realmente su uso de memoria.
La utilidad pmap
El kernel expone mucho de lo que está haciendo con la RAM a través de dos pseudo-archivos en el pseudo-sistema de archivos de información del sistema “/proc”. Hay dos archivos por proceso, con el nombre del ID de proceso o PID de cada proceso: “/proc/maps” y “/proc//smaps”.
La pmap
herramienta lee la información de estos archivos y muestra los resultados en la ventana del terminal. Será obvio que debemos proporcionar el PID del proceso que nos interesa cada vez que usamos pmap
.
Encontrar la identificación del proceso
Hay varias formas de encontrar el PID de un proceso . Aquí está el código fuente de un programa trivial que usaremos en nuestros ejemplos. Está escrito en C. Todo lo que hace es imprimir un mensaje en la ventana de la terminal y esperar a que el usuario presione la tecla «Enter».
#incluir <stdio.h> int main(int argc, char *argv[]) { printf("Programa de prueba How-To Geek"); getc(entrada estándar); } // fin de principal
El programa fue compilado a un ejecutable llamado pm
usando el gcc
compilador:
gcc -o pm pm.c
Debido a que el programa esperará a que el usuario presione «Enter», permanecerá ejecutándose todo el tiempo que queramos.
./pm
El programa se inicia, imprime el mensaje y espera la pulsación de la tecla. Ahora podemos buscar su PID. El ps
comando enumera los procesos en ejecución. La -e
opción (mostrar todos los procesos) hace una ps
lista de todos los procesos. Canalizaremos la salida grep
y filtraremos las entradas que tengan «pm» en su nombre.
pd-e | grep pm
Esto enumera todas las entradas con «pm» en cualquier lugar de sus nombres.
Podemos ser más específicos usando el pidof
comando. Damos pidof
el nombre del proceso que nos interesa en la línea de comando e intenta encontrar una coincidencia. Si se encuentra una coincidencia, pidof
imprime el PID del proceso de coincidencia.
pido de la tarde
El pidof
método es más ordenado cuando conoce el nombre del proceso, pero el ps
método funcionará incluso si solo conoce parte del nombre del proceso.
usando pmap
Con nuestro programa de prueba en ejecución, y una vez que hayamos identificado su PID, podemos usar pmap de esta manera:
pmapa 40919
Las asignaciones de memoria para el proceso se enumeran para nosotros.
Aquí está el resultado completo del comando:
40919: ./pm 000056059f06c000 4K r---- pm 000056059f06d000 4K rx-- pm 000056059f06e000 4K r---- pm 000056059f06f000 4K r---- pm 000056059f070000 4K rw--- p.m. 000056059fc39000 132K rw--- [anónimo] 00007f97a3edb000 8K rw--- [anónimo] 00007f97a3edd000 160K r---- libc.so.6 00007f97a3f05000 1616K rx-- libc.so.6 00007f97a4099000 352K r---- libc.so.6 00007f97a40f1000 4K ----- libc.so.6 00007f97a40f2000 16K r---- libc.so.6 00007f97a40f6000 8K rw--- libc.so.6 00007f97a40f8000 60K rw--- [anónimo] 00007f97a4116000 4K r---- ld-linux-x86-64.so.2 00007f97a4117000 160K rx--ld-linux-x86-64.so.2 00007f97a413f000 40K r---- ld-linux-x86-64.so.2 00007f97a4149000 8K r---- ld-linux-x86-64.so.2 00007f97a414b000 8K rw --- ld-linux-x86-64.so.2 00007ffca0e7e000 132K rw--- [pila] 00007ffca0fe1000 16K r---- [anónimo] 00007ffca0fe5000 8K rx-- [anónimo] ffffffffff600000 4K --x-- [anónimo] total 2756K
La primera línea es el nombre del proceso y su PID. Cada una de las otras líneas muestra una dirección de memoria asignada y la cantidad de memoria en esa dirección, expresada en kilobytes. Los siguientes cinco caracteres de cada línea se denominan permisos de memoria virtual . Los permisos válidos son:
- r : El proceso puede leer la memoria mapeada.
- w : La memoria mapeada puede ser escrita por el proceso.
- x : el proceso puede ejecutar cualquier instrucción contenida en la memoria mapeada.
- s : la memoria asignada se comparte y los cambios realizados en la memoria compartida son visibles para todos los procesos que comparten la memoria.
- R : No hay reserva de espacio de intercambio para esta memoria asignada.
La información final en cada línea es el nombre de la fuente del mapeo. Puede ser un nombre de proceso, un nombre de biblioteca o un nombre de sistema, como pila o montón.
La pantalla extendida
La -x
opción (extendida) proporciona dos columnas adicionales.
pmap -x 40919
Las columnas reciben títulos. Ya hemos visto las columnas «Dirección», «Kbytes», «Modo» y «Mapeo». Las nuevas columnas se llaman «RSS» y «Dirty».
Aquí está la salida completa:
40919: ./pm Dirección Kbytes RSS Modo sucio Mapeo 000056059f06c000 4 4 0 r---- pm 000056059f06d000 4 4 0 rx-- pm 000056059f06e000 4 4 0 r---- pm 000056059f06f000 4 4 4 r---- pm 000056059f070000 4 4 4 rw--- pm 000056059fc39000 132 4 4 rw--- [anónimo] 00007f97a3edb000 8 4 4 rw--- [anónimo] 00007f97a3edd000 160 160 0 r---- libc.so.6 00007f97a3f05000 1616 788 0 rx-- libc.so.6 00007f97a4099000 352 64 0 r---- libc.so.6 00007f97a40f1000 4 0 0 ----- libc.so.6 00007f97a40f2000 16 16 16 r---- libc.so.6 00007f97a40f6000 8 8 8 rw--- libc.so.6 00007f97a40f8000 60 28 28 rw--- [anónimo] 00007f97a4116000 4 4 0 r---- ld-linux-x86-64.so.2 00007f97a4117000 160 160 0 rx-- ld-linux-x86-64.so.2 00007f97a413f000 40 40 0 r---- ld-linux-x86-64.so.2 00007f97a4149000 8 8 8 r---- ld-linux-x86-64.so.2 00007f97a414b000 8 8 8 rw---ld-linux-x86-64.so.2 00007ffca0e7e000 132 12 12 rw--- [pila] 00007ffca0fe1000 16 0 0 r---- [ anónimo ] 00007ffca0fe5000 8 4 0 rx-- [anónimo] ffffffffff600000 4 0 0 --x-- [anónimo] ------- ------- ------- ------- kB totales 2756 1328 96
- RSS : Este es el tamaño del conjunto residente . Es decir, la cantidad de memoria que hay actualmente en la RAM y que no se ha intercambiado.
- Sucia : la memoria «sucia» ha cambiado desde que comenzó el proceso y la asignación.
Muestrame todo
El -X
(incluso más que extendido) agrega columnas adicionales a la salida. Tenga en cuenta la «X» mayúscula. Otra opción llamada -XX
(incluso más que -X
) le muestra todo lo que pmap
puede obtener del kernel. Como -X
es un subconjunto de -XX
, describiremos la salida de -XX
.
pmap -XX 40919
La salida se envuelve horriblemente en una ventana de terminal y es prácticamente indescifrable. Aquí está la salida completa:
40919: ./pm Dirección Perm Desplazamiento Dispositivo Tamaño de inodo KernelPageSize MMUPageSize Rss Pss Shared_Clean Shared_Dirty Private_Clean Private_Dirty Referenciado Anónimo LazyFree AnonHugePages ShmemPmdMapped FilePmdMapped Shared_Hugetlb Private_Hugetlb Swap SwapPss Bloqueado THPeligible VmFlags Asignación 56059f06c000 r--p 00000000 08:03 393304 4 4 4 4 4 0 0 4 0 4 0 0 0 0 0 0 0 0 0 0 0 rd mr mw me dw sd pm 56059f06d000 r-xp 00001000 08:03 393304 4 4 4 4 4 0 0 4 0 4 0 0 0 0 0 0 0 0 0 0 0 rd ex mr mw me dw sd pm 56059f06e000 r--p 00002000 08:03 393304 4 4 4 4 4 0 0 4 0 4 0 0 0 0 0 0 0 0 0 0 0 rd mr mw me dw sd pm 56059f06f000 r--p 00002000 08:03 393304 4 4 4 4 4 0 0 0 4 4 4 0 0 0 0 0 0 0 0 0 0 rd mr mw me dw ac sd pm 56059f070000 rw-p 00003000 08:03 393304 4 4 4 4 4 0 0 0 4 4 4 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me dw ac sd pm 56059fc39000 rw-p 00000000 00:00 0 132 4 4 4 4 0 0 0 4 4 4 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me ac sd [heap] 7f97a3edb000 rw-p 00000000 00:00 0 8 4 4 4 4 0 0 0 4 4 4 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me ac sd 7f97a3edd000 r--p 00000000 08:03 264328 160 4 4 160 4 160 0 0 0 160 0 0 0 0 0 0 0 0 0 0 0 rd mr mw me sd libc.so.6 7f97a3f05000 r-xp 00028000 08:03 264328 1616 4 4 788 32 788 0 0 0 788 0 0 0 0 0 0 0 0 0 0 0 rd ex mr mw me sd libc.so.6 7f97a4099000 r--p 001bc000 08:03 264328 352 4 4 64 1 64 0 0 0 64 0 0 0 0 0 0 0 0 0 0 0 rd mr mw me sd libc.so.6 7f97a40f1000 ---p 00214000 08:03 264328 4 4 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 señor mw me sd libc.so.6 7f97a40f2000 r--p 00214000 08:03 264328 16 4 4 16 16 0 0 0 16 16 16 0 0 0 0 0 0 0 0 0 0 rd mr mw me ac sd libc.so.6 7f97a40f6000 rw-p 00218000 08:03 264328 8 4 4 8 8 0 0 0 8 8 8 0 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me ac sd libc.so.6 7f97a40f8000 rw-p 00000000 00:00 0 60 4 4 28 28 0 0 0 28 28 28 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me ac sd 7f97a4116000 r--p 00000000 08:03 264305 4 4 4 4 0 4 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 rd mr mw me dw sd ld-linux-x86-64.so.2 7f97a4117000 r-xp 00001000 08:03 264305 160 4 4 160 11 160 0 0 0 160 0 0 0 0 0 0 0 0 0 0 0 rd ex mr mw me dw sd ld-linux-x86-64.so.2 7f97a413f000 r--p 00029000 08:03 264305 40 4 4 40 1 40 0 0 0 40 0 0 0 0 0 0 0 0 0 0 0 rd mr mw me dw sd ld-linux-x86-64.so.2 7f97a4149000 r--p 00032000 08:03 264305 8 4 4 8 8 0 0 0 8 8 8 0 0 0 0 0 0 0 0 0 0 0 rd mr mw me dw ac sd ld-linux-x86-64.so.2 7f97a414b000 rw-p 00034000 08:03 264305 8 4 4 8 8 0 0 0 8 8 8 0 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me dw ac sd ld-linux-x86-64.so.2 7ffca0e7e000 rw-p 00000000 00:00 0 132 4 4 12 12 0 0 0 12 12 12 0 0 0 0 0 0 0 0 0 0 rd wr mr mw me gd ac [pila] 7ffca0fe1000 r--p 00000000 00:00 0 16 4 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 rd mr pf io de dd sd [vvar] 7ffca0fe5000 r-xp 00000000 00:00 0 8 4 4 4 0 4 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 rd ex mr mw me de sd [vdso] ffffffffff600000 --xp 00000000 00:00 0 4 4 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ex [vsyscall] ==== ============== =========== ==== === ============ == ========== ============= ============= ========== ==== ===== ======== ============= ============== ========== === ============== =============== ==== ======= ====== = ========== 2756 92 92 1328 157 1220 0 12 96 1328 96 0 0 0 0 0 0 0 0 0 0 KB
Hay mucha información aquí. Esto es lo que contienen las columnas:
- Dirección : la dirección de inicio de esta asignación. Esto utiliza el direccionamiento de la memoria virtual.
- Perm : Los permisos de la memoria.
- Desplazamiento : si la memoria está basada en archivos, el desplazamiento de esta asignación dentro del archivo.
- Dispositivo : el número de dispositivo de Linux, dado en números mayores y menores. Puede ver los números de dispositivo en su computadora ejecutando el
lsblk
comando. - Inodo : el inodo del archivo con el que está asociado el mapeo. Por ejemplo, en nuestro ejemplo, este podría ser el inodo que contiene información sobre el programa pm.
- Tamaño : El tamaño de la región mapeada en memoria.
- KernelPageSize : el tamaño de página utilizado por el núcleo.
- MMUPageSize : el tamaño de página utilizado por la unidad de administración de memoria.
- Rss : Este es el tamaño del conjunto residente . Es decir, la cantidad de memoria que hay actualmente en la RAM y que no se ha intercambiado.
- Pss : Este es el tamaño de la parte proporcional . Este es el tamaño compartido privado agregado al (tamaño compartido dividido por el número de acciones).
- Shared_Clean : la cantidad de memoria compartida con otros procesos que no se ha modificado desde que se creó la asignación. Tenga en cuenta que incluso si la memoria es compartible, si no se ha compartido realmente, todavía se considera memoria privada.
- Shared_Dirty : la cantidad de memoria compartida con otros procesos que se ha modificado desde que se creó la asignación.
- Private_Clean : la cantidad de memoria privada, no compartida con otros procesos, que no se ha modificado desde que se creó la asignación.
- Private_Dirty : la cantidad de memoria privada que se ha modificado desde que se creó la asignación.
- Referenciada : la cantidad de memoria actualmente marcada como referenciada o accedida.
- Anónimo : memoria que no tiene un dispositivo para intercambiar. Es decir, no está respaldado por archivos.
- LazyFree : páginas que han sido marcadas como
MADV_FREE
. Estas páginas se han marcado como disponibles para ser liberadas y reclamadas, aunque pueden tener cambios no escritos en ellas. Sin embargo, si se producen cambios posteriores después deMADV_FREE
que se haya establecido en la asignación de memoria, elMADV_FREE
indicador se elimina y las páginas no se recuperarán hasta que se escriban los cambios. - AnonHugePages : estas son páginas de memoria «enormes» sin respaldo de archivos (más de 4 KB).
- ShmemPmdMapped : memoria compartida asociada con páginas grandes. También pueden ser utilizados por sistemas de archivos que residen completamente en la memoria.
- FilePmdMapped : el Page Middle Directory es uno de los esquemas de paginación disponibles para el núcleo. Este es el número de páginas respaldadas por archivos a las que apuntan las entradas de PMD.
- Shared_Hugetlb : Translation Lookaside Tables, o TLB, son cachés de memoria que se utilizan para optimizar el tiempo necesario para acceder a las ubicaciones de memoria del espacio de usuario. Esta cifra es la cantidad de RAM utilizada en los TLB que están asociados con páginas de memoria enorme compartidas.
- Private_Hugetlb : esta cifra es la cantidad de RAM utilizada en los TLB que están asociados con páginas privadas de gran memoria.
- Intercambio : la cantidad de intercambio que se utiliza.
- SwapPss : El tamaño de la parte proporcional del swap . Esta es la cantidad de intercambio compuesta por páginas de memoria privada intercambiadas agregadas al (tamaño compartido dividido por el número de recursos compartidos).
- Bloqueado : las asignaciones de memoria se pueden bloquear para evitar que el sistema operativo pagine la memoria del montón o fuera del montón.
- THElegible : Este es un indicador que indica si el mapeo es elegible para asignar páginas grandes transparentes . 1 significa verdadero, 0 significa falso. Las páginas grandes transparentes son un sistema de administración de memoria que reduce la sobrecarga de las búsquedas de páginas TLB en computadoras con una gran cantidad de RAM.
- VmFlags : vea la lista de banderas a continuación.
- Mapeo : El nombre de la fuente del mapeo. Puede ser un nombre de proceso, un nombre de biblioteca o nombres de sistema como stack o heap.
Las VmFlags (marcas de memoria virtual) serán un subconjunto de la siguiente lista.
- rd : Legible.
- wr : Escribible.
- ej : ejecutable.
- sh : Compartido.
- mr : Puede leer.
- mw : Puede escribir.
- yo : Puede ejecutar.
- ms : Puede compartir.
- gd : El segmento de pila crece hacia abajo.
- pf : rango de número de marco de página pura. Los números de marco de página son una lista de las páginas de memoria física.
- dw : escritura deshabilitada en el archivo asignado.
- lo : las páginas están bloqueadas en la memoria.
- io : Área de E/S mapeada en memoria.
- sr : Asesoramiento de lectura secuencial proporcionado (por la
madvise()
función). - rr : Asesoramiento de lectura aleatoria proporcionado.
- dc : no copie esta región de memoria si el proceso está bifurcado.
- de : No expanda esta región de memoria en la reasignación.
- ac : El área es responsable.
- nr : El espacio de intercambio no está reservado para el área.
- ht : el área usa páginas TLB enormes.
- sf : falla de página síncrona.
- ar : indicador específico de la arquitectura.
- wf : Limpia esta región de memoria si el proceso está bifurcado.
- dd : no incluya esta región de memoria en los volcados del núcleo.
- sd : Bandera blanda sucia.
- mm : Área de mapa mixto.
- hg : Indicador de aviso de página enorme.
- nh : No hay bandera de aviso de página grande.
- mg : Indicador de notificación fusionable.
- bt : Página protegida de inestabilidad de temperatura de polarización ARM64.
- mt : ARM64 Las etiquetas de extensión de etiquetado de memoria están habilitadas.
- um : falta el seguimiento de Userfaultfd.
- uw : seguimiento de protección wr de Userfaultfd.
La gestión de la memoria es complicada
Y trabajar hacia atrás a partir de tablas de datos para comprender lo que realmente está sucediendo es difícil. Pero al menos pmap
le brinda una imagen completa para que tenga la mejor oportunidad de descubrir lo que necesita saber.
Es interesante notar que nuestro programa de ejemplo se compiló en un ejecutable binario de 16 KB y, sin embargo, usa (o comparte) unos 2756 KB de memoria, casi en su totalidad debido a las bibliotecas de tiempo de ejecución.
Un buen truco final es que puede usar pmap
y los pidof
comandos juntos, combinando las acciones de encontrar el PID del proceso y pasarlo a pmap
un solo comando:
pmap $(pidof pm)