Как выполнить мягкое (graceful) завершение процесса в Linux?

Ответ

Мягкое завершение — это отправка сигнала, который процесс может перехватить и корректно обработать (закрыть файлы, завершить транзакции, сохранить состояние). Основной сигнал для этого — SIGTERM (сигнал 15).

Стандартная последовательность действий:

  1. Найти PID процесса:

    ps aux | grep -i <process_name>
    # Или с помощью pgrep
    pgrep -f <process_name>
  2. Отправить SIGTERM:

    kill -15 <PID>
    # Или по имени
    pkill -15 -f <process_name>

    После этого у процесса есть время на завершение (обычно это 30 секунд, но зависит от приложения).

  3. Если SIGTERM не сработал — SIGINT (2): Это сигнал, аналогичный нажатию Ctrl+C в терминале.

    kill -2 <PID>
  4. Только в крайнем случае — SIGKILL (9): Сигнал SIGKILL нельзя перехватить или проигнорировать. Я использую его, только если процесс не реагирует на мягкие сигналы, понимая риски (потеря данных, неконсистентное состояние).

    kill -9 <PID>

Практический пример для веб-сервера (Nginx):

# Graceful shutdown Nginx (завершит обработку текущих соединений)
sudo nginx -s quit
# Эта команда внутри отправляет SIGQUIT мастер-процессу

# Альтернативно, если знаем PID мастера:
sudo kill -QUIT $(cat /var/run/nginx.pid)

Важно для скриптов: В bash-скриптах, которые запускают долгоживущие процессы, я использую trap для обработки сигналов завершения и очистки.

#!/bin/bash
cleanup() {
  echo "Получен сигнал завершения, останавливаем дочерние процессы..."
  kill -TERM $child_pid 2>/dev/null
}
trap cleanup TERM INT

# Запуск основного процесса в фоне
./my_long_running_process &
child_pid=$!

# Ожидание завершения
wait $child_pid

Ответ 18+ 🔞

А, ну это ж классика, ебать мои старые костыли! Сигналы в линуксе — это как общение с упрямым соседом. Сначала вежливо попросить, потом наорать, а в самом крайнем случае — вышибить дверь ногой.

Вот смотри, как правильно процесс уговаривать лечь и не дышать, чтобы он не накосячил.

Правильный подход, как с дамой:

  1. Сначала ищем, с кем будем беседовать (PID):

    ps aux | grep -i <process_name>
    # Или так, покороче
    pgrep -f <process_name>
  2. Вежливая просьба (SIGTERM, он же 15):

    kill -15 <PID>
    # Или сразу по имени, если лень цифры вводить
    pkill -15 -f <process_name>

    Тут процессу говорят: «Дружище, закругляйся, но не спеши». У него есть время (ну, секунд 30 обычно) всё красиво прибрать — файлы закрыть, транзакции завершить. Если он адекватный, то сделает и сам выключится.

  3. Если вежливость не работает — уже строже (SIGINT, он же 2): Это как нажать Ctrl+C в терминале. Более жёсткое, но ещё не смертельное указание.

    kill -2 <PID>
  4. Ну а если процесс совсем охренел и не реагирует — тогда уже пуля в лоб (SIGKILL, он же 9): Вот это уже не просьба, а приговор. Сигнал SIGKILL нельзя перехватить или проигнорировать. Просто херак — и нет процесса. Но и последствия могут быть пиздец: данные не сохранились, файлы остались открытыми, состояние системы — как после буйной пьянки. Использую только в самом крайнем случае, когда терпения ноль ебать.

    kill -9 <PID>

Пример из жизни, с веб-сервером Nginx:

# Красиво просим Nginx завершиться (дождаться текущих запросов)
sudo nginx -s quit
# Внутри эта команда шлёт мастер-процессу сигнал SIGQUIT

# Или можно вручную, если знаем PID мастера:
sudo kill -QUIT $(cat /var/run/nginx.pid)

Важный лайфхак для скриптов: Если пишешь скрипт, который что-то запускает, сделай ему «совесть». Чтобы он, если его самого прервут, успел прибраться за своими детьми.

#!/bin/bash
cleanup() {
  echo "Нас прерывают, чувак! Гасим всё, что запустили..."
  kill -TERM $child_pid 2>/dev/null
}
# Вешаем ловушку на сигналы TERM и INT
trap cleanup TERM INT

# Запускаем наш долгий процесс в фоне
./my_long_running_process &
child_pid=$!

# И ждём, пока он не кончится
wait $child_pid

Вот так-то, ёпта. Сначала договаривайся, а уж потом ломай. Иначе можно такого наворотить, что потом неделю расхлёбывать.