Что такое зомби-процесс — и почему kill -9 его не берёт

В списке процессов висит странная строка: [chrome] <defunct>. Ты шлёшь ей kill -9, самый сильный сигнал в Linux. Смотришь снова, а она на месте.
Сигнал не сломался. Просто убивать уже некого. Этот процесс давно мёртв.
Что такое зомби-процесс
Зомби — это завершившийся процесс, от которого осталась одна запись в таблице процессов. Кода нет. Памяти нет. Есть номер (PID) и код выхода.
Зачем система хранит запись о мертвеце? Потому что родительский процесс может спросить: «Как там мой ребёнок, успешно закончил?» Пока родитель не спросил, ответ надо где-то держать.
Представь гардероб. Пальто уже забрали, а номерок всё висит, потому что никто его не снял. Вещи нет, место занято.
Как он появляется
У процесса есть короткая последняя стадия:
- Ребёнок завершается. Система освобождает его память и закрывает файлы.
- Запись остаётся в таблице со статусом
Z. В этот момент он зомби. - Родитель делает системный вызов
waitи забирает код выхода. - Запись исчезает.
Смотри, неожиданная штука: зомби бывает каждый процесс на свете, обычно на миллисекунды. Беда начинается, когда шаг 3 не наступает. Родитель жив, работает, а спросить забыл.
Собрать зомби можно за пять строк на Python:
import os, time
pid = os.fork()
if pid == 0:
os._exit(3) # ребёнок умер сразу
time.sleep(60) # родитель не спрашивает
В соседнем окне ты увидишь это:
$ ps -o pid,stat,cmd -p 3912796
PID STAT CMD
3912796 Z [python3] <defunct>
$ kill -9 3912796
$ ps -o pid,stat,cmd -p 3912796
PID STAT CMD
3912796 Z [python3] <defunct>
Сигнал дошёл до процесса, которого нет. Стоило родителю вызвать os.waitpid, и строка пропала.
Как найти и убрать зомби
Покажи всех зомби и их родителей:
$ ps -eo pid,ppid,stat,cmd | awk '$3 ~ /^Z/'
Вторая колонка — PPID, номер родителя. Именно он тут виноват.
Лекарства два:
- Починить родителя. Пусть вызывает
wait. В Python это значит не бросатьsubprocess.Popenбез.wait(). Обычныйsubprocess.runждёт сам. - Завершить родителя. Сироты переходят к процессу с номером 1, а тот честно зовёт
waitза всех. Зомби исчезают сами.
Вот этот второй пункт и есть главный вывод: чтобы убрать зомби, убивать надо не его.
Перед лечением прочитай колонку STAT в ps. Буквы подскажут, с кем ты имеешь дело:
Z— зомби, уже мёртв. Сигналы бесполезны.D— живой процесс застрял в ожидании диска или сети. Егоkill -9тоже не возьмёт, пока ожидание не закончится, но это совсем другая болезнь: виноват диск, а не родитель.SилиR— обычный живой процесс. Сюдаkillкак раз и работает.
Одна буква экономит час поисков не в том месте.
Почему тебе это важно
Один зомби безвреден. Память он не ест. Но номеров процессов конечное число, а в контейнере их обычно ограничивают отдельно. Закончатся номера — новые процессы не запустятся. Выглядит это как fork: Resource temporarily unavailable. Причём падать будет всё подряд, и зомби в этом никто не заподозрит.
Самое частое место встречи сегодня — Docker с браузером внутри. Смотри, как складывается ловушка:
- Твой ИИ-агент или скрейпер запускает headless Chrome.
- Chrome порождает кучу помощников, и часть из них становится сиротами.
- Сирот усыновляет процесс номер 1.
- В контейнере номер 1 — это твоё приложение. Node.js не зовёт
waitза чужих детей.
На обычной машине номер 1 — это init, он убирает за всеми. В контейнере уборщика нет. Зомби копятся неделями, и один разработчик описал контейнер, который дошёл так до лимита в 15 тысяч процессов.
Лечится это одним флагом, и его рекомендует документация Puppeteer:
$ docker run --init my-scraper
Флаг --init ставит крошечный процесс номер 1, который умеет убирать сирот. В Docker Compose это строка init: true. Если в проекте есть браузер в контейнере, поставь этот флаг сразу, вреда от него нет.
Рядом стоит родственный лимит, про который тоже узнают по странной ошибке: файловые дескрипторы. Логика та же. Ресурс маленький, текут незаметно, а падает совсем другое.
Запомни простую проверку. Видишь Z или <defunct> — смотри на PPID. Лечить надо родителя, а мертвеца оставь в покое.
Зомби занимают память?
Почти нет: только небольшую запись в таблице процессов. Настоящая цена — занятый номер процесса.
Чем зомби отличается от сироты?
Зомби мёртв, а его родитель жив. Сирота жив, а его родитель умер. Сироту усыновляет процесс номер 1, и у него зомби не задерживаются.
Можно ли убить зомби вручную?
Нет, он уже завершён. Можно только заставить родителя вызвать wait или завершить самого родителя.
Короткие уроки-истории, симулятор агента и ежедневная практика — в нашем мобильном приложении. Бесплатно.





