Вопрос или проблема
При запуске команды, работающей в фоновом режиме по SSH из неинтерактивного shell-скрипта, я заметил, что процесс продолжает работать на удаленной машине без использования nohup
, disown
или аналогичных инструментов.
Удаленная среда (цель SSH):
- Linux 6.12.9
- OpenSSH 9.9p1, OpenSSL 3.3.2
- Логин-шелл: bash 5.2.37
- Также для неинтерактивных сессий (проверено с помощью
ssh -T $HOST "echo \$SHELL"
)
- Также для неинтерактивных сессий (проверено с помощью
- Дистрибутив: NixOS 24.11
На стороне клиента я могу выполнить:
# Закрытие выходящих файловых дескрипторов (stdout и stderr) важно для
# немедленного завершения сеанса SSH (EOF). Нам также нужна
# неинтерактивная сессия (-T).
ssh -T $HOST "long_running_command >/dev/null 2>/dev/null &"
чтобы запустить команду, работающую долго, на удаленной машине, не оставляя SSH-сеанс активным.
Я ожидал, что фоновые задания завершатся или получат SIGHUP, когда сеанс SSH завершится. Однако процесс автоматически переподчиняется PID 1 (init) и продолжает работать. Я могу проверить это с помощью htop
, ps
и т. д.
Почему это работает без nohup
или disown
?
- Почему это просто работает так? Почему никакие
SIGHUP
или аналогичные события не отправляютсяlong_running_command
? - Почему управление заданиями (
&
) работает в bash в неинтерактивном режиме? - Кто решает, что выполняющееся фоновое задание сменит владельца на процесс init? Bash? Это задокументировано?
Ответ или решение
Вопрос о том, как командные процессы, запущенные в фоновом режиме через SSH, продолжают выполняться после завершения сеанса, даже без использования nohup
или disown
, является интересным и затрагивает некоторые аспекты управления процессами в Linux.
Теория
Когда вы запускаете команду через SSH, вы создаете удаленный сеанс, в котором исполняется ваш процесс. В интерактивных сессиях bash обычно привязывает фоновые процессы к сеансу, и они получают сигнал SIGHUP при разрыве связи. Этот сигнал указывает процессу завершить работу, что делает nohup
полезным, так как он предотвращает отправку сигнала SIGHUP процессу.
Однако в случае, описанном вами, процесс продолжается без nohup
. Это обусловлено несколькими факторами. Как вы правильно отметили, SSH-сессия запускается в неинтерактивном режиме (ssh -T
), отключает стандартные потоки вывода и ошибок (>/dev/null 2>/dev/null
), и помещает процесс в фоновый режим (&
). Когда процесс запущен в неинтерактивной среде, которая не управляет терминалом, то эти фоновые процессы становятся слабо привязанными к сессии.
Когда SSH сессия завершается, все порожденные функции оболочки (например, bash session
) закрываются, и любые фоновые процессы, не привязанные к терминалу, автоматически переподчиняются процессу init
(инициализации, с PID 1). Это стандартное поведение для всех неуправляемых процессов в Unix/Linux системах.
Пример
Допустим, вы запускаете long_running_command
на удаленной машине с помощью SSH:
ssh -T $HOST "long_running_command >/dev/null 2>/dev/null &"
Здесь long_running_command
выполняется в фоне (специальный символ &
). Более важно то, что в этом случае не используется интерактивная оболочка. После того, как команда была запущена и SSH сессия завершилась, процесс отцепляется от терминала. Из-за отсутствия привязки к интерактивному терминалу, процесс больше не получает SIGHUP, и система переподчиняет его процессу init
.
Применение
Есть несколько важнейших аспектов этого поведения, которые можно использовать при работе с процессами:
-
Использование Non-Interactive SSH: Если у вас есть желание избежать простоя процессов, следует использовать неинтерактивный запуск SSH с целью эвентуального отцепления процессов.
-
Закрытие потоков вывода/ошибок: При закрытии стандартных потоков вывода и ошибок с использованием
>/dev/null 2>/dev/null
, мы гарантируем, что процесс не будет зависеть от этих каналов, что также способствует его автономности после завершения сессии. -
Понимание Переподчинения Процессов: Когда процессы становятся "сиротами" (то есть процесс-родитель завершился), они автоматически подбираются init-системой, что делает их независимыми от начальной оболочки, в которой они были созданы.
Документация по поведению процессов в bash и SSH может быть полезна для глубокого понимания этого поведения. Некоторые элементы этого поведения описаны в документации к bash, особенно касательно управления работами (job control) и сигналами. Пользователи систем на основе Unix должны быть осведомлены о роли init-процесса и своей способности "усыновлять" сиротские процессы, сохраняя их работающими.
Таким образом, если вы часто занимаетесь запуском длительных команд через SSH, понимание вышеописанных аспектов поможет вам организовать ваши задачи более эффективно и предсказуемо.