Procesos zombis - Procesos e Hilos
Volver a Procesos e Hilos Bloque 6 - Zombis, huerfanos y demonios - Tema 24 de 43

Tema 24

Procesos zombis

Cuando un proceso termina, no desaparece inmediatamente. Su PCB se conserva hasta que el padre lo recoge. Mientras tanto, el proceso es un zombi: ya no se ejecuta ni usa memoria, pero sigue en la tabla de procesos. En este tema veras por que existe el zombi, Como detectarlo, y por que se acumulan.

1. Qué es un zombi

Un proceso zombi es un proceso que ha terminado su ejecución pero cuyo bloque de control de proceso (PCB) se conserva en la tabla de procesos, esperando que su padre lo recoga con wait() o waitpid(). El zombi no ejecuta código, no usa memoria (Más alla del PCB minimo), y no responde a senales. Solo ocupa una entrada en la tabla de procesos.

En la salida de ps, un zombi se reconoce por la letra Z en la columna STAT y por el nombre del proceso entre corchetes (por ejemplo, [worker]). En /proc/PID/status, el estado es Z (zombie).

El nombre "zombi" es metaforico: el proceso esta "muerto" (Terminó) pero sigue "vivo" en la tabla de procesos (su PCB existe). Es un estado intermedio entre "terminado" y "liberado".

El zombi no consume recursos (casí)

Un zombi no usa CPU ni memoria (el núcleo libera la memoria, los archivos y los descriptores cuándo el proceso termina). Solo conserva el PCB minimo con el PID, el PPID y el código de salida. Pero ocupa una entrada en la tabla de procesos, y sí se acumulan, pueden agotar el limite de procesos.

2. Por que existe el zombi

El zombi existe porque el padre necesita saber Cómo terminó el hijo. Cuando un hijo termina, el núcleo guarda su código de salida (o la senal que lo terminó) en el PCB. El padre puede consultar esta información con wait() o waitpid().

Si el PCB se liberara inmediatamente al terminar el hijo, el padre no podria obtener el código de salida. El zombi es el mecanismo que permite al padre recoger esta información en cualquier momento después de que el hijo terminó.

El zombi es una solución al problema de la sincronizacion: el hijo puede terminar en cualquier momento, y el padre puede no estar listo para recogerlo inmediatamente. El zombi "guarda" el resultado hasta que el padre este listo.

En sistemás dónde el padre no necesita el código de salida (por ejemplo, Sí ignora SIGCHLD), el núcleo puede recoger al hijo automaticamente sin crear un zombi. Pero por omision, el zombi se crea para que el padre pueda consultar el resultado.

3. El ciclo de vida de un zombi

El ciclo de vida de un proceso que termina y queda como zombi es:

  1. El hijo termina: llama a exit() o return en main, o recibe una senal fatal.
  2. El núcleo libera los recursos: Memoria, archivos, descriptores. El proceso ya no se ejecuta.
  3. El núcleo guarda el código de salida: en el PCB, junto con el estado de terminación.
  4. El núcleo marca el proceso como zombi: cambia el estado a Z. El PCB se conserva.
  5. El núcleo envia SIGCHLD al padre: Sí el padre tiene un manejador, se ejecuta.
  6. El padre llama a wait(): el núcleo devuelve el estado de terminación y libera el PCB.
  7. El proceso desaparece: el PCB se libera y el PID Queda disponible para reutilizacion.

Si el padre nunca llama a wait(), el proceso queda como zombi indefinidamente (paso 5 Sin paso 6). El PCB se conserva hasta que el padre lo recoge o hasta que el padre termina.

Si el padre termina antes de recoger al zombi, el zombi queda huerfano y es adoptado por el proceso 1 (o un subreaper), Que lo recoge automaticamente. Esto se ve en el tema 26.

4. Cómo detectar un zombi

Un zombi se detecta fácilmente con las herramientas estandar:

terminal - detectar zombis con ps

$ ps -o pid,ppid,stat,comm -p 999
    PID    PPID STAT COMMAND
    999     998 Z    [worker]

# Buscar todos los zombis del sistema
$ ps -e -o pid,ppid,stat,comm | grep " Z"
    999     998 Z    [worker]
   1000     998 Z    [worker]
   1001     998 Z    [worker]

La letra Z en la columna STAT y el nombre entre corchetes son los indicadores de zombi. Tambien se puede contar:

terminal - contar zombis

$ ps -e -o stat | grep -c "^Z"
3

5. El problema de los zombis acumulados

Un zombi individual no es un problema: ocupa una entrada en la tabla de procesos y nada más. Pero sí un programa crea hijos y no los espera, los zombis se acumulan. Cada hijo que termina queda como zombi, y después de miles de hijos, la tabla de procesos se llena.

Cuando la tabla de procesos se llena, el sistema no puede crear más procesos: fork falla con EAGAIN. Esto puede hacer que el sistema deje de funciónar: no se pueden ejecutar comandos, no se pueden crear conexiones, etc.

El problema es especialmente grave en servidores que crean un proceso por conexion (Como apache en modo prefork). Si el servidor no recoge a los hijos terminados, se acumulan zombis y el servidor deja de funciónar.

Los zombis son un síntoma, no la causa

Los zombis son el síntoma de un programa que no recoge a sus hijos. La causa es la falta de wait() o de un manejador de SIGCHLD. Arreglar el programa (agregar wait o SIGCHLD) elimina los zombis. Matar al padre (Que es el que debe recogerlos) También elimina los zombis, pero es una solución drastica.

6. Zombis en /proc

En /proc, un zombi tiene un directorio con su PID, pero la mayoria de los archivos estan vacios o no disponibles, porque el núcleo ya libero los recursos:

terminal - zombi en /proc

$ grep -E "^(Name|State|Pid|PPid)" /proc/999/status
Name:   worker
State:  Z (zombie)
Pid:    999
PPid:   998
$ ls /proc/999/
attr  auxv  cgroup  clear_refs  cmdline  comm  environ  exe  fd  ...
$ cat /proc/999/cmdline  # vacio
$ ls -l /proc/999/fd  # vacio
$ cat /proc/999/maps  # vacio

El directorio existe, pero los archivos estan vacios porque el núcleo ya libero la memoria, los descriptores y los argumentos. Solo el PCB minimo se conserva.

7. Zombis y el limite de procesos

Los zombis cuentan para el limite de procesos por usuario (ulimit -u) y para el limite del sistema (pid_max). Cada zombi ocupa una entrada en la tabla de procesos y un PID. Si se acumulan suficientes zombis, fork falla con EAGAIN.

Esto es especialmente grave en sistemás con muchos procesos de corta duracion (servidores web, scripts de automatizacion). Si el programa no recoge a los hijos, se agota el limite y el sistema deja de funciónar.

El limite de zombis que un sistema puede tolerar es el mismo que el limite de procesos: tipicamente miles. Pero en sistemás con limites bajos (contenedores, Sistemás embebidos), unos pocos cientos de zombis pueden ser suficientes para agotar el limite.

8. Zombi vs proceso detenido

Es importante no confundir un zombi (Z) con un proceso detenido (T). Aunque ambos no se ejecutan, son estados muy diferentes:

CaracteristicaZombi (Z)Detenido (T)
Ya terminó?SiNo
Se puede reanudar?NoSi (con SIGCONT)
Responde a senales?NoSi (SIGCONT lo reanuda)
Usa memoria?No (Sólo PCB)Si (toda su memoria)
Usa CPU?NoNo
Cómo se elimina?El padre lo recoge con wait()SIGCONT o SIGKILL
Nombre en psEntre corchetes [nombre]Nombre normal

9. Zombis en sistemás reales

En sistemás reales, los zombis son un problema comun. Las causas más frecuentes son:

Para diagnosticar zombis en un sistema:

terminal - diagnosticar zombis

# Contar zombis
$ ps -e -o stat | grep -c "^Z"
47

# Encontrar el padre de los zombis
$ ps -e -o pid,ppid,stat,comm | grep " Z"
   999     998 Z    [worker]
  1000     998 Z    [worker]
  1001     998 Z    [worker]

# El padre es el PID 998
$ ps -o pid,comm -p 998
    PID COMMAND
    998 servidor

Una vez identificado el padre, las opciones son:

10. Un programa que crea zombis

El siguiente programa crea zombis intencionalmente para demostrar como se detectan:

crear_zombis.c

#define _POSIX_C_SOURCE 200809L
#include 
#include 
#include 

