¿Fork () copia inmediatamente todo el montón de procesos en Linux?

30

Una fork()llamada al sistema clona un proceso hijo del proceso en ejecución. Los dos procesos son idénticos excepto por su PID.

Naturalmente, si los procesos solo leen de sus montones en lugar de escribirles, copiar el montón sería un gran desperdicio de memoria.

¿Se ha copiado todo el montón del proceso? ¿Está optimizado de tal manera que solo la escritura desencadena una copia del montón?

Adam Matan
fuente

Respuestas:

19

La totalidad de fork()se implementa utilizando mmap / copy en escritura.

Esto no solo afecta el montón, sino también las bibliotecas compartidas, la pila, las áreas BSS.

Lo cual, por cierto, significa que fork es una operación extremadamente ligera, hasta que los 2 procesos resultantes (padre e hijo) realmente comiencen a escribir en los rangos de memoria. Esta característica es un contribuyente importante a la letalidad de las bombas de horquilla: terminas con demasiados procesos antes de que el núcleo se sobrecargue con la replicación y diferenciación de la página.

Será difícil encontrar en un sistema operativo moderno un ejemplo de una operación en la que el núcleo realice una copia impresa (los controladores de dispositivo son la excepción): es mucho, mucho más fácil y más eficiente emplear la funcionalidad de VM.

Even execve()es esencialmente "por favor mmap el binario / ld.so / whatnot, seguido de ejecutar" - y la VM maneja la carga real del proceso a la RAM y la ejecución. Las variables locales no inicializadas terminan siendo mapeadas desde una 'página cero' - página especial de solo lectura de copia-escritura que contiene ceros, las variables locales inicializadas terminan siendo mapeadas (copia-sobre-escritura, nuevamente) del archivo binario mismo, etc.

qdot
fuente
Una excepción notable son los procesos Java. Busque "fork java memory" y encontrará docenas de problemas que afectan a JVM de servidores grandes o JVM incrustados que intentan ejecutar un comando de shell pequeño y se estrellan miserablemente en una excepción de "No se puede asignar memoria" (estos son solo enlaces aleatorios, este problema es sistémico a los entornos Java). Esta respuesta SO acusa al recolector de basura de JVM y al compilador JIT de evitar que la memoria de procesos se comparta.
WhiteWinterWolf
24

El kernel de Linux implementa Copy-on-Write cuando fork()se llama. Cuando se ejecuta la llamada al sistema, las páginas que comparten el padre y el hijo se marcan como de solo lectura.

Si se realiza una escritura en la página de solo lectura, se copia, ya que la memoria ya no es idéntica entre los dos procesos. Por lo tanto, si solo se realizan operaciones de lectura, las páginas no se copiarán en absoluto.

mmk
fuente
1
+1 ¡Gracias! 1. ¿Podría por favor proporcionar enlaces de referencia? 2. ¿Se copia el montón por completo o en partes?
Adam Matan
44
2. - En páginas :) El kernel tiene muy poca comprensión de lo que es el "montón": para el kernel, son solo un montón de páginas privadas mmapped, que los asignadores de libc manejan a su gusto.
qdot
¿Es esto realmente una bomba tenedor necesariamente? Me parece que, en lugar de bifurcar el proceso actual, este código creará más instancias del mismo programa que se ejecutan desde el inicio en lugar de desde la siguiente instrucción después de la fork()llamada.
sherrellbc
@mmk FYI, me sorprendió bastante su "Nota al margen interesante:" y entonces probé (en Linux 3.2.0) para ver, y no parece ser cierto. Solía /proc/self/pagemapdeterminar el mapeo de la dirección virtual a la página física para el propósito de la prueba. Como esperaba, si el nieto y solo el nieto escriben la página compartida, el padre y el hijo original continúan compartiéndolo. Solo el nieto termina con una copia privada.
Celada
@Celada. Hmm Había leído esto en alguna parte, y no recuerdo la versión del núcleo a la que se refería (¿probablemente una más antigua?), Por lo tanto, puede que ya no sea válida.
mmk
10

Linux hace copia en escritura. A medida que se forkcrea un nuevo proceso, las páginas asignadas se marcan como de solo lectura y se comparten entre el padre y el hijo. Cuando cualquiera de ellos intenta modificar una página, se genera un error de página que resulta en copiar la página y ajustar la tabla de páginas adecuadamente.

unxnut
fuente