← Linux: процессы вглубь
12 мин · средне · Урок 5 из 7

Демоны и фоновые процессы

Демоны и фоновые процессы: 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.

Дрилл · вопросы как на собесе

Проверь себя без подсказок

Сформулируй ответ вслух или письменно, потом раскрой и честно отметь: знал или нет.

01

Что такое демон и как процесс отвязывают от терминала?

LIN-133

Прошли материал до конца? Отметьте урок пройденным.