Что такое зомби-процесс? Как можно создать такой процесс?devops-47

Что такое зомби-процесс?

Зомби-процесс (Zombie, defunct process) — это уже завершенный процесс, который остается в таблице процессов до тех пор, пока его родитель не прочитает его статус завершения.

Ключевые характеристики:

  • Обозначается статусом Z в ps/top
  • Не потребляет CPU и память (только запись в таблице процессов)
  • Не может быть "убит" сигналом SIGKILL (уже мертв)
  • Занимает только PID в системе

Пример обнаружения:

ps aux | grep 'Z'
# Или более точно:
ps -eo pid,ppid,stat,cmd | grep -w 'Z'

Как создается зомби-процесс?

Механизм возникновения

  1. Процесс завершается (через exit() или сигнал)
  2. Ядро сохраняет его статус завершения (exit code)
  3. Родительский процесс должен вызвать wait() или waitpid() для чтения статуса
  4. Если родитель не вызывает wait() → процесс становится зомби

Способы искусственного создания

1. Необработанный дочерний процесс в программе на C

#include <stdlib.h>
#include <unistd.h>

int main() {
    pid_t pid = fork();

    if (pid == 0) {
        // Дочерний процесс завершается сразу
        exit(0);
    } else {
        // Родитель НЕ вызывает wait() и продолжает работу
        sleep(30);
    }
    return 0;
}

Компиляция и запуск:

gcc zombie.c -o zombie
./zombie
# В другом терминале:
ps aux | grep 'Z'

2. Игнорирование SIGCHLD

Если родительский процесс явно игнорирует сигнал SIGCHLD:

signal(SIGCHLD, SIG_IGN);

3. Ошибки в скриптах

Пример на Python:

import os
import time

pid = os.fork()
if pid == 0:
    # Дочерний процесс
    os._exit(0)
else:
    # Родитель не вызывает os.wait()
    time.sleep(1000)

Чем опасны зомби-процессы?

  1. Утечка PID:

    • Каждый зомби занимает запись в таблице процессов
    • При достижении лимита (/proc/sys/kernel/pid_max) нельзя создать новые процессы
  2. Проблемы мониторинга:

    • Искажают статистику (например, общее число процессов)
  3. Признак проблем в приложении:

    • Указывают на неправильную обработку дочерних процессов

Как бороться с зомби-процессами?

1. Правильная обработка в коде

// Вариант 1: Блокирующий вызов
wait(NULL);

// Вариант 2: Неблокирующая проверка
while (waitpid(-1, NULL, WNOHANG) > 0);

2. Перехват SIGCHLD

void sigchld_handler(int sig) {
    while (waitpid(-1, NULL, WNOHANG) > 0);
}

int main() {
    signal(SIGCHLD, sigchld_handler);
    // ...
}

3. Если родительский процесс "умер":

  1. Найдите PPID зомби-процесса:
    ps -eo pid,ppid,stat,cmd | grep -w 'Z'
    
  2. Если PPID = 1 (init), перезапустите init:
    sudo kill -HUP 1
    

4. Автоматическая очистка

Ядро Linux автоматически очищает зомби, если:

  • Родительский процесс завершается
  • Родитель явно игнорирует SIGCHLD (signal(SIGCHLD, SIG_IGN))

Практический пример: создание и устранение

  1. Создаем зомби:
perl -e 'fork(); exit; sleep 10'
  1. Находим его:
ps -eo pid,ppid,stat,cmd | grep -w 'Z'
  1. "Убиваем" (на самом деле завершаем родительский процесс):
kill -9 PPID_зомби

Резюмируем:

  • Зомби-процесс — завершенный процесс, ожидающий чтения статуса родителем
  • Создается при отсутствии вызова wait()/waitpid()
  • Не опасен сам по себе, но указывает на проблемы в коде
  • Лечится: правильной обработкой дочерних процессов или завершением родителя
  • Ключевое правило: всегда обрабатывайте завершение дочерних процессов в своих программах!