Al igual que los zombis reales, un proceso zombie no se puede matar, porque ya está muerto.
Como sucede
Cuando en Linux / Unix un proceso muere / finaliza, toda la información del proceso se elimina de la memoria del sistema, solo queda el descriptor del proceso. El proceso entra en el estado Z (zombie). Su proceso padre recibe una señal del núcleo: SIGCHLD
eso significa que uno de sus procesos hijos sale, se interrumpe o se reanuda después de ser interrumpido (en nuestro caso, simplemente sale).
El proceso padre ahora necesita ejecutar la wait()
llamada al sistema para leer el estado de salida y otra información de su proceso hijo. Luego, el descriptor se elimina de la memoria y el proceso ya no es un zombie.
Si el proceso padre nunca llama a la llamada al wait()
sistema, el descriptor del proceso zombie permanece en la memoria y come cerebros. Normalmente no ves procesos zombies, porque el procedimiento anterior lleva menos tiempo.
El amanecer de los muertos
Cada descriptor de proceso necesita una cantidad muy pequeña de memoria, por lo que algunos zombis no son muy peligrosos (como en la vida real). Un problema es que cada proceso zombie mantiene su identificación de proceso, y un sistema operativo Linux / Unix tiene un número limitado de pid. Si un software programado incorrectamente genera muchos procesos zombies, puede suceder que los procesos ya no puedan iniciarse porque no hay más identificadores de proceso disponibles.
Entonces, si están en grandes grupos, son muy peligrosos (como en muchas películas se demuestra muy bien)
¿Cómo podemos defendernos de una horda de zombis?
Un disparo en la cabeza funcionaría, pero no sé el comando para eso (SIGKILL no funcionará porque el proceso ya está muerto).
Bueno, puedes enviar SIGCHLD a través de kill al proceso padre, pero cuando ignora esta señal, ¿qué sucede entonces? Su única opción es matar el proceso padre y que el proceso init "adopte" al zombi. Init llama periódicamente al wait()
syscall para limpiar a sus hijos zombis.
En tu caso
En su caso, debe enviar SIGCHLD al proceso crond:
root@host:~# strace -p $(pgrep cron)
Process 1180 attached - interrupt to quit
Luego desde otra terminal:
root@host:~$ kill -17 $(pgrep cron)
El resultado es:
restart_syscall(<... resuming interrupted call ...>) = ? ERESTART_RESTARTBLOCK (To be restarted)
--- SIGCHLD (Child exited) @ 0 (0) ---
wait4(-1, 0x7fff51be39dc, WNOHANG, NULL) = -1 ECHILD (No child processes) <-- Here it happens
rt_sigreturn(0xffffffffffffffff) = -1 EINTR (Interrupted system call)
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=1892, ...}) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {0x403170, [CHLD], SA_RESTORER|SA_RESTART, 0x7fd6a7e9d4a0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({42, 0}, ^C <unfinished ...>
Process 1180 detached
Verá que la wait4()
llamada al sistema devuelve -1 ECHILD, lo que significa que no hay ningún proceso secundario. Entonces la conclusión es: cron reacciona a la llamada al sistema SIGCHLD y no debe forzar el apocalipsis.