GNU/Linux >> Tutoriales Linux >  >> Linux

¿Carga de bibliotecas compartidas y uso de RAM?

Me pregunto sobre la forma en que Linux administra las bibliotecas compartidas. (en realidad, estoy hablando de Maemo Fremantle, una distribución basada en Debian lanzada en 2009 que se ejecuta en 256 MB de RAM).

Supongamos que tenemos dos ejecutables que se vinculan a libQtCore.so.4 y usan sus símbolos (usando sus clases y funciones). Por simplicidad, llamémoslos a y b . Suponemos que ambos ejecutables enlazan con las mismas bibliotecas.

Primero lanzamos a . La biblioteca tiene que ser cargada. ¿Se carga en su totalidad o se carga en la memoria solo en la parte que se requiere (como no usamos cada clase, solo se carga el código relacionado con las clases usadas)?

Luego lanzamos b . Suponemos que a todavía está funcionando. b también enlaza con libQtCore.so.4 y usa algunas de las clases que a usa, pero también algunos que no son usados ​​por a . ¿Se cargará dos veces la biblioteca (por separado para a y por separado para b )? ¿O usarán el mismo objeto que ya está en la RAM? Si b no usa nuevos símbolos y a ya se está ejecutando ¿aumentará la memoria RAM utilizada por las bibliotecas compartidas? (¿O la diferencia será insignificante)

Respuesta aceptada:

NOTA:Voy a suponer que su máquina tiene una unidad de mapeo de memoria (MMU). Hay una versión de Linux (µClinux) que no requiere una MMU y esta respuesta no se aplica allí.

¿Qué es una MMU? Es hardware, parte del procesador y/o controlador de memoria. Comprender la vinculación de bibliotecas compartidas no requiere que comprenda exactamente cómo funciona una MMU, solo que una MMU permite que haya una diferencia entre lógica direcciones de memoria (las que usan los programas) y físicas direcciones de memoria (las que realmente están presentes en el bus de memoria). La memoria se divide en páginas, normalmente de 4K de tamaño en Linux. Con páginas de 4k, las direcciones lógicas 0–4095 son la página 0, las direcciones lógicas 4096–8191 son la página 1, etc. La MMU las asigna a páginas físicas de RAM, y cada página lógica se puede asignar típicamente a 0 o 1 páginas físicas. Una página física determinada puede corresponder a varias páginas lógicas (así es como se comparte la memoria:varias páginas lógicas corresponden a la misma página física). Tenga en cuenta que esto se aplica independientemente del sistema operativo; es una descripción del hardware.

En el cambio de proceso, el kernel cambia las asignaciones de página de MMU, de modo que cada proceso tenga su propio espacio. La dirección 4096 en el proceso 1000 puede ser (y normalmente lo es) completamente diferente de la dirección 4096 en el proceso 1001.

Casi siempre que ves una dirección, es una dirección lógica. Los programas de espacio de usuario casi nunca tratan con direcciones físicas.

Ahora, también hay varias formas de crear bibliotecas. Digamos que un programa llama a la función foo() en la biblioteca. La CPU no sabe nada sobre símbolos o llamadas a funciones en realidad, solo sabe cómo saltar a una dirección lógica y ejecutar cualquier código que encuentre allí. Hay un par de formas en que podría hacer esto (y cosas similares se aplican cuando una biblioteca accede a sus propios datos globales, etc.):

  1. Podría codificar alguna dirección lógica para llamarlo. Esto requiere que la biblioteca siempre cargarse exactamente en la misma dirección lógica. Si dos bibliotecas requieren la misma dirección, la vinculación dinámica falla y no puede iniciar el programa. Las bibliotecas pueden requerir otras bibliotecas, por lo que básicamente requiere que cada biblioteca en el sistema tenga direcciones lógicas únicas. Sin embargo, es muy rápido si funciona. (Así es como a.out hizo las cosas, y el tipo de configuración que hace el preenlace, más o menos).
  2. Podría codificar una dirección lógica falsa y decirle al enlazador dinámico que edite en la correcta al cargar la biblioteca. Esto cuesta bastante tiempo al cargar las bibliotecas, pero después de eso es muy rápido.
  3. Podría agregar una capa de direccionamiento indirecto:use un registro de CPU para mantener la dirección lógica en la que se carga la biblioteca y luego acceda a todo como un desplazamiento desde ese registro. Esto impone un costo de rendimiento en cada acceso.
Relacionado:Linux:¿cp pierde los metadatos del archivo?

Prácticamente ya nadie usa el #1, al menos no en sistemas de propósito general. Mantener esa lista de direcciones lógicas únicas es imposible en los sistemas de 32 bits (no hay suficientes para todos) y una pesadilla administrativa en los sistemas de 64 bits. Sin embargo, la vinculación previa hace esto por sistema.

Si se usa #2 o #3 depende de si la biblioteca se creó con -fPIC de GCC (posición de código independiente) opción. #2 es sin, #3 es con. Generalmente, las bibliotecas se construyen con -fPIC , entonces #3 es lo que sucede.

Para obtener más detalles, consulte Cómo escribir bibliotecas compartidas (PDF) de Ulrich Drepper.

