Демоны и фоновые процессы
Демоны и фоновые процессы: setsid, nohup, disown.
Что такое демон
Демон — это фоновый процесс, отвязанный от терминала и живущий долго. Веб-сервер, cron, sshd запускаются один раз при старте системы и работают, пока их не остановят. У такого процесса нет управляющего терминала: некому нажать Ctrl+C, некуда писать вывод.
Отвязку от терминала легко увидеть по колонке TTY в ps. У обычной
команды там номер терминала, у демона стоит знак вопроса:
$ ps -o pid,tty,comm -C sshd
PID TT COMMAND
742 ? sshd
Знак ? означает, что процесс не привязан ни к какому терминалу. Закрытие
любой сессии его не заденет.
Как процесс становится демоном
Классическая схема запуска состоит из трёх шагов. Сначала fork создаёт
потомка, а родитель сразу завершается. Потом потомок вызывает setsid и
открывает новую сессию, где у него больше нет управляющего терминала. В
конце процесс сбрасывает рабочую директорию на /, чтобы не держать занятой
случайную точку монтирования.
Команда setsid делает главный шаг вручную:
setsid ./worker.sh # запустить в новой сессии, без управляющего терминала
Процесс, запущенный так, сразу получает ? в колонке TTY и не зависит от
shell, из которого его позвали.
Амперсанд делает фоновым, но не отвязывает
Запуск через амперсанд (&) отправляет процесс в фон и возвращает
управление shell. Но потомок остаётся привязан к shell:
$ ./long-script.sh &
[1] 4211
$ jobs
[1]+ Running ./long-script.sh &
Здесь проблема. При закрытии терминала shell получает SIGHUP (сигнал
«терминал закрылся») и тянет потомков за собой. Мнение «& переживёт выход
из ssh» ошибочно: без дополнительных мер процессу прилетит SIGHUP и он
умрёт вместе с сессией.
nohup и disown
Чтобы процесс пережил закрытие сессии, есть два инструмента. nohup
запускает команду с игнорированием SIGHUP и перенаправляет вывод в файл
nohup.out:
$ nohup ./long-script.sh &
[1] 4230
nohup: ignoring input and appending output to 'nohup.out'
$ jobs
[1]+ Running nohup ./long-script.sh &
disown работает с уже запущенной задачей: он убирает её из списка заданий
shell, и при выходе SIGHUP ей не отправляется.
./long-script.sh & # запустили обычным фоном
disown %1 # отвязали задачу 1 от shell
Одна тонкость: disown встроен в bash и zsh, а в dash (это /bin/sh на
Debian) его нет. Там обходятся nohup или setsid.
На проде systemd, а не nohup
Долгоживущие сервисы на nohup не держат. За таким процессом никто не следит
и не перезапускает его при падении, вывод копится в одном файле, а после
перезагрузки хоста ничего не поднимется. Вместо этого сервис оформляют как
systemd-юнит, который сам стартует его при загрузке, ловит падения и
пишет логи в journal.
systemctl status myapp # состояние сервиса, следит ли systemd за ним
nohup и & остаются для разовых задач: прогнать долгий скрипт и уйти с
хоста, не дожидаясь конца.
Ещё о сигналах и демонах
Пара фактов, которые пригодятся при работе с фоновыми процессами.
SIGKILL и SIGSTOP — единственные два сигнала, которые нельзя
перехватить, заблокировать или проигнорировать. Все остальные процесс
вправе обработать по-своему.
У каждого сигнала есть действие по умолчанию, если процесс его не перехватил. Всего таких действий пять:
| Действие | Что делает |
|---|---|
| Term | завершить процесс |
| Ign | проигнорировать сигнал |
| Core | завершить и сбросить core dump |
| Stop | приостановить процесс |
| Cont | продолжить приостановленный |
SIGCHLD по умолчанию как раз попадает в Ign: ядро шлёт его родителю,
когда потомок завершился, но родитель волен его не замечать. Если родитель
не вызовет wait(), потомок останется зомби и займёт запись в таблице
процессов (номер PID), а не память.
Осиротевший процесс, у которого умер родитель, усыновляет init (PID 1) и
делает за него wait(), убирая зомби из таблицы. С ядра 3.4 роль
усыновителя вместо init можно передать специально назначенному
процессу-subreaper.
Что важно запомнить
Демон — фоновый процесс без управляющего терминала; в ps его выдаёт ? в
колонке TTY.
Схема запуска: fork, setsid (новая сессия без терминала), сброс рабочей
директории на /.
& делает процесс фоновым, но он остаётся потомком shell и получит
SIGHUP при закрытии терминала.
nohup игнорирует SIGHUP и пишет вывод в nohup.out; disown отвязывает
уже запущенную задачу (нет в dash).
Долгий сервис оформляют systemd-юнитом, а не держат на nohup.
SIGKILL и SIGSTOP перехватить нельзя; зомби держит запись в таблице
процессов, а не память.
В следующем уроке — наблюдение за процессами: ps, lsof и strace.
Дрилл · вопросы как на собесе
Проверь себя без подсказок
Сформулируй ответ вслух или письменно, потом раскрой и честно отметь: знал или нет.
Что такое демон и как процесс отвязывают от терминала?
LIN-133Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс