En informática, un proceso padre es un proceso que ha creado uno o más procesos hijos .
En los sistemas operativos tipo Unix , cada proceso excepto el proceso 0 (el intercambiador) se crea cuando otro proceso ejecuta la llamada al sistema fork() . El proceso que invocó fork es el proceso padre y el proceso recién creado es el proceso hijo . Cada proceso (excepto el proceso 0) tiene un proceso padre, pero puede tener muchos procesos hijos.
El núcleo del sistema operativo identifica cada proceso por su identificador de proceso. El proceso 0 es un proceso especial que se crea cuando se inicia el sistema; después de bifurcar un proceso secundario (proceso 1), el proceso 0 se convierte en el proceso intercambiador (a veces también conocido como la " tarea inactiva "). El proceso 1 , conocido como init , es el antecesor de todos los demás procesos del sistema. [1]
En el núcleo de Linux , en el que hay una diferencia muy pequeña entre procesos e hilos POSIX , hay dos tipos de procesos padre, es decir, padre real y padre. El padre es el proceso que recibe la señal SIGCHLD al finalizar el proceso hijo, mientras que el padre real es el hilo que realmente creó este proceso hijo en un entorno multihilo. Para un proceso normal, estos dos valores son iguales, pero para un hilo POSIX que actúa como un proceso, estos dos valores pueden ser diferentes. [2]
El sistema operativo mantiene una tabla que asocia cada proceso, mediante su identificador de proceso (generalmente denominado " pid "), a los datos necesarios para su funcionamiento. Durante la vida de un proceso, dichos datos pueden incluir segmentos de memoria designados para el proceso, los argumentos con los que se lo ha invocado, variables de entorno , contadores sobre el uso de recursos, identificadores de usuario, identificadores de grupo y conjuntos de grupos, y tal vez otros tipos de información.
Cuando un proceso termina su ejecución, ya sea llamando a exit (aunque sea de manera implícita, al ejecutar un comando de retorno desde la función principal ) o al recibir una señal que hace que termine abruptamente, el sistema operativo libera la mayoría de los recursos y la información relacionada con ese proceso, pero aún conserva los datos sobre la utilización de recursos y el código de estado de terminación , porque un proceso padre podría estar interesado en saber si ese hijo se ejecutó exitosamente (usando funciones estándar para decodificar el código de estado de terminación) y la cantidad de recursos del sistema que consumió durante su ejecución.
De manera predeterminada, el sistema asume que el proceso padre está realmente interesado en dicha información en el momento de la terminación del hijo, y por lo tanto envía al padre la señal SIGCHLD para alertar que hay algunos datos sobre un hijo que deben recopilarse. Dicha recopilación se realiza llamando a una función de la familia wait (ya sea wait en sí o una de sus relacionadas, como waitpid , waitid o wait4 ). Tan pronto como se realiza esta recopilación, el sistema libera esos últimos bits de información sobre el proceso hijo y elimina su pid de la tabla de procesos. Sin embargo, si el proceso padre demora en recopilar los datos del hijo (o no lo hace en absoluto), el sistema no tiene otra opción que mantener el pid y los datos de terminación del hijo en la tabla de procesos indefinidamente.
Un proceso terminado cuyos datos no han sido recopilados se denomina proceso zombi , o simplemente zombi , en la jerga de UNIX. El nombre es una analogía humorística que se debe a que se considera que un proceso terminado "ya no está vivo" o "está muerto" (ya que en realidad ha dejado de funcionar) y que es un proceso muerto persistente que todavía está "encarnado" en el "mundo de los procesos vivos" (la tabla de procesos), que, por lo tanto, en realidad está "no muerto" o "zombi".
Los procesos zombies pueden plantear problemas en sistemas con recursos limitados o que tienen tablas de procesos de tamaño limitado, ya que la creación de nuevos procesos activos puede verse impedida por la falta de recursos que aún utilizan los zombies de larga duración.
Por lo tanto, es una buena práctica de programación en cualquier programa que pueda generar procesos secundarios tener código para evitar la formación de zombis duraderos a partir de sus procesos secundarios originales. El enfoque más obvio es tener código que llame a wait o uno de sus parientes en algún lugar después de haber creado un nuevo proceso. Si se espera que el programa cree muchos procesos secundarios que pueden ejecutarse de forma asincrónica y terminar en un orden impredecible, generalmente es bueno crear un controlador para la señal SIGCHLD , llamando a una de las funciones de la familia wait en un bucle, hasta que no queden datos secundarios sin recopilar. Es posible que el proceso padre ignore por completo la terminación de sus hijos y aún así no cree zombis, pero esto requiere la definición explícita de un controlador para SIGCHLD a través de una llamada a sigaction con el indicador de opción especial SA_NOCLDWAIT . [3]
Los procesos huérfanos son una situación opuesta a los procesos zombi, y se refieren al caso en el que un proceso padre termina antes que sus procesos hijos, que se dice que quedan "huérfanos". A diferencia de la notificación asincrónica de hijo a padre que ocurre cuando un proceso hijo termina (a través de la señal SIGCHLD ), los procesos hijos no son notificados inmediatamente cuando su padre termina. En cambio, el sistema simplemente redefine el campo "PID padre" en los datos del proceso hijo para que sea el proceso que es el "antecesor" de todos los demás procesos del sistema, cuyo PID generalmente tiene el valor de 1 (uno), y cuyo nombre es tradicionalmente "init" (excepto en el kernel Linux 3.4 y superiores [más información a continuación]). Por lo tanto, se dijo que init "adopta" todos los procesos huérfanos del sistema. [4] [5]
Una suposición bastante común entre los programadores nuevos en UNIX era que los procesos secundarios de un proceso que termina serían adoptados por el proceso padre inmediato de este proceso (de ahí el nombre de "abuelo" de esos procesos secundarios). Tal suposición era incorrecta, a menos, por supuesto, que ese "abuelo" fuera el propio init.
Después del kernel 3.4 de Linux esto ya no es así; de hecho, los procesos pueden ejecutar la llamada al sistema prctl() con la opción PR_SET_CHILD_SUBREAPER y, como resultado, ellos, no el proceso n.° 1, se convertirán en el padre de cualquiera de sus procesos descendientes huérfanos. Esta es la forma de trabajar de los administradores de servicios modernos y las utilidades de supervisión de demonios, incluidos systemd, upstart y el administrador de servicios nosh.
Este es un resumen de la página del manual, que informa que:
Un subreaper cumple el rol de init(1) para sus procesos descendientes. Cuando un proceso se vuelve huérfano (es decir, su padre inmediato termina), entonces ese proceso será re-parentalizado con el subreaper ancestro más cercano que aún esté vivo. Posteriormente, las llamadas a getppid() en el proceso huérfano devolverán ahora el PID del proceso subreaper, y cuando el huérfano termina, es el proceso subreaper el que recibirá una señal SIGCHLD y podrá esperar(2) al proceso para descubrir su estado de terminación. [6]