Подтвердить что ты не робот

Почему 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, хотя этого не происходит, когда я запускаю их с терминала и выхожу из этого терминала?
4b9b3361

Ответ 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"

  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".