Процессы и сигналы
PID, ps/top, что значат SIGTERM и SIGKILL, как корректно остановить процесс.
Что такое процесс
Процесс — это запущенная программа: код + выделенная ей память + открытые файлы + контекст выполнения. Один и тот же бинарник можно запустить десять раз, получится десять разных процессов.
У каждого процесса есть номер — PID (process id). PID 1 особый:
это самый первый процесс, которого запускает ядро при старте системы
(обычно systemd). От него деревом расходятся все остальные.
Процессы рождаются не из воздуха: один процесс (родитель) порождает другой (потомок). У потомка есть PPID — PID его родителя. Так получается дерево: терминал запустил твой shell, а shell запускает скрипты, которые ты в нём вызываешь.
Чем смотреть процессы
ps aux # снимок всех процессов разом
ps aux | grep nginx # отфильтровать по имени
top # живой монитор, по умолчанию обновляется каждые 3 секунды
htop # то же, но удобнее (нужно ставить отдельно)
pgrep -a nginx # PID-ы по имени + командная строка
Так выглядит вывод ps aux:
$ ps aux
USER PID %CPU %MEM VSZ RSS STAT START TIME COMMAND
root 1 0.0 0.1 168240 11500 Ss 09:00 0:02 /sbin/init
www 812 1.3 2.4 720160 98400 Sl 09:01 0:21 nginx: worker
deploy 1455 0.0 0.0 12100 3200 R+ 10:14 0:00 ps aux
Полезные колонки: PID — номер процесса, %CPU/%MEM — нагрузка, RSS —
реально занятая память в КБ, STAT — состояние (R, S, D, Z, T),
COMMAND — что запущено. В top сверху видно load average и занятую память.
Состояния процесса (колонка STAT):
R— выполняется или готов выполняться,S— спит, ждёт события (нормальное состояние для большинства),D— непрерываемый сон, обычно ждёт диск (тревожный знак, если надолго),Z— зомби (см. ниже),T— остановлен (например, по Ctrl+Z).
Сигналы
Сигнал — асинхронное уведомление, которое ядро (или другой процесс)
доставляет процессу. Отправляются командой kill <PID> (несмотря на
название, kill — это «послать сигнал», не обязательно убить).
Самые важные три:
| Сигнал | Номер | Что значит |
|---|---|---|
SIGTERM | 15 | "заверши работу красиво". Сигнал по умолчанию у kill |
SIGKILL | 9 | "умри немедленно". Процесс не может его перехватить |
SIGHUP | 1 | "перечитай конфиг" (по соглашению у демонов) или "терминал закрылся" |
kill 1234 # послать SIGTERM процессу 1234
kill -9 1234 # послать SIGKILL
kill -HUP 1234 # послать SIGHUP (перечитать конфиг)
pkill nginx # по имени, а не по PID
Почему kill -9 — последнее средство
SIGTERM процесс может перехватить и обработать: дописать данные на
диск, закрыть соединения с БД, корректно завершить запросы, удалить
временные файлы. Это называется graceful shutdown.
SIGKILL (-9) перехватить нельзя: ядро убивает процесс мгновенно.
Последствия:
- незаписанные данные теряются,
- соединения и файлы остаются открытыми до подчистки ядром,
- может остаться битый state (полузаписанный файл, lock, который не сняли).
Правильный порядок: сначала SIGTERM, подожди (на практике 5–30 секунд,
смотря по сервису), и только если процесс завис намертво, шли SIGKILL. Так
работают и docker stop, и systemctl stop, только таймауты у них разные.
docker stop даёт 10 секунд, меняется флагом -t. У systemd отсчёт
задаёт TimeoutStopSec= в юните, а по умолчанию действует
DefaultTimeoutStopSec из /etc/systemd/system.conf, там 90 секунд.
Отсюда типичная жалоба «перезапуск сервиса висит полторы минуты»: сервис
не отвечает на SIGTERM, и systemd честно ждёт свой таймаут, прежде чем
дослать SIGKILL.
Зомби-процессы
Когда процесс завершается, он не исчезает мгновенно: остаётся запись в
таблице процессов, пока родитель не "заберёт" его код возврата вызовом
wait(). Такой завершённый, но не убранный процесс — зомби (Z в
ps).
Зомби не ест ресурсов, он уже мёртв. Занят только номер в таблице. Но
если родитель кривой и не делает wait(), зомби копятся и могут забить
таблицу PID-ов.
Лечится перезапуском родителя. Когда родитель умирает, его потомков
усыновляет процесс с PID 1. Ядро шлёт PID 1 сигнал SIGCHLD, тот делает
wait(), и зомби убирается из таблицы.
Фоновые процессы
./long-script.sh & # запустить в фоне, вернуть управление shell
jobs # список фоновых задач текущего shell
fg %1 # вернуть задачу 1 на передний план
Проблема: фоновый процесс остаётся потомком shell. Закроешь терминал, и
shell получит SIGHUP и потянет потомков за собой. Чтобы процесс пережил
закрытие сессии:
nohup ./long-script.sh & # игнорировать SIGHUP, лог в nohup.out
disown %1 # отвязать уже запущенную задачу от shell
disown — встроенная команда bash/zsh; в dash (это /bin/sh на Debian)
её нет, там обходятся nohup и setsid.
На проде для долгоживущих сервисов так не делают: их оформляют как
systemd-юнит, который сам следит за процессом и перезапускает при
падении. nohup и & годятся для разовых задач.
Что важно запомнить
Процесс = запущенная программа со своим PID. Дерево растёт от PID 1.
Смотреть процессы: ps aux, top/htop, pgrep.
SIGTERM (15) — заверши красиво, SIGKILL (9) — убей насмерть,
перехватить нельзя. Сначала TERM, KILL только как крайняя мера.
Зомби — завершённый процесс, которого не убрал родитель. Лечится перезапуском родителя.
Для долгого сервиса нужен systemd-юнит, а не nohup &.
В следующем уроке разберём права доступа: кто и что может делать с файлами.
Когда база уляжется, продолжение в треке «Linux: процессы вглубь»: fork и exec, состояния процессов, зомби и strace.
Дрилл · вопросы как на собесе
Проверь себя без подсказок
Сформулируй ответ вслух или письменно, потом раскрой и честно отметь: знал или нет.
Для чего нужны сигналы? Перечисли 5–10 самых частых.
LIN-010Что делает команда kill? Какие сигналы можно слать?
LIN-026В чём разница между Ctrl+C и Ctrl+Z? Какой сигнал получит активный процесс?
LIN-035Как обрабатываются сигналы? Чем SIGTERM отличается от SIGKILL?
LIN-057Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс