Сигналы и завершение процессов: TERM, KILL и корректная остановка

Сигнал — асинхронное уведомление процессу от ядра или другого процесса. Он может попросить приложение перечитать конфигурацию, остановиться, продолжить работу или завершиться. Сигнал не равен гарантированной «команде»: процесс может обработать некоторые сигналы, заблокировать их или уже исчезнуть.

Начинайте с SIGTERM

kill PID без флага отправляет SIGTERM. Это запрос на корректное завершение: приложение может перестать принимать новые запросы, закончить текущую запись, закрыть сокеты и удалить временные файлы.

sleep 300 &
pid=$!
kill -TERM "$pid"
wait "$pid"
printf 'wait status=%s\n' "$?"

wait связывает наблюдение с конкретным дочерним PID. Проверка только через ps может попасть в момент до завершения или увидеть уже переиспользованный номер.

Обработчик и cleanup

Bash-скрипт может поставить trap. Обработчик должен быть коротким и идемпотентным: второй сигнал не должен разрушать уже начатую очистку.

tmp_dir=$(mktemp -d)
cleanup() {
  rm -r -- "$tmp_dir"
}
trap cleanup EXIT
trap 'exit 143' TERM
printf 'working in %s\n' "$tmp_dir"

Trap на EXIT выполняется и при обычном завершении. В production временный каталог лучше создавать на подходящем filesystem и удалять только после проверки непустого ожидаемого префикса.

SIGKILL — крайняя мера

SIGKILL нельзя перехватить, заблокировать или проигнорировать. Ядро прекращает выполнение немедленно, поэтому приложение не делает cleanup. kill -KILL оправдан, когда процесс после разумного timeout не отвечает на TERM и подтверждён его PID. Он не исправляет непрерываемое ожидание D: такой процесс завершится только когда вернётся из операции ядра.

Для группы процессов service manager надёжнее управлять unit, чем посылать сигнал одному случайному worker. Иначе supervisor может сразу запустить замену.

Ошибка и диагностика

Опасная ошибка — pkill -f app с широким pattern. Сначала выполните pgrep -a -f 'pattern', подтвердите пользователей, parent и число кандидатов. После TERM наблюдайте status service, журналы и timeout. Если понадобился KILL, сохраните причину зависания: stack, state, открытые файлы или хотя бы последние логи до уничтожения процесса.

Практика

Создайте Bash-скрипт с временным файлом и trap на EXIT/TERM. Запустите его в фоне, отправьте TERM и проверьте удаление файла. Затем повторите с KILL и объясните, почему cleanup не выполнился. Удалите оставшийся fixture вручную по точному пути.

Что важно запомнить

Источники