superar ser querido que pérdida poema papa niños niño murio muerte muere madre los joven hijo explicar duelo digo cómo cuando como años ayudar afrontar afecta c linux unix process fork

ser - ¿Cómo hacer que el proceso hijo muera después de que el padre sale?



duelo en niños de 3 a 6 años (23)

¿El proceso secundario tiene un conducto hacia / desde el proceso principal? Si es así, recibirías un SIGPIPE si escribes, u obtienes EOF al leer; estas condiciones podrían detectarse.

Supongamos que tengo un proceso que engendra exactamente un proceso secundario. Ahora, cuando el proceso principal se cierra por cualquier razón (normalmente o de forma anómala, mediante kill, ^ C, assert failure o cualquier otra cosa), quiero que el proceso hijo muera. ¿Cómo hacer eso correctamente?

Algunas preguntas similares sobre stackoverflow:

Algunas preguntas similares sobre stackoverflow para Windows :


Algunos carteles ya han mencionado pipes y kqueue . De hecho, también puede crear un par de sockets de dominio Unix conectados mediante la llamada socketpair() . El tipo de socket debe ser SOCK_STREAM .

Supongamos que tiene los dos descriptores de archivos socket fd1, fd2. Ahora fork() para crear el proceso hijo, que heredará los fds. En el padre cierra fd2 y en el hijo cierra fd1. Ahora cada proceso puede poll() el fd abierto restante en su propio extremo para el evento POLLIN . Siempre que cada lado no close() explícitamente close() su fd durante el tiempo de vida normal, puede estar bastante seguro de que un indicador POLLHUP debe indicar la terminación del otro (no importa si está limpio o no). Al ser notificado de este evento, el niño puede decidir qué hacer (por ejemplo, morir).

#include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/socket.h> #include <poll.h> #include <stdio.h> int main(int argc, char ** argv) { int sv[2]; /* sv[0] for parent, sv[1] for child */ socketpair(AF_UNIX, SOCK_STREAM, 0, sv); pid_t pid = fork(); if ( pid > 0 ) { /* parent */ close(sv[1]); fprintf(stderr, "parent: pid = %d/n", getpid()); sleep(100); exit(0); } else { /* child */ close(sv[0]); fprintf(stderr, "child: pid = %d/n", getpid()); struct pollfd mon; mon.fd = sv[1]; mon.events = POLLIN; poll(&mon, 1, -1); if ( mon.revents & POLLHUP ) fprintf(stderr, "child: parent hung up/n"); exit(0); } }

Puede intentar compilar el código de prueba de concepto anterior y ejecutarlo en un terminal como ./a.out & . Tienes aproximadamente 100 segundos para experimentar con la eliminación del PID padre por varias señales, o simplemente saldrá. En cualquier caso, debería ver el mensaje "hijo: padre colgado".

En comparación con el método que utiliza el controlador SIGPIPE , este método no requiere probar la llamada write() .

Este método también es simétrico , es decir, los procesos pueden usar el mismo canal para monitorear la existencia de los demás.

Esta solución solo llama a las funciones POSIX. Intenté esto en Linux y FreeBSD. Creo que debería funcionar en otros Unixes pero realmente no lo he probado.

Ver también:

  • unix(7) de las páginas man de Linux, unix(4) para FreeBSD, poll(2) , socketpair(2) , socket(7) en Linux.

Aunque pasaron 7 años, acabo de encontrarme con este problema porque estoy ejecutando la aplicación SpringBoot que necesita para iniciar el servidor webpack-dev-server durante el desarrollo y necesita eliminarlo cuando el proceso de back-end se detenga.

Intento usar Runtime.getRuntime().addShutdownHook pero funcionó en Windows 10 pero no en Windows 7.

Lo he cambiado para usar un hilo dedicado que espera que el proceso se cierre o para InterruptedException que parece funcionar correctamente en ambas versiones de Windows.

