Завершение работы системы
TL;DR: Shutdown — это загрузка наоборот. init просит процессы завершиться, неответивших убивает SIGTERM → SIGKILL, затем демонтирует файловые системы, перемонтирует корневую в read-only, записывает буферы на диск (
sync) и вызываетreboot(2)для выключения или перезагрузки.
Зачем это знать
Некорректное завершение (выдернуть питание, убить PID 1) — это потерянные данные в буферах, повреждённые файловые системы и сервисы, которые не сохранили состояние. Понимание процесса помогает:
- Диагностировать зависание при shutdown (какой сервис не отвечает на SIGTERM)
- Настраивать graceful shutdown для своих приложений
- Понимать почему
kill -9— крайняя мера, а не первая
Последовательность завершения
Независимо от реализации init (systemd, SysVinit), процедура выполняется в следующем порядке:
initпросит каждый процесс полностью завершиться- Если процесс не отвечает через некоторое время,
initпринудительно завершит его, сначала применив сигналTERM - Если сигнал
TERMне работает,initиспользует сигналKILLдля любых оставшихся активными процессов - Система блокирует системные файлы на своих местах и выполняет остальные приготовления к завершению работы
- Система демонтирует все файловые системы, кроме корневой
- Система заново монтирует корневую файловую систему в режиме только для чтения
- Система записывает все буферизованные данные в файловую систему с помощью программы
sync - На последнем шаге система указывает ядру перезагрузиться или остановиться с помощью системного вызова
reboot(2). Это может быть сделано посредствомinitили вспомогательной программы, напримерreboot,haltилиpoweroff
В systemd это означает активацию юнитов выключения (poweroff.target, reboot.target). В System V — изменение уровня выполнения на 0 (остановка) или 6 (перезагрузка).
Ключевые этапы
1. Команда пользователя → init
# Все три способа эквивалентны:
sudo shutdown -h now # классическая команда
sudo systemctl poweroff # через systemctl (systemd)
sudo poweroff # обёрткаshutdown сообщает init о начале процесса завершения работы. В systemd это активация юнитов выключения, в System V — смена runlevel.
| Команда | systemd target | SysV runlevel | Результат |
|---|---|---|---|
poweroff | poweroff.target | 0 | Остановка + выключение питания |
reboot | reboot.target | 6 | Остановка + перезагрузка |
halt | halt.target | 0 | Остановка CPU, питание не выключается |
2–3. Корректное завершение → SIGTERM → SIGKILL
init просит каждый процесс корректно завершиться. Если процесс не отвечает — отправляет SIGTERM. Если SIGTERM не помогает — SIGKILL.
В systemd это реализовано так: для каждого сервиса выполняется ExecStop= из unit-файла, а если не задан — отправляется SIGTERM. Таймаут ожидания между SIGTERM и SIGKILL определяется TimeoutStopSec (по умолчанию DefaultTimeoutStopSec=90s в /etc/systemd/system.conf).
# Пример: сервис с кастомным graceful shutdown
[Service]
ExecStart=/usr/bin/myapp
ExecStop=/usr/bin/myapp --graceful-stop # кастомная остановка
TimeoutStopSec=30 # ждать 30с вместо дефолтных 90Важно: Приложение, которое перехватывает SIGTERM и корректно завершается (закрывает соединения к БД, дописывает логи) — это graceful shutdown. Приложение, которое игнорирует SIGTERM — получит SIGKILL через таймаут и потеряет несохранённые данные. SIGKILL процесс не может перехватить.
4–7. Файловые системы и sync
После уничтожения всех пользовательских процессов:
- Система блокирует системные файлы и выполняет приготовления к завершению
- Демонтирует все файловые системы, кроме корневой
- Корневая ФС перемонтируется в режиме read-only
sync— запись всех буферизованных данных на диск
Именно поэтому «выдернуть питание» опасно: данные в буферах ядра, ещё не записанные на диск, будут потеряны. Journaling-ФС (ext4, XFS) минимизируют повреждения, но гарантируют целостность только метаданных — не пользовательских данных.
8. Системный вызов reboot(2)
На последнем шаге система указывает ядру перезагрузиться или остановиться с помощью системного вызова reboot(2). Это может быть сделано самим init или вспомогательной программой (reboot, halt, poweroff).
shutdown с задержкой и уведомлением
# Выключить через 10 минут с сообщением пользователям
sudo shutdown -h +10 "Server going down for maintenance"
# Перезагрузка в конкретное время
sudo shutdown -r 23:00 "Scheduled reboot at 23:00"
# Отменить запланированный shutdown
sudo shutdown -c "Maintenance postponed"shutdown с задержкой отправляет wall-сообщение всем залогиненным пользователям и создаёт файл /run/nologin, который запрещает новые логины (кроме root).
Как это работает на практике
Graceful shutdown для Node.js приложения
// Перехват SIGTERM — корректное завершение
process.on('SIGTERM', async () => {
console.log('SIGTERM received, shutting down gracefully...');
// 1. Перестать принимать новые соединения
server.close();
// 2. Дождаться завершения текущих запросов
await activeConnections.drain();
// 3. Закрыть подключение к БД
await db.disconnect();
// 4. Выход с кодом 0 (успех)
process.exit(0);
});Systemd unit с корректным shutdown
[Service]
ExecStart=/usr/bin/node /opt/app/server.js
# KillSignal по умолчанию SIGTERM — его и перехватываем
TimeoutStopSec=30 # даём 30с на graceful shutdown
KillMode=mixed # SIGTERM главному процессу,
# SIGKILL дочерним после таймаутаKillMode определяет кому отправляется сигнал:
| KillMode | Поведение |
|---|---|
control-group (default) | Сигнал всем процессам в cgroup |
mixed | SIGTERM главному, SIGKILL остальным после таймаута |
process | Сигнал только главному процессу |
none | systemd не убивает процессы (опасно) |
Подводные камни
Важно: Если сервер «зависает при shutdown» — причина почти всегда в сервисе с длинным
TimeoutStopSecили процессе, игнорирующем SIGTERM.
| Проблема | Симптом | Решение |
|---|---|---|
| Shutdown висит долго | «A stop job is running for…» | Найти сервис: systemctl list-jobs. Уменьшить TimeoutStopSec в unit-файле или исправить обработку SIGTERM в приложении |
| Данные теряются при перезагрузке | Приложение не обрабатывает SIGTERM | Добавить обработчик сигнала + graceful shutdown |
| ФС повреждена после сбоя питания | fsck при загрузке | Использовать journaling FS (ext4/XFS), UPS |
halt не выключает машину | Экран зависает на «System halted» | Использовать poweroff вместо halt — halt не отправляет ACPI сигнал выключения |
| NFS/сетевые ФС не размонтируются | Shutdown зависает на umount | _netdev в fstab → systemd размонтирует до остановки сети |
Связанные материалы
- systemd — targets, units, процесс загрузки
- process-model — PID, сигналы, состояния процессов
- manage-services — systemctl start/stop/enable
- systemd-reference — справочник по systemctl, journalctl