Почему ssh ждет моих подоболочек без -t и убивает их с помощью -t?
У меня есть bash script start.sh, который выглядит следующим образом:
for thing in foo bar; do
{
background_processor $thing
cleanup_on_exit $thing
} &
done
Это делает то, что я хочу: я запускаю start.sh, он выходит с кодом 0, и две подоболочки работают в фоновом режиме. Каждая подоболочка работает background_processor
, и, когда она завершается, выполняется cleanup_on_exit
. Это работает, даже если я выхожу из терминала, с которого я изначально запускал start.sh(даже если это было ssh-соединение).
Затем я попробовал это:
ssh [email protected] "start.sh"
Это работает, за исключением того, что после выхода start.sh
ssh, судя по всему, также ждет завершения работы подоболочек. Я не понимаю, почему. Как только start.sh
завершается, подоболочки становятся дочерними элементами pid 1, и они даже не назначаются с помощью tty... поэтому я не могу понять, как они все еще связаны с моим ssh-соединением.
Я позже попытался:
ssh -t [email protected] "start.sh"
Теперь процессы имеют назначенное псевдо-tty. Теперь я обнаружил, что ssh завершает работу, как только start.sh
завершает работу, но также убивает дочерние процессы.
Я предположил, что дочерние процессы отправляются SIGHUP в последнем случае, поэтому я сделал это:
ssh -t [email protected] "nohup start.sh"
Это действительно работает! Итак, у меня есть решение моей практической проблемы, но я хотел бы понять тонкости материала SIGHUP/tty здесь.
В заключение, мои вопросы:
- Почему ssh (без -t) ждет, пока дочерний процесс завершится даже после завершения
start.sh
, хотя у них есть родительский pid 1?
- Почему ssh (с -t) убивает дочерние процессы, по-видимому, с SIGHUP, хотя этого не происходит, когда я запускаю их с терминала и выхожу из этого терминала?
Ответы
Ответ 1
Думаю, я могу объяснить это сейчас! Мне пришлось немного узнать, что такое сеансы и группы процессов, что я сделал, прочитав TTY Demystified.
- Почему ssh (без -t) ожидает, пока дочерний процесс будет работать даже после выхода start.sh, хотя у них есть родительский pid 1?
Поскольку без tty ssh подключается к stdin/stdout/stderr процесса оболочки через каналы (которые потом унаследованы дочерними элементами), а версия OpenSSH, которую я использую (OpenSSH_4.3p2), ждет эти сокеты закрыть перед выходом. Некоторые более ранние версии OpenSSH не вели себя так. Существует хорошее объяснение этому, с обоснованием, здесь.
И наоборот, при использовании интерактивного входа (или ssh -t
) ssh и процессы используют TTY, поэтому нет ожидающих труб.
Я могу восстановить поведение, которое я хочу, перенаправляя потоки. Этот вариант немедленно возвращается: ssh [email protected] "start.sh < /dev/null > /dev/null 2>&1"
- Почему ssh (с -t) убивает дочерние процессы, по-видимому, с SIGHUP, хотя этого не происходит, когда я запускаю их с терминала и выхожу из этого терминала?
Поскольку bash запускается в неинтерактивном режиме, что означает, что управление заданиями по умолчанию отключено, и, следовательно, дочерние процессы находятся в той же группе процессов, что и родительский процесс bash (который является лидером сеанса), Когда исходный процесс bash завершается, ядро отправляет SIGHUP в свою группу процессов (которая находится на переднем плане), как описано в setpgid(2)
:
Если сеанс имеет управляющий терминал,... [и] лимит сеанса завершается, сигнал SIGHUP будет отправляться каждому процессу в группе процесса переднего плана управляющего терминала.
И наоборот, при использовании интерактивного входа bash находится в интерактивном режиме, что означает, что управление заданием включено по умолчанию, и поэтому дочерние процессы переходят в отдельную группу процессов и никогда не получают SIGHUP при выходе.
Я могу восстановить поведение, которое я хочу, используя set -m
, чтобы включить управление заданиями в bash. Если я добавлю set -m
в start.sh
, дети больше не будут убиты при выходе ssh.
Тайны решены:)
Ответ 2
Я подозреваю (но Im постулирую), что, когда нет tty, bash передает SIGHUP в ваш разветвленный процесс, который обрабатывает сам сигнал и спокойно игнорирует его и продолжает связывать сеанс SSH.
Однако с tty между вами и процессом драйвер tty перехватывает SIGHUP, понимает, что он потерял пользователя, и запускает сам запуск без сеанса ssh в роли родителя.
Ответ 3
Подготовьте любой вызов, с которым вы не хотите, чтобы этот SIGHUP выполнялся с "nohup".