int main(void)
{
    printf("Padre PID: %d\n", (int) getpid());
    printf("Creando 5 hijos que terminaran inmediatamente...\n");

    for (int i = 0; i < 5; i++) {
        pid_t pid = fork();
        if (pid < 0) { perror("fork"); return 1; }
        if (pid == 0) {
            /* Hijo: terminar inmediatamente */
            _exit(0);
        }
        /* Padre: NO espera al hijo */
    }

    printf("Hijos creados. Observa los zombis con: ps -o pid,ppid,stat,comm -p %d,...\n", (int) getpid());
    printf("Presiona enter para recoger a los hijos y terminar...\n");
    getchar();

    /* Recoger a todos los hijos */
    while (wait(NULL) > 0);
    printf("Todos los hijos recogidos. Terminando.\n");
    return 0;
}
terminal - ejecutar crear_zombis

$ gcc -std=c17 -Wall -Wextra -o crear_zombis crear_zombis.c
$ ./crear_zombis &
[1] 1050
Padre PID: 1050
Creando 5 hijos que terminaran inmediatamente...
Hijos creados. Observa los zombis con: ps -o pid,ppid,stat,comm -p 1050,...

$ ps -e -o pid,ppid,stat,comm | grep " Z"
  1051  1050 Z    [crear_zombis]
  1052  1050 Z    [crear_zombis]
  1053  1050 Z    [crear_zombis]
  1054  1050 Z    [crear_zombis]
  1055  1050 Z    [crear_zombis]

$ # Presionar enter en la terminal del programa
Todos los hijos recogidos. Terminando.

El programa crea 5 hijos que terminan inmediatamente. Como el padre no los espera, Quedan como zombis. Despues de presionar enter, el padre los recoge con wait() y los zombis desaparecen.

Mientras los zombis existen, puedes observarlos en /proc: sus directorios existen pero estan vacios (Sin memoria, Sin descriptores, Sin cmdline).

11. Resumen

12. Preguntas de repaso

Intenta responder cada una antes de abrir la respuesta.

1. Qué es un proceso zombi?

Un proceso que terminó su ejecución pero cuyo PCB se conserva en la tabla de procesos, esperando que el padre lo recoga.

2. Por que existe el zombi?

Para que el padre pueda obtener el código de salida del hijo. El PCB se conserva con el estado de terminación hasta que el padre lo recoge con wait().

3. Cómo se detecta un zombi en ps?

Por la letra Z en la columna STAT y el nombre del proceso entre corchetes (por ejemplo, [worker]).

4. Un zombi consume CPU o memoria?

No. No consume CPU ni memoria (el núcleo libera los recursos al terminar). Solo ocupa una entrada en la tabla de procesos y un PCB minimo.

5. Qué pasa sí los zombis se acumulan?

Se agota el limite de procesos (ulimit -u, pid_max). fork falla con EAGAIN y el sistema no puede crear más procesos.

6. Cómo se recoge a un zombi?

El padre llama a wait() o waitpid(). O, en linux, ignorando SIGCHLD, Que hace que los hijos se recogan automaticamente.

7. Qué pasa sí el padre termina antes de recoger al zombi?

El zombi queda huerfano y es adoptado por el proceso 1 (o un subreaper), Que lo recoge automaticamente.

8. Cómo se diferencia un zombi de un proceso detenido?

El zombi (Z) ya terminó y no se puede reanudar. El detenido (T) no terminó y se puede reanudar con SIGCONT.

9. Qué se ve en /proc para un zombi?

El directorio existe, pero los archivos (cmdline, maps, fd, etc.) estan vacios porque el núcleo ya libero los recursos.

10. Cómo se diagnostican zombis en un sistema?

Con ps -e -o pid,ppid,stat,comm | grep Z para listarlos, y luego identificar al padre (PPID) para arreglar el programa o reiniciar el servicio.

Para prácticar

  1. Compila y ejecuta crear_zombis.c. Observa los zombis con ps mientras el programa espera.
  2. Mientras los zombis existen, lee /proc/PID/status y /proc/PID/cmdline de uno de ellos.
  3. Presiona enter en el programa para recoger a los hijos y verifica que los zombis desaparecen.
  4. Cuenta los zombis de tu sistema con ps -e -o stat | grep -c "^Z".
  5. Escribe un programa que cree 100 hijos sin esperarlos y observa como se acumulan los zombis.
  6. Modifica el programa para ignorar SIGCHLD y verifica que no se crean zombis.