El fork()
y vfork()
Los envoltorios en glibc se implementan a través de clone()
llamada del sistema. Para comprender mejor la relación entre fork()
y clone()
, debemos considerar la relación entre procesos e hilos en Linux.
Tradicionalmente, fork()
duplicaría todos los recursos propiedad del proceso padre y asignaría la copia al proceso hijo. Este enfoque incurre en una sobrecarga considerable, que podría ser en vano si el niño llama inmediatamente a exec()
. En Linux, fork()
utiliza copia en escritura páginas para retrasar o evitar por completo la copia de los datos que se pueden compartir entre los procesos principal y secundario. Por lo tanto, la única sobrecarga en la que se incurre durante un fork()
normal es la copia de las tablas de páginas de los padres y la asignación de una estructura de descriptor de proceso única, task_struct
, para el niño.
Linux también adopta un enfoque excepcional para los subprocesos. En Linux, los hilos son simplemente procesos ordinarios que comparten algunos recursos con otros procesos. Este es un enfoque radicalmente diferente de los subprocesos en comparación con otros sistemas operativos como Windows o Solaris, donde los procesos y los subprocesos son tipos de bestias completamente diferentes. En Linux, cada subproceso tiene un task_struct
ordinario por sí mismo que simplemente está configurado de tal manera que comparte ciertos recursos, como un espacio de direcciones, con el proceso principal.
El flags
parámetro del clone()
La llamada al sistema incluye un conjunto de banderas que indican qué recursos, si los hay, deben compartir los procesos padre e hijo. Los procesos y los subprocesos se crean a través de clone()
, la única diferencia es el conjunto de indicadores que se pasa a clone()
.
Un fork()
normal podría implementarse como:
clone(SIGCHLD, 0);
Esto crea una tarea que no comparte ningún recurso con su principal y está configurada para enviar el SIGCHLD
señal de terminación al padre cuando sale.
Por el contrario, una tarea que comparte el espacio de direcciones, los recursos del sistema de archivos, los descriptores de archivos y los controladores de señales con el padre, en otras palabras, un hilo , podría crearse con:
clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);
vfork()
a su vez se implementa a través de un CLONE_VFORK
separado flag, que hará que el proceso padre entre en suspensión hasta que el proceso hijo lo despierte a través de una señal. El hijo será el único hilo de ejecución en el espacio de nombres del padre, hasta que llame a exec()
o salidas. El niño no puede escribir en la memoria. El clone()
correspondiente la llamada podría ser la siguiente:
clone(CLONE_VFORK | CLONE_VM | SIGCHLD, 0)
La implementación de sys_clone()
es específico de la arquitectura, pero la mayor parte del trabajo ocurre en do_fork()
definido en kernel/fork.c
. Esta función llama al clone_process()
estático , que crea un nuevo proceso como una copia del padre, pero aún no lo inicia. clone_process()
copia los registros, asigna un PID a la nueva tarea y duplica o comparte partes apropiadas del entorno del proceso según lo especificado por el clon flags
. Cuando clone_process()
devuelve, do_clone()
activará el proceso recién creado y lo programará para que se ejecute.
El componente responsable de traducir las funciones de llamadas al sistema del usuario a llamadas al sistema del kernel bajo Linux es libc. En GLibC, la biblioteca NPTL redirige esto a clone(2)
llamada al sistema.