В ps появились zombie: ищем родителя, который не забрал статус
Дежурный видит в ps несколько десятков строк с состоянием Z и предлагает завершить их через kill -9. Команда ничего не меняет. Причина проста: zombie уже завершился. В таблице процессов осталась запись с результатом выполнения, который должен забрать родитель. Поэтому исправлять нужно жизненный цикл дочерних задач.
Найти связанный процесс
ps -eo pid,ppid,stat,etime,cmd ps -p 4321 -o pid,ppid,stat,lstart,cmd cat /proc/4321/status
Сначала найдите строки, где STAT начинается с Z, и запишите PPID. Во второй команде используйте именно родительский PID. Посмотрите, это работающая служба, разовый управляющий процесс или оболочка оператора. Несколько кратковременных Z между завершением задачи и вызовом wait нормальны. Важны устойчивое накопление и отсутствие освобождения записей.
Zombie не продолжает исполнять код приложения и не держит его прежний адресный простор. Однако запись и PID остаются занятыми. Массовое накопление может мешать созданию новых процессов. Механизм получения статуса через wait и waitpid описан в wait(2).
Почему сигнал ребёнку бесполезен
Сигнал завершения рассчитан на живую задачу. Для zombie больше нет выполняющегося кода, которому можно поручить выйти. Родитель должен вызвать функцию ожидания и получить статус. Если он сам завершится, обязанности по ожиданию перейдут к подходящему принимающему процессу, в том числе subreaper или init. Это объясняет, почему перезапуск родителя иногда убирает строки Z.
Но родитель может обслуживать другие задания. Перед его остановкой посмотрите дочерние процессы, активные работы и правило повторного запуска. Аварийное уничтожение управляющего процесса способно прервать полезную работу и оставить внешние операции в неопределённом состоянии. Плановый перезапуск — временная мера, если программа пока не исправлена.
Что передать разработчику
Сохраните PID и время старта родителя, несколько завершившихся детей, динамику их числа и момент появления проблемы. В программе ищут ветви создания дочерних задач и соответствующие вызовы ожидания. Частая ошибка — ожидать одного ребёнка, пока несколько других уже завершились. При неблокирующем ожидании нужно обработать все доступные результаты, а не остановиться после первого.
Отдельно проверьте обработчик SIGCHLD и библиотеку управления subprocess. Сложное приложение должно иметь одно понятное место, которое владеет ожиданием, иначе разные части кода могут конфликтовать. Не применяйте глобальную смену поведения SIGCHLD без понимания, требуется ли приложению код завершения дочерней команды.
Прочитать дерево из трёх строк
Условный вывод ps показывает PID 4200 с PPID 1 и состоянием Sl, затем PID 4211 и 4212 с PPID 4200 и состоянием Z. Оба завершившихся ребёнка относятся к одному управляющему процессу. Если через минуту появляются 4213 и 4214, а прежние Z остаются, наблюдается накопление. Передавайте разработчику PID 4200 и его время старта: именно там нужно искать невыполненное ожидание.
Если вместо этого каждый снимок содержит один-два Z с новыми PID, а предыдущие исчезают, родитель может нормально забирать статусы с небольшой задержкой. Для вывода об утечке нужно наблюдать сохранение старых записей, а не только ненулевое число в одном снимке. Это позволяет избежать лишнего перезапуска исправной службы.
Подтвердить устранение
Повторите серию коротких заданий на стенде. После завершения очереди число Z должно возвращаться к исходному уровню, а результаты дочерних задач — оставаться доступными управляющему коду. Для рабочих серверов добавьте наблюдение за устойчивым ростом, чтобы заметить регрессию раньше исчерпания PID.
Откройте курс Linux и Bash для DevOps-инженеров: практика с процессами и сервисами помогает читать дерево родительских связей и отличать зависшую программу от уже завершённой. Это полезная основа для разговора с разработчиком на языке проверенных фактов.
Ещё по теме
Продолжить на практике
- Linux и Bash для DevOps-инженеров ↗
Linux для работы с серверами: процессы, права, systemd, сеть, диски и логи. Автоматизируйте повседневные задачи на Bash и разберите типичные сбои сервиса — от занятого порта до переполненного диска.
