Процессы, сигналы и зомби
Три вопроса о жизненном цикле процесса, которые отделяют «умею перезапускать сервис» от «понимаю, что при этом происходит».
Вопрос: «Чем
killотличается отkill -9, и почему второй вариант не стоит делать по привычке?»
Что на самом деле проверяет интервьюер
Это вопрос про корректное завершение работы. DevOps каждый день останавливает и катит приложения; если инженер по привычке шлёт -9, он теряет несохранённые данные, оставляет битые блокировки и ломает graceful shutdown. Второй слой — понимает ли кандидат, что сигнал это не «убийство», а асинхронное уведомление, на которое программа может отреагировать.
Развёрнутый ответ
kill без аргументов посылает SIGTERM (15) — вежливую просьбу завершиться. Процесс может её перехватить: закрыть соединения, дописать буферы на диск, снять блокировку, отписаться от очереди — и только потом выйти. Именно на SIGTERM построен graceful shutdown в nginx, PostgreSQL и любом нормальном веб-приложении.
SIGKILL (9) обрабатывает не процесс, а ядро: оно просто снимает процесс с выполнения и освобождает память. Перехватить или проигнорировать SIGKILL невозможно — в этом его сила и его опасность. Данные из буферов не попадут на диск, временные файлы и pid-файлы останутся, дочерние процессы могут осиротеть.
kill 4231 # SIGTERM: попроси завершиться
kill -TERM 4231 # то же самое, явно
kill -HUP 4231 # перечитать конфиг (nginx, sshd)
kill -9 4231 # SIGKILL: последнее средство
# список сигналов
kill -l | head -3
Правильный порядок действий: SIGTERM → подождать разумный таймаут (5–30 секунд) → если процесс жив, SIGKILL. Ровно так делают systemd (TimeoutStopSec), Docker (docker stop ждёт 10 секунд) и Kubernetes (terminationGracePeriodSeconds, по умолчанию 30). Если ваше приложение не успевает закрыться за это время — увеличивайте таймаут, а не переходите сразу к -9.
Полезные сигналы, которые стоит назвать
| Сигнал | Смысл | Перехватывается |
| SIGTERM (15) | вежливое завершение | да |
| SIGINT (2) | Ctrl+C в терминале | да |
| SIGHUP (1) | обрыв терминала или «перечитай конфиг» | да |
| SIGKILL (9) | снять немедленно | нет |
| SIGSTOP (19) | заморозить процесс | нет |
Вопрос 2: что такое зомби-процесс и опасен ли он
Когда процесс завершается, ядро не удаляет его запись сразу: оно оставляет код возврата, чтобы родитель мог его прочитать вызовом wait(). Такое состояние называется zombie (в ps — статус Z). Это нормальная промежуточная фаза длиной в миллисекунды.
Проблема начинается, если родитель написан небрежно и никогда не вызывает wait(). Тогда записи копятся. Памяти зомби почти не занимает, но таблица PID конечна: при накоплении десятков тысяч зомби система перестаёт создавать новые процессы, и вы получаете fork: retry: Resource temporarily unavailable.
# найти зомби и их родителей
ps -eo pid,ppid,stat,comm | awk '$3 ~ /^Z/'
# вывод: PID PPID STAT COMMAND
# 8123 8100 Z worker
# kill -9 на зомби бесполезен: он уже мёртв.
# лечится перезапуском РОДИТЕЛЯ (PPID 8100)
Отдельно стоит назвать сироту (orphan): это живой процесс, чей родитель умер. Его усыновляет PID 1, который корректно вызывает wait(). Поэтому сироты, в отличие от зомби, безвредны.
Вопрос 3: почему в контейнере не работает Ctrl+C
Любимый вопрос на стыке Linux и Docker. Процесс с PID 1 имеет особенность: для него не работают сигналы по умолчанию. Обычный процесс без обработчика SIGTERM просто умирает — за него это делает ядро. У PID 1 такого поведения нет: если приложение не установило обработчик SIGTERM явно, сигнал будет проигнорирован.
Отсюда классический симптом: docker stop висит ровно 10 секунд, а потом контейнер убивается жёстко. Вторая частая причина — приложение запущено через шелл (CMD npm start в shell-форме): PID 1 получает /bin/sh, который сигнал не пробрасывает потомку.
# плохо: PID 1 = /bin/sh -c, сигналы не доходят до node
CMD npm start
# хорошо: exec-форма, PID 1 = node, он сам получает SIGTERM
CMD ["node", "server.js"]
# если процесс плодит потомков и не собирает их — включаем init
# docker run --init ...
Типичные ошибки кандидатов
- «
kill -9надёжнее, поэтому я всегда так» — красный флаг, показывающий, что человек не сталкивался с потерей данных после жёсткого убийства СУБД. - Пытаются убить зомби через
kill -9. Зомби уже не выполняется, сигналы к нему неприменимы — убирать надо родителя. - Путают зомби и сироту: сирота живёт и работает, зомби — только запись в таблице процессов.
- Не знают про особенность PID 1 и потом неделю ищут, почему поды в Kubernetes всегда выселяются по таймауту.
- Забывают, что SIGKILL и SIGSTOP невозможно перехватить — на этом строится уточняющий вопрос.
Как ответить кратко (20–30 секунд)
«
killшлёт SIGTERM — процесс может его перехватить и корректно завершиться: дописать буферы, закрыть соединения.kill -9— это SIGKILL, его обрабатывает ядро, перехватить нельзя, поэтому данные в буферах теряются. Правильная последовательность — SIGTERM, таймаут, и только потом SIGKILL; именно так работают systemd, docker stop и Kubernetes. Отдельно помню, что PID 1 игнорирует сигналы без явного обработчика — из-за этого контейнеры зависают при остановке.»