Как SSH-запущенные долгосрочные фоновые задания отсоединяются без помощи nohup или disown?

Вопрос или проблема

При запуске команды, работающей в фоновом режиме по 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.

Применение

Есть несколько важнейших аспектов этого поведения, которые можно использовать при работе с процессами:

  1. Использование Non-Interactive SSH: Если у вас есть желание избежать простоя процессов, следует использовать неинтерактивный запуск SSH с целью эвентуального отцепления процессов.

  2. Закрытие потоков вывода/ошибок: При закрытии стандартных потоков вывода и ошибок с использованием >/dev/null 2>/dev/null, мы гарантируем, что процесс не будет зависеть от этих каналов, что также способствует его автономности после завершения сессии.

  3. Понимание Переподчинения Процессов: Когда процессы становятся "сиротами" (то есть процесс-родитель завершился), они автоматически подбираются init-системой, что делает их независимыми от начальной оболочки, в которой они были созданы.

Документация по поведению процессов в bash и SSH может быть полезна для глубокого понимания этого поведения. Некоторые элементы этого поведения описаны в документации к bash, особенно касательно управления работами (job control) и сигналами. Пользователи систем на основе Unix должны быть осведомлены о роли init-процесса и своей способности "усыновлять" сиротские процессы, сохраняя их работающими.

Таким образом, если вы часто занимаетесь запуском длительных команд через SSH, понимание вышеописанных аспектов поможет вам организовать ваши задачи более эффективно и предсказуемо.

Оцените материал
Добавить комментарий

Капча загружается...