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 la biblioteca compartida no requiere que comprenda exactamente cómo funciona una MMU, solo que una MMU permite que haya una diferencia entre las direcciones de memoria lógica (las utilizadas por los programas) y las físicas.direcciones de memoria (las que realmente están presentes en el bus de memoria). La memoria se divide en páginas, generalmente de 4K en Linux. Con 4k páginas, 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 las 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 dada puede corresponder a múltiples 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 MMU, de modo que cada proceso tiene su propio espacio. La dirección 4096 en el proceso 1000 puede ser (y generalmente 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 construir 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 realmente, 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.):
- Podría codificar alguna dirección lógica para llamarlo. Esto requiere que la biblioteca siempre se cargue en la misma dirección lógica exacta. 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 esto básicamente requiere que cada biblioteca del 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 la vinculación previa, más o menos).
- Podría codificar una dirección lógica falsa y decirle al vinculador dinámico que edite la correcta al cargar la biblioteca. Esto cuesta bastante tiempo al cargar las bibliotecas, pero después de eso es muy rápido.
- Podría agregar una capa de indirección: 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.
Prácticamente ya nadie usa el # 1, al menos no en sistemas de uso general. Mantener esa lista de direcciones lógicas únicas es imposible en los sistemas de 32 bits (no hay suficiente para todos) y una pesadilla administrativa en los sistemas de 64 bits. Sin embargo, la pre-vinculación hace esto por sistema.
El uso de # 2 o # 3 depende de si la biblioteca se creó con la -fPIC
opción de GCC (código independiente de posición). # 2 es sin, # 3 es con. En general, las bibliotecas se construyen con -fPIC
, por lo que # 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:
- Si la biblioteca está construida con
-fPIC
(como seguramente debería ser), la gran mayoría de las páginas son exactamente iguales para cada proceso que la carga. Sus procesos a
y b
bien pueden cargar la biblioteca en diferentes direcciones lógicas, pero esas apuntarán a las mismas páginas físicas: se compartirá la memoria. Además, los datos en la RAM coinciden exactamente con lo que hay en el disco, por lo que solo se puede cargar cuando sea necesario por el controlador de fallas de página.
- Si la biblioteca se construye sin
-fPIC
ella, 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 son compartidos. Las páginas no coinciden con lo que hay en el disco, por lo que no me sorprendería que se cargara toda la biblioteca. Por supuesto, posteriormente se puede cambiar al disco (en el archivo de intercambio).
Puede examinar esto con la pmap
herramienta, o directamente marcando varios archivos /proc
. Por ejemplo, aquí hay una salida (parcial) de pmap -x
dos bc
s recién generados diferentes . Tenga en cuenta que las direcciones que muestra pmap son, como típicas, 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 se carga en varias partes y pmap -x
le brinda detalles sobre cada una por separado. Notarás que las direcciones lógicas son diferentes entre los dos procesos; razonablemente esperaría que fueran iguales (ya que se está ejecutando el mismo programa y las computadoras generalmente son predecibles así), pero hay una característica de seguridad llamada aleatorización del diseño del espacio de direcciones que los aleatoriza intencionalmente.
Por la diferencia de tamaño (Kbytes) y tamaño de residente (RSS), puede ver 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 está en el disco.
Puede volver a ejecutarlo 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
176, que coincide exactamente con el RSS
. Shared
memoria significa que las páginas físicas se comparten entre múltiples procesos, y dado que coincide con el RSS, eso significa que toda la biblioteca que está en la memoria se comparte (consulte la sección Ver también a continuación para obtener una explicación más detallada de lo compartido frente a lo privado):
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
-fPIC
uso ha cambiado por completo hace algún tiempo)?