Entonces, finalmente, su pregunta puede ser respondida:

  1. Si la biblioteca está construida con -fPIC (como es casi seguro que debería ser), la gran mayoría de las páginas son exactamente iguales para cada proceso que las carga. Tus procesos a y b bien puede cargar la biblioteca en diferentes direcciones lógicas, pero esas apuntarán a las mismas páginas físicas:la memoria será compartida. Además, los datos en la RAM coinciden exactamente con los que hay en el disco, por lo que solo se pueden cargar cuando el controlador de errores de página los necesite.
  2. Si la biblioteca está construida sin -fPIC , entonces resulta que la mayoría de las páginas de la biblioteca necesitarán ediciones de enlaces y serán diferentes. Por lo tanto, deben ser páginas físicas separadas (ya que contienen datos diferentes). Eso significa que no se comparten. Las páginas no coinciden con lo que hay en el disco, por lo que no me sorprendería si se carga toda la biblioteca. Por supuesto, se puede cambiar posteriormente al disco (en el archivo de intercambio).

Puede examinar esto con el pmap herramienta, o directamente revisando varios archivos en /proc . Por ejemplo, aquí hay una salida (parcial) de pmap -x en dos bc recién generados diferentes s. Tenga en cuenta que las direcciones que muestra pmap son, por lo general, direcciones lógicas:

pmap -x 14739
Address           Kbytes     RSS   Dirty Mode  Mapping
00007f81803ac000     244     176       0 r-x-- libreadline.so.6.2
00007f81803e9000    2048       0       0 ----- libreadline.so.6.2
00007f81805e9000       8       8       8 r---- libreadline.so.6.2
00007f81805eb000      24      24      24 rw--- libreadline.so.6.2


pmap -x 17739
Address           Kbytes     RSS   Dirty Mode  Mapping
00007f784dc77000     244     176       0 r-x-- libreadline.so.6.2
00007f784dcb4000    2048       0       0 ----- libreadline.so.6.2
00007f784deb4000       8       8       8 r---- libreadline.so.6.2
00007f784deb6000      24      24      24 rw--- libreadline.so.6.2

Puede ver que la biblioteca está cargada en varias partes y pmap -x le da detalles sobre cada uno por separado. Notará que las direcciones lógicas son diferentes entre los dos procesos; es razonable esperar que sean iguales (ya que se ejecuta el mismo programa y las computadoras suelen ser predecibles de esa manera), pero hay una característica de seguridad llamada aleatorización del diseño del espacio de direcciones que los aleatoriza intencionalmente.

Puede ver por la diferencia de tamaño (Kbytes) y tamaño residente (RSS) que no se ha cargado todo el segmento de la biblioteca. Finalmente, puede ver que para las asignaciones más grandes, sucio es 0, lo que significa que corresponde exactamente a lo que hay en el disco.

Puede volver a ejecutar con pmap -XX , y le mostrará, dependiendo de la versión del kernel que esté ejecutando, ya que la salida -XX varía según la versión del kernel, que la primera asignación tiene un Shared_Clean de 176, que coincide exactamente con el RSS . Shared memoria significa que las páginas físicas se comparten entre varios procesos, y dado que coincide con el RSS, eso significa que se comparte toda la biblioteca que está en la memoria (consulte Vea también a continuación para obtener una explicación más detallada de compartida frente a privada):

pmap -XX 17739
         Address Perm   Offset Device   Inode  Size  Rss Pss Shared_Clean Shared_Dirty Private_Clean Private_Dirty Referenced Anonymous AnonHugePages Swap KernelPageSize MMUPageSize Locked                   VmFlagsMapping
    7f784dc77000 r-xp 00000000  fd:00 1837043   244  176  19          176            0             0             0        176         0             0    0              4           4      0       rd ex mr mw me sd  libreadline.so.6.2
    7f784dcb4000 ---p 0003d000  fd:00 1837043  2048    0   0            0            0             0             0          0         0             0    0              4           4      0             mr mw me sd  libreadline.so.6.2
    7f784deb4000 r--p 0003d000  fd:00 1837043     8    8   8            0            0             0             8          8         8             0    0              4           4      0       rd mr mw me ac sd  libreadline.so.6.2
    7f784deb6000 rw-p 0003f000  fd:00 1837043    24   24  24            0            0             0            24         24        24             0    0              4           4      0    rd wr mr mw me ac sd  libreadline.so.6.2

Ver también

  • Obtener información sobre el uso de memoria de un proceso de /proc/pid/smaps para obtener una explicación de todo el asunto limpio/sucio compartido/privado.
Relacionado:Linux:¿cómo hacer que se muestre sar para el día anterior?
Linux
  1. Error al cargar bibliotecas compartidas libcrypto.so.1.1 – OpenSSL [Fijar]

  2. ¿Medir el uso de RAM de un programa?

  3. Cómo verificar el tamaño total de RAM y el uso de memoria en Linux

  4. Cómo inicializar una biblioteca compartida en Linux

  5. ¿Cómo incrustar la información de la versión en la biblioteca compartida y el binario?

Biblioteca compartida de Jenkins:cómo crear, configurar y usar

Controle el uso de RAM y CPU por Kodi en tiempo real

Error al cargar la biblioteca compartida (glow)

Ubuntu reconoce el ejecutable como biblioteca compartida y no lo ejecutará haciendo clic en

conda.exe:error al cargar bibliotecas compartidas:libz.so.1

¿Cómo verificar si una biblioteca compartida está instalada?