Comprender Linux /proc/pid/maps o /proc/self/maps
Estoy tratando de comprender el uso de la memoria de mi aplicación Linux integrada. La /proc/pid/maps
utilidad/archivo parece ser un buen recurso para ver los detalles. Lamentablemente no entiendo todas las columnas y entradas.
¿Qué significan las entradas del inodo anónimo 0? Estos parecen ser algunos de los segmentos de memoria más grandes.
Cada fila /proc/$PID/maps
describe una región de memoria virtual contigua en un proceso o subproceso. Cada fila tiene los siguientes campos:
address perms offset dev inode pathname
08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm
- dirección : esta es la dirección inicial y final de la región en el espacio de direcciones del proceso.
- permisos : describe cómo se puede acceder a las páginas de la región. Hay cuatro permisos diferentes: lectura, escritura, ejecución y compartido. Si la lectura/escritura/ejecución están deshabilitadas,
-
aparecerá un en lugar der
// . Si una región no es compartida , es privada , por lo que aparecerá un en lugar de un . Si el proceso intenta acceder a la memoria de una manera no permitida, se genera una falla de segmentación. Los permisos se pueden cambiar mediante la llamada al sistema.w
x
p
s
mprotect
- desplazamiento : si la región se asignó desde un archivo (usando
mmap
), este es el desplazamiento en el archivo donde comienza la asignación. Si la memoria no se asignó desde un archivo, es solo 0. - dispositivo : si la región se asignó desde un archivo, este es el número de dispositivo mayor y menor (en hexadecimal) donde reside el archivo.
- inodo : si la región se asignó desde un archivo, este es el número de archivo.
- nombre de ruta : si la región se asignó desde un archivo, este es el nombre del archivo. Este campo está en blanco para regiones mapeadas anónimas. También hay regiones especiales con nombres como
[heap]
,[stack]
o[vdso]
.[vdso]
significa objeto compartido dinámico virtual. Lo utilizan las llamadas al sistema para cambiar al modo kernel. Aquí hay un buen artículo al respecto: "¿Qué es linux-gate.so.1?"
Es posible que notes muchas regiones anónimas. Por lo general, estos son creados por mmap
ningún archivo, pero no están adjuntos a él. Se utilizan para muchas cosas diversas, como memoria compartida o búferes no asignados en el montón. Por ejemplo, creo que la biblioteca pthread utiliza regiones mapeadas anónimas como pilas para nuevos subprocesos.
Por favor consulte: http://man7.org/linux/man-pages/man5/proc.5.html
address perms offset dev inode pathname
00400000-00452000 r-xp 00000000 08:02 173521 /usr/bin/dbus-daemon
El campo de dirección es el espacio de direcciones en el proceso que ocupa el mapeo.
El campo de permisos es un conjunto de permisos:
r = read
w = write
x = execute
s = shared
p = private (copy on write)
El campo de desplazamiento es el desplazamiento en el archivo/lo que sea;
dev es el dispositivo (mayor:menor);
inodo es el inodo en ese dispositivo. 0 indica que no hay ningún inodo asociado con la región de memoria, como sería el caso con BSS (datos no inicializados).
El campo de nombre de ruta normalmente será el archivo que respalda la asignación. Para archivos ELF, puede coordinar fácilmente con el campo de compensación mirando el campo Compensación en los encabezados del programa ELF (readelf -l).
En Linux 2.0, no hay ningún campo que indique el nombre de la ruta.
El mapeo de memoria no solo se usa para mapear archivos en la memoria, sino que también es una herramienta para solicitar RAM del kernel. Estas son esas entradas del inodo 0: su pila, montón, segmentos bss y más
Aunque la pregunta menciona específicamente los sistemas integrados, el título solo menciona proc/<pid>/maps
, lo cual también es muy útil para comprender los programas "normales". En este contexto más amplio, es importante darse cuenta de que la memoria asignada por malloc()
puede terminar en el montón o en cualquier número de segmentos de memoria anónimos. Por lo tanto, es probable que grandes bloques de memoria anónima provengan de malloc()
.
Lo que /proc/<pid>/maps
se refiere a [heap]
es más precisamente una región contigua entre la memoria asignada para variables estáticas (llamada segmento BSS ) y una dirección llamada "interrupción del programa" (consulte el diagrama a continuación). Inicialmente, esta región está vacía y no hay ningún montón. Cuando malloc()
se llama, puede crear/expandir el montón pidiéndole al núcleo (a través de la brk()
llamada al sistema) que mueva la pausa del programa. Del mismo modo, free()
puede reducir el montón si todas las direcciones adyacentes a la interrupción del programa ya no están en uso.
Sin embargo, mover la pausa del programa no es la única manera de malloc()
ganar más espacio. También puede pedirle al kernel, a través de la mmap()
llamada al sistema, que reserve un bloque de espacio en algún lugar entre la pila y el montón (consulte el diagrama a continuación). La memoria asignada de esta manera aparece /proc/<pid>/maps
como las "entradas de inodo anónimo 0" mencionadas en la pregunta.
Credito de imagen
Vale la pena profundizar mmap()
un poco en la llamada al sistema. Hay cuatro tipos de mapas de memoria que mmap()
se pueden crear y cada uno de ellos se utiliza para propósitos muy diferentes. En primer lugar, la memoria puede estar vinculada al contenido de un determinado archivo o no. Este último se denomina mapa "anónimo". En segundo lugar, la memoria puede ser "privada" o "compartida". Privado significa que los cambios realizados por un proceso no serán visibles para ningún otro; Esto generalmente se implementa de una manera lenta y eficiente llamada " copia en escritura ". Compartido significa que cada proceso tendrá acceso a la misma memoria física subyacente. A continuación se detallan los usos que conozco para cada tipo de mapa de memoria:
- Archivos privados: ejecutables, bibliotecas dinámicas, copias eficientes de grandes estructuras de datos.
- Mapas anónimos privados:
malloc()
segmentos BSS para bibliotecas dinámicas, espacio de pila para subprocesos - Archivos compartidos: compartir memoria entre procesos no relacionados
- Mapas anónimos compartidos: compartir memoria entre procesos relacionados
Volviendo a /proc/<pid>/maps
, puede determinar qué tipo de mapa de memoria describe cada línea mirando las columnas "nombre de ruta" y "permisos". (Estos nombres de columnas provienen de los documentos del kernel ). Para asignaciones de archivos, la columna "nombre de ruta" contendrá una ruta real al archivo que se está asignando. Para mapas anónimos, la columna "nombre de ruta" estará vacía. También hay algunos nombres de rutas especiales como [heap]
y [stack]
. Para mapas privados y compartidos, la columna "permanentes" incluirá la bandera p
o s
, respectivamente.
Implementaciones actuales de malloc()
uso brk()
para asignaciones pequeñas y mmap()
para grandes. Tiene sentido asignar pequeñas cantidades de memoria en el montón, porque muy a menudo es posible encontrar el espacio necesario sin tener que realizar una costosa llamada al sistema (por ejemplo, reutilizando el espacio previamente liberado). Sin embargo, las asignaciones grandes corren el riesgo de no volver a devolverse al sistema operativo. Considere lo que sucedería si hiciera una asignación grande en el montón seguida de varias asignaciones pequeñas. Incluso si una vez liberada la asignación grande, la interrupción del programa no podría retroceder hasta que también se liberaran todas las asignaciones pequeñas. Este ejemplo simple supone que las asignaciones van al montón en orden, lo cual es un enfoque ingenuo, pero ilustra cómo el montón hace que sea mucho más difícil liberar memoria para el sistema operativo.
Aquí está la sección relevante de man malloc
:
Normalmente, malloc() asigna memoria del montón y ajusta el tamaño del montón según sea necesario, usando sbrk(2). Al asignar bloques de memoria mayores que MMAP_THRESHOLD bytes, la implementación de glibc malloc() asigna la memoria como un mapeo anónimo privado usando mmap(2). MMAP_THRESHOLD es de 128 kB de forma predeterminada, pero se puede ajustar usando mallopt(3). Antes de Linux 4.7, las asignaciones realizadas con mmap(2) no se veían afectadas por el límite de recursos RLIMIT_DATA; Desde Linux 4.7, este límite también se aplica a las asignaciones realizadas mediante mmap(2).
En resumen, si su programa utiliza malloc()
, malloc()
es probable que sea responsable de muchos de los segmentos grandes y anónimos que se asignan a la memoria virtual y son reportados por /proc/<pid>/maps
.
Advertencia: prácticamente todo lo que escribí aquí lo aprendí hoy, así que tómalo con cautela. Dicho esto, aquí hay enlaces a recursos que encontré muy útiles para comprender todo esto:
- Excelente introducción a la memoria virtual.
- Documentación del kernel para /proc/<pid>/maps
- Este es el enlace a la documentación de todo el
/proc
sistema de archivos y, desafortunadamente, no creo que haya una manera de vincularlo directamente a la parte relevante. Pero si busca "/proc/PID/maps", debería poder encontrar el lugar correcto.
- Este es el enlace a la documentación de todo el
- Definición exacta del montón.
- ¿Por qué las llamadas no
malloc()
crean un montón? - Méritos de
brk()
vsmmap()
- como
malloc()
yfree()
trabajar