private void startWebpackDevServer() { String cmd = isWindows() ? "cmd /c gradlew webPackStart" : "gradlew webPackStart"; logger.info("webpack dev-server " + cmd); Thread thread = new Thread(() -> { ProcessBuilder pb = new ProcessBuilder(cmd.split(" ")); pb.redirectOutput(ProcessBuilder.Redirect.INHERIT); pb.redirectError(ProcessBuilder.Redirect.INHERIT); pb.directory(new File(".")); Process process = null; try { // Start the node process process = pb.start(); // Wait for the node process to quit (blocking) process.waitFor(); // Ensure the node process is killed process.destroyForcibly(); System.setProperty(WEBPACK_SERVER_PROPERTY, "true"); } catch (InterruptedException | IOException e) { // Ensure the node process is killed. // InterruptedException is thrown when the main process exit. logger.info("killing webpack dev-server", e); if (process != null) { process.destroyForcibly(); } } }); thread.start(); }


Como han señalado otras personas, confiar en el pid padre para convertirse en 1 cuando el padre sale no es portátil. En lugar de esperar una ID de proceso principal específica, solo espere a que cambie la ID:

pit_t pid = getpid(); switch (fork()) { case -1: { abort(); /* or whatever... */ } default: { /* parent */ exit(0); } case 0: { /* child */ /* ... */ } } /* Wait for parent to exit */ while (getppid() != pid) ;

Agregue una micro-suspensión si lo desea, si no desea sondear a toda velocidad.

Esta opción me parece más simple que usar un tubo o confiar en las señales.


Creo que una manera rápida y sucia es crear un conducto entre el niño y el padre. Cuando los padres salen, los niños recibirán un SIGPIPE.


En POSIX , las funciones exit() , _exit() y _Exit() se definen a:

  • Si el proceso es un proceso de control, la señal SIGHUP se enviará a cada proceso en el grupo de procesos en primer plano de la terminal de control que pertenece al proceso de llamada.

Por lo tanto, si organiza que el proceso principal sea un proceso de control para su grupo de procesos, el niño debería recibir una señal SIGHUP cuando el padre salga. No estoy absolutamente seguro de que eso suceda cuando el padre se cuelga, pero creo que sí. Ciertamente, para los casos que no son colisiones, debería funcionar bien.

Tenga en cuenta que es posible que tenga que leer una gran cantidad de letra pequeña, incluida la sección Definiciones de base (Definiciones), así como la información de Servicios del sistema para exit() y setsid() y setpgrp() - para obtener una imagen completa. (¡Yo también!)


En Linux, puede instalar una señal de muerte principal en el niño, por ejemplo:

#include <sys/prctl.h> #include <signal.h> // signals #include <unistd.h> // fork() #include <stdio.h> // perror() // ... pid_t ppid_before_fork = getpid(); pid_t pid = fork(); if (pid == -1) { perror(0); exit(1); } if (pid) { ; // continue parent execution } else { int r = prctl(PR_SET_PDEATHSIG, SIGTERM); if (r == -1) { perror(0); exit(1); } // test in case the original parent exited just // before the prctl() call if (getppid() != ppid_before_fork) exit(1); // continue child execution ...

Tenga en cuenta que almacenar el id. De proceso principal antes de la bifurcación y probarlo en el hijo después de prctl() elimina una condición de prctl() entre prctl() y la salida del proceso que llamó al niño.

También tenga en cuenta que la señal de muerte principal del niño se borra en sus propios hijos recién creados. No se ve afectado por un execve() .

Esa prueba se puede simplificar si estamos seguros de que el proceso del sistema que está a cargo de adoptar a todos los orphans tiene PID 1:

pid_t pid = fork(); if (pid == -1) { perror(0); exit(1); } if (pid) { ; // continue parent execution } else { int r = prctl(PR_SET_PDEATHSIG, SIGTERM); if (r == -1) { perror(0); exit(1); } // test in case the original parent exited just // before the prctl() call if (getppid() == 1) exit(1); // continue child execution ...

Sin embargo, confiar en que el proceso del sistema es init y tener PID 1 no es portátil. POSIX.1-2008 especifica :

El ID del proceso principal de todos los procesos secundarios existentes y los procesos zombies del proceso que realiza la llamada se establecerá en la ID del proceso de un proceso del sistema definido por la implementación. Es decir, estos procesos se heredarán mediante un proceso de sistema especial.

Tradicionalmente, el proceso del sistema que adopta a todos los huérfanos es PID 1, es decir, init, que es el ancestro de todos los procesos.

En sistemas modernos como prctl() o FreeBSD otro proceso podría tener ese rol. Por ejemplo, en Linux, un proceso puede llamar a prctl() para establecerse como un proceso de sistema que hereda todos los huérfanos de cualquiera de sus descendientes (ver example en Fedora 25).


En caso de que sea relevante para cualquier otra persona, cuando genero instancias de JVM en procesos secundarios bifurcados de C ++, la única manera en que podía hacer que las instancias de JVM finalizaran correctamente después de que el proceso principal se completara era hacer lo siguiente. Con suerte, alguien puede proporcionar comentarios en los comentarios si esta no era la mejor manera de hacerlo.

1) Llame a prctl(PR_SET_PDEATHSIG, SIGHUP) en el proceso hijo bifurcado como se sugiere antes de iniciar la aplicación Java a través de execv , y

2) Agregue un gancho de cierre a la aplicación Java que sondee hasta que su PID padre sea igual a 1, luego ejecute un Runtime.getRuntime().halt(0) . El sondeo se realiza al lanzar un shell separado que ejecuta el comando ps (Ver: ¿Cómo encuentro mi PID en Java o JRuby en Linux? ).

EDITAR 130118:

Parece que no fue una solución robusta. Todavía estoy luchando un poco para entender los matices de lo que está sucediendo, pero a veces aún me quedaban procesos de JVM huérfanos al ejecutar estas aplicaciones en sesiones de pantalla / SSH.

En lugar de sondear el PPID en la aplicación Java, simplemente hice que el gancho de cierre realizara la limpieza, seguido de un alto completo como el anterior. Luego me aseguré de invocar waitpid en la aplicación padre C ++ en el proceso hijo generado cuando llegó el momento de terminar todo. Esta parece ser una solución más robusta, ya que el proceso hijo asegura que finaliza, mientras que el padre usa las referencias existentes para asegurarse de que sus hijos finalicen. Compare esto con la solución anterior que hizo que el proceso principal finalizara cuando quisiera, e hizo que los niños trataran de averiguar si habían quedado huérfanos antes de terminar.


Encontré 2 soluciones, ambas no perfectas.

1. Mata a todos los niños con kill (-pid) cuando recibas la señal SIGTERM.
Obviamente, esta solución no puede manejar "kill -9", pero funciona en la mayoría de los casos y es muy simple porque no necesita recordar todos los procesos secundarios.

var childProc = require(''child_process'').spawn(''tail'', [''-f'', ''/dev/null''], {stdio:''ignore''}); var counter=0; setInterval(function(){ console.log(''c ''+(++counter)); },1000); if (process.platform.slice(0,3) != ''win'') { function killMeAndChildren() { /* * On Linux/Unix(Include Mac OS X), kill (-pid) will kill process group, usually * the process itself and children. * On Windows, an JOB object has been applied to current process and children, * so all children will be terminated if current process dies by anyway. */ console.log(''kill process group''); process.kill(-process.pid, ''SIGKILL''); } /* * When you use "kill pid_of_this_process", this callback will be called */ process.on(''SIGTERM'', function(err){ console.log(''SIGTERM''); killMeAndChildren(); }); }

De la misma manera, puede instalar el manejador ''exit'' como se muestra arriba si llama a process.exit en algún lugar. Nota: Ctrl + C y crash repentino han sido procesados ​​automáticamente por OS para matar el grupo de procesos, por lo que no hay más aquí.

2.Utilice chjj/pty.js para generar su proceso con la terminal controladora adjunta.
Cuando matas el proceso actual de todos modos, incluso kill -9, todos los procesos secundarios también se eliminarán automáticamente (¿por SO?). Supongo que porque el proceso actual tiene otro lado de la terminal, por lo que si el proceso actual muere, el proceso secundario tendrá SIGPIPE, por lo que muere.

var pty = require(''pty.js''); //var term = pty.spawn(''any_child_process'', [/*any arguments*/], { name: ''xterm-color'', cols: 80, rows: 30, cwd: process.cwd(), env: process.env }); /*optionally you can install data handler term.on(''data'', function(data) { process.stdout.write(data); }); term.write(.....); */


Esta solución funcionó para mí:

  • Pase la tubería stdin al niño; no es necesario que escriba datos en la transmisión.
  • El niño lee indefinidamente de stdin hasta EOF. Un EOF indica que el padre se ha ido.
  • Esta es una forma infalible y portátil de detectar cuando el padre se ha ido. Incluso si el padre falla, OS cerrará el conducto.

Esto fue para un proceso de tipo trabajador cuya existencia solo tenía sentido cuando el padre estaba vivo.


Estoy tratando de resolver el mismo problema, y ​​dado que mi programa debe ejecutarse en OS X, la solución solo de Linux no funcionó para mí.

Llegué a la misma conclusión que las otras personas en esta página: no existe una forma compatible con POSIX de notificar a un niño cuando un padre muere. Así que pensé en la siguiente mejor cosa: tener la encuesta infantil.

Cuando un proceso principal fallece (por algún motivo) el proceso principal del niño pasa a ser el proceso 1. Si el niño simplemente realiza sondeos periódicamente, puede verificar si su padre es 1. Si es así, el niño debe salir.

Esto no es genial, pero funciona, y es más fácil que las soluciones de sondeo TCP / lockfile sugeridas en otras partes de esta página.


He logrado esto en el pasado al ejecutar el código "original" en el "niño" y el código "generado" en el "padre" (es decir: invierte el sentido habitual de la prueba después del fork() ). Luego atrapa a SIGCHLD en el código "generado" ...

Puede que no sea posible en tu caso, pero lindo cuando funciona.


Inspirado por otra respuesta aquí, se me ocurrió la siguiente solución all-POSIX. La idea general es crear un proceso intermedio entre el padre y el hijo, que tiene un objetivo: observar cuándo el padre muere y matar explícitamente al hijo.

Este tipo de solución es útil cuando el código en el niño no se puede modificar.

int p[2]; pipe(p); pid_t child = fork(); if (child == 0) { close(p[1]); // close write end of pipe setpgid(0, 0); // prevent ^C in parent from stopping this process child = fork(); if (child == 0) { close(p[0]); // close read end of pipe (don''t need it here) exec(...child process here...); exit(1); } read(p[0], 1); // returns when parent exits for any reason kill(child, 9); exit(1); }

Hay dos pequeñas advertencias con este método:

  • Si matas deliberadamente el proceso intermedio, entonces el niño no morirá cuando el padre fallezca.
  • Si el hijo sale antes que el padre, entonces el proceso intermedio intentará matar el pid del niño original, que ahora podría referirse a un proceso diferente. (Esto podría solucionarse con más código en el proceso intermedio).

Como un aparte, el código real que estoy usando está en Python. Aquí está para completar:

def run(*args): (r, w) = os.pipe() child = os.fork() if child == 0: os.close(w) os.setpgid(0, 0) child = os.fork() if child == 0: os.close(r) os.execl(args[0], *args) os._exit(1) os.read(r, 1) os.kill(child, 9) os._exit(1) os.close(r)


Instale un manipulador de trampas para capturar SIGINT, que elimina el proceso de su hijo si todavía está vivo, aunque otros carteles son correctos de que no atrapará SIGKILL.

Abra un .lockfile con acceso exclusivo y haga que la encuesta infantil intente abrirlo; si el abierto tiene éxito, el proceso hijo debe salir


Logré hacer una solución portátil, sin votación, con 3 procesos abusando del control de terminal y las sesiones. Esto es masturbación mental, pero funciona.

El truco es:

  • se inicia el proceso A
  • El proceso A crea una tubería P (y nunca lee de ella)
  • proceso A se bifurca en el proceso B
  • proceso B crea una nueva sesión
  • el proceso B asigna un terminal virtual para esa nueva sesión
  • el proceso B instala el controlador SIGCHLD para que muera cuando el niño sale
  • el proceso B establece un controlador SIGPIPE
  • el proceso B se bifurca en el proceso C
  • el proceso C hace lo que necesita (por ejemplo, exec () s el binario sin modificar o ejecuta cualquier lógica)
  • el proceso B escribe en la tubería P (y bloquea de esa manera)
  • process A wait () s en el proceso B y sale cuando muere

De esa manera:

  • si el proceso A muere: el proceso B obtiene un SIGPIPE y muere
  • si el proceso B muere: el proceso A espera () vuelve y muere, el proceso C obtiene un SIGHUP (porque cuando el líder de sesión de una sesión asociada muere, todos los procesos en el grupo de proceso en primer plano obtienen un SIGHUP)
  • si el proceso C muere: el proceso B obtiene un SIGCHLD y muere, entonces el proceso A muere

Deficiencias:

  • el proceso C no puede manejar SIGHUP
  • el proceso C se ejecutará en una sesión diferente
  • process C can''t use session/process group API because it''ll break the brittle setup
  • creating a terminal for every such operation is not the best idea ever

No creo que sea posible garantizar el uso solo de llamadas POSIX estándar. Al igual que la vida real, una vez que un niño es engendrado, tiene vida propia.

Es posible que el proceso principal atrape la mayoría de los eventos de terminación posibles e intente matar el proceso secundario en ese punto, pero siempre hay algunos que no se pueden atrapar.

Por ejemplo, ningún proceso puede atrapar un SIGKILL . Cuando el kernel maneje esta señal, matará el proceso especificado sin ninguna notificación a ese proceso en absoluto.

Para ampliar la analogía, la única forma estándar de hacerlo es que el niño se suicide cuando descubre que ya no tiene un padre.

Hay una forma de hacerlo solo para Linux con prctl(2) - ver otras respuestas.


Por el bien de todos. En Mac OS X puedes usar kqueue:

void noteProcDeath( CFFileDescriptorRef fdref, CFOptionFlags callBackTypes, void* info) { // LOG_DEBUG(@"noteProcDeath... "); struct kevent kev; int fd = CFFileDescriptorGetNativeDescriptor(fdref); kevent(fd, NULL, 0, &kev, 1, NULL); // take action on death of process here unsigned int dead_pid = (unsigned int)kev.ident; CFFileDescriptorInvalidate(fdref); CFRelease(fdref); // the CFFileDescriptorRef is no longer of any use in this example int our_pid = getpid(); // when our parent dies we die as well.. LOG_INFO(@"exit! parent process (pid %u) died. no need for us (pid %i) to stick around", dead_pid, our_pid); exit(EXIT_SUCCESS); } void suicide_if_we_become_a_zombie(int parent_pid) { // int parent_pid = getppid(); // int our_pid = getpid(); // LOG_ERROR(@"suicide_if_we_become_a_zombie(). parent process (pid %u) that we monitor. our pid %i", parent_pid, our_pid); int fd = kqueue(); struct kevent kev; EV_SET(&kev, parent_pid, EVFILT_PROC, EV_ADD|EV_ENABLE, NOTE_EXIT, 0, NULL); kevent(fd, &kev, 1, NULL, 0, NULL); CFFileDescriptorRef fdref = CFFileDescriptorCreate(kCFAllocatorDefault, fd, true, noteProcDeath, NULL); CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack); CFRunLoopSourceRef source = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, fdref, 0); CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode); CFRelease(source); }


Si el padre muere, el PPID de huérfanos cambia a 1 - solo necesita verificar su propio PPID. En cierto modo, esto es un sondeo, mencionado anteriormente. aquí hay una pieza de concha para eso:

check_parent () { parent=`ps -f|awk ''$2==''$PID''{print $3 }''` echo "parent:$parent" let parent=$parent+0 if [[ $parent -eq 1 ]]; then echo "parent is dead, exiting" exit; fi } PID=$$ cnt=0 while [[ 1 = 1 ]]; do check_parent ... something done


Si envía una señal al pid 0, usando por ejemplo

kill(0, 2); /* SIGINT */

esa señal se envía a todo el grupo de procesos, lo que efectivamente mata al niño.

Puedes probarlo fácilmente con algo como:

(cat && kill 0) | python

Si luego presiona ^ D, verá el texto "Terminated" como una indicación de que el intérprete de Python se ha cancelado, en lugar de simplemente salir debido a que se cerró el stdin.


Si no puede modificar el proceso secundario, puede intentar algo como lo siguiente:

int pipes[2]; pipe(pipes) if (fork() == 0) { close(pipes[1]); /* Close the writer end in the child*/ dup2(0, pipes[0]); /* Use reader end as stdin */ exec("sh -c ''set -o monitor; child_process & read dummy; kill %1''") } close(pipes[0]); /* Close the reader end in the parent */

Esto ejecuta al elemento secundario dentro de un proceso de shell con el control de trabajo habilitado. El proceso secundario se genera en segundo plano. El shell espera una línea nueva (o un EOF) y luego mata al niño.

Cuando el padre muere, sin importar el motivo, cerrará su extremo del conducto. El shell hijo obtendrá un EOF de la lectura y procederá a matar el proceso secundario de fondo.


Another way to do this that is Linux specific is to have the parent be created in a new PID namespace. It will then be PID 1 in that namespace, and when it exits it all of it''s children will be immediately killed with SIGKILL .

Unfortunately, in order to create a new PID namespace you have to have CAP_SYS_ADMIN . But, this method is very effective and requires no real change to the parent or the children beyond the initial launch of the parent.

Vea clone(2) , pid_namespaces(7) y unshare(2) .


El niño puede pedirle al kernel que entregue SIGHUP (u otra señal) cuando el padre fallezca al especificar la opción PR_SET_PDEATHSIG en prctl() syscall de esta manera:

prctl(PR_SET_PDEATHSIG, SIGHUP);

Ver man 2 prctl para más detalles.

Editar: esto es solo para Linux


Historically, from UNIX v7, the process system has detected orphanity of processes by checking a process'' parent id. As I say, historically, the init(8) system process is a special process by only one reason: It cannot die. It cannot die because the kernel algorithm to deal with assigning a new parent process id, depends on this fact. when a process executes its exit(2) call (by means of a process system call or by external task as sending it a signal or the like) the kernel reassigns all children of this process the id of the init process as their parent process id. This leads to the most easy test, and most portable way of knowing if a process has got orphan. Just check the result of the getppid(2) system call and if it is the process id of the init(2) process then the process got orphan before the system call.

Two issues emerge from this approach that can lead to issues:

  • first, we have the possibility of changing the init process to any user process, so How can we assure that the init process will always be parent of all orphan processes? Well, in the exit system call code there''s a explicit check to see if the process executing the call is the init process (the process with pid equal to 1) and if that''s the case, the kernel panics (It should not be able anymore to maintain the process hierarchy) so it is not permitted for the init process to do an exit(2) call.
  • second, there''s a race condition in the basic test exposed above. Init process'' id is assumed historically to be 1 , but that''s not warranted by the POSIX approach, that states (as exposed in other response) that only a system''s process id is reserved for that purpose. Almost no posix implementation does this, and you can assume in original unix derived systems that having 1 as response of getppid(2) system call is enough to assume the process is orphan. Another way to check is to make a getppid(2) just after the fork and compare that value with the result of a new call. This simply doesn''t work in all cases, as both call are not atomic together, and the parent process can die after the fork(2) and before the first getppid(2) system call. The process parent id only changes once, when its parent does an exit(2) call, so this should be enough to check if the getppid(2) result changed between calls to see that parent process has exit. This test is not valid for the actual children of the init process, because they are always children of init(8)`, but you can assume safely these processes as having no parent either (except when you substitute in a system the init process)