Сигналы и kill
Сигналы — это асинхронные уведомления, доставляемые ядром процессам. SIGTERM (15) вежливо просит завершиться и может быть перехвачен; SIGKILL (9) не может быть перехвачен или проигнорирован и пропускает всю очистку. Сначала используй SIGTERM — SIGKILL только в крайнем случае.
Сигнал — это не сообщение с полезной нагрузкой, а лёгкое касание плеча со стороны ядра. Ядро ставит в очередь небольшое целое число у дверей процесса, и процесс либо обрабатывает его зарегистрированной функцией, либо игнорирует, либо позволяет выполниться действию по умолчанию. Большинство действий по умолчанию — «завершить». Четыре сигнала, которые ты будешь использовать ежедневно: SIGTERM (15), SIGKILL (9), SIGINT (2) и SIGHUP (1). Они выглядят похоже, но ведут себя очень по-разному, и выбор неправильного стоит целостности данных, открытых соединений и чистых записей в логах.
После этого урока ты сможешь отправлять SIGTERM, SIGKILL, SIGINT и SIGHUP по имени и номеру с помощью kill, pkill и killall; объяснять, почему SIGKILL нельзя перехватить; описывать, что делает обработчик сигнала; и знать, когда применяется каждый из четырёх сигналов.
Сигналы — это целые числа. У каждого есть действие по умолчанию, которое можно переопределить. Ядро доставляет сигнал, устанавливая бит ожидающего сигнала в структуре задачи процесса. При следующем проходе через планировщик ядра ожидающий сигнал проверяется и обрабатывается.
Процесс может зарегистрировать обработчик сигнала — функцию, которая выполняется вместо действия по умолчанию. Он также может игнорировать сигнал (SIG_IGN). Исключения: SIGKILL (9) и SIGSTOP (19/17) — их никогда нельзя перехватить, заблокировать или проигнорировать. Ядро всегда обрабатывает их напрямую.
Ключевые сигналы и их действия по умолчанию:
| Сигнал | Номер | По умолчанию | Перехват? | Типичное применение |
|---|---|---|---|---|
| SIGHUP | 1 | Завершить | Да | Перезагрузка конфига |
| SIGINT | 2 | Завершить | Да | Ctrl-C |
| SIGQUIT | 3 | Core dump | Да | Ctrl-\ |
| SIGKILL | 9 | Завершить | Нет | Принудительное завершение |
| SIGTERM | 15 | Завершить | Да | Плавная остановка |
| SIGSTOP | 19/17 | Остановить | Нет | Пауза процесса |
| SIGTSTP | 20/18 | Остановить | Да | Ctrl-Z |
| SIGCONT | 18/19 | Продолжить | Да | Возобновить остановленный |
kill PID отправляет SIGTERM (15) — вежливый запрос на завершение. Несмотря на название, kill по умолчанию не завершает процесс принудительно. Он отправляет SIGTERM, который процесс может перехватить для выполнения очистки: сброс буферов записи, закрытие соединений с базой данных, завершение текущих запросов, запись надгробия PID-файла.
kill 1843 # отправить SIGTERM процессу с PID 1843
kill -15 1843 # то же, явный номер сигнала
kill -SIGTERM 1843 # то же, полное имя сигнала
kill -TERM 1843 # то же, короткое имяЕсли процесс зарегистрировал обработчик SIGTERM, он запускает эту функцию. Если нет — немедленно выполняется действие по умолчанию (завершение). Правильно написанные демоны (nginx, postgres, redis) всегда перехватывают SIGTERM и завершаются чисто.
kill -9 PID отправляет SIGKILL — безусловное завершение. Ядро доставляет SIGKILL напрямую, не заходя в пространство пользователя. Никакой обработчик не может его перехватить. Процесс не получает возможности сбросить буферы, закрыть соединения или очистить временные файлы.
kill -9 1843 # SIGKILL — немедленно, без очистки
kill -KILL 1843 # то же, по имениКогда использовать SIGKILL: только после того, как SIGTERM не сработал, а процесс действительно завис (блокируется в коде ядра без возможности прерывания, игнорирует SIGTERM или уже стал зомби). Стандартная последовательность:
kill 1843 # SIGTERM — дать время на очистку
sleep 5
kill -0 1843 2>/dev/null && kill -9 1843 # SIGKILL только если ещё живkill -0 не отправляет сигнал — он проверяет, существует ли процесс и есть ли у тебя право отправлять ему сигналы. Код выхода 0 означает, что процесс ещё существует.
pkill и killall отправляют сигналы по имени процесса, а не по PID. Полезно, когда не хочется сначала искать PID.
pkill nginx # SIGTERM всем процессам с именем "nginx"
pkill -9 nginx # SIGKILL всем процессам с именем "nginx"
pkill -u alice # SIGTERM всем процессам пользователя alice
pkill -f 'python train.py' # совпадение по полной командной строке
killall nginx # то же, что pkill nginx на Linux (на BSD поведение отличается)pkill сопоставляет по полю имени процесса (усечённому до 15 символов в некоторых ядрах). -f сопоставляет всю командную строку, что точнее. Всегда проверяй, какие процессы попадут под pgrep nginx, прежде чем отправлять сигнал.
pgrep nginx # список PID без отправки сигнала
pgrep -l nginx # список PID + именаSIGHUP (1) — сигнал перезагрузки конфигурации. Изначально означал «терминал отключился». Демоны переопределяют его: при получении SIGHUP они перечитывают файл конфигурации без перезапуска. Так ты говоришь nginx, sshd или rsyslogd подхватить изменения конфига без разрыва соединений.
kill -HUP $(pgrep nginx) # сказать nginx перезагрузить конфиг
kill -1 $(cat /var/run/nginx.pid) # то же через PID-файлSIGINT (2) — то, что Ctrl-C отправляет группе процессов переднего плана. Оболочка генерирует его, и ядро доставляет его одновременно каждому процессу в группе переднего плана. Процесс, игнорирующий SIGINT, не отреагирует на Ctrl-C.
Плавно остановить зависший веб-сервер с откатом на SIGKILL.
# Шаг 1: найти PID
pgrep -a gunicorn
# Шаг 2: попробовать SIGTERM
kill $(pgrep gunicorn)
# Шаг 3: подождать и проверить
sleep 10
pgrep gunicorn && echo "ещё жив"
# Шаг 4: если жив — SIGKILL
kill -9 $(pgrep gunicorn)На практике правильно написанный gunicorn перехватывает SIGTERM, завершает текущие запросы, закрывает пулы соединений с базой данных и завершается чисто. Если он не завершается в течение 10–30 секунд (в зависимости от SLA), то SIGKILL оправдан. Главное: никогда не начинай с SIGKILL.
▸Почему это работает
На macOS номера сигналов SIGSTOP и SIGCONT отличаются (SIGSTOP = 17, SIGCONT = 19 на macOS; на Linux наоборот). Имена сигналов всегда работают переносимо. Команды pkill и pgrep доступны на macOS, но могут не поддерживать все те же флаги. killall на macOS по умолчанию отправляет SIGTERM и ведёт себя идентично Linux для базового использования.
▸Частая ошибка
kill -9 как первый и единственный инструмент — самая распространённая ошибка с сигналами. SIGKILL пропускает плавное завершение: открытые файлы могут остаться в несогласованном состоянии, журналы упреждающей записи базы данных — незакоммиченными, сетевые соединения сбрасываются вместо аккуратного закрытия (TCP FIN никогда не отправляется), временные файлы накапливаются. Всегда давай процессу возможность убраться за собой через SIGTERM. Если твой сервис регулярно требует SIGKILL для остановки — это баг в сервисе, а не в системе: он не обрабатывает SIGTERM.
Почему процесс не может перехватить или проигнорировать SIGKILL?
Сигналы — целочисленные уведомления, доставляемые ядром. SIGTERM (15) — вежливый запрос на завершение, может быть перехвачен для плавной очистки. SIGKILL (9) не может быть перехвачен или проигнорирован; ядро немедленно завершает процесс без очистки. SIGINT (2) — то, что Ctrl-C отправляет группе процессов переднего плана. SIGHUP (1) говорит демонам перезагрузить конфигурацию. Используй kill PID для SIGTERM, kill -9 PID для SIGKILL; pkill NAME адресует по имени процесса; pgrep NAME показывает, какие PID будут затронуты, без отправки сигнала. Всегда начинай с SIGTERM.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.