2015-09-03 1 views
10

Недавно я столкнулся с немного странным поведением при запуске команд над ssh. Мне было бы интересно услышать какие-либо объяснения поведения ниже.Почему выполняется фоновое задание с ошибкой ssh ​​при назначении псевдо-tty?

Запуск ssh localhost 'touch foobar &' создает файл с именем foobar, как ожидалось:

[[email protected] ~]$ ssh localhost 'touch foobar &' 
[[email protected] ~]$ ls foobar 
foobar 

Однако работает та же команда, но с -t опцией, чтобы заставить выделение псевдо-терминальную не удается создать foobar:

[[email protected] ~]$ ssh -t localhost 'touch foobar &' 
Connection to localhost closed. 
[[email protected] ~]$ echo $? 
0 
[[email protected] ~]$ ls foobar 
ls: cannot access foobar: No such file or directory 

Мои Текущая теория состоит в том, что, поскольку процесс касания задается, псевдо-tty выделяется и нераспределяется до того, как процесс имеет шанс запустить. Конечно, добавив один второй сон позволяет штрихом работать, как ожидалось:

[[email protected] ~]$ ssh -t localhost 'touch foobar & sleep 1' 
Connection to localhost closed. 
[[email protected] ~]$ ls foobar 
foobar 

Если кто-то имеет однозначного объяснения, я был бы очень интересно услышать его. Благодарю.

+0

Я думаю, что это именно он. Фоновый процесс связан с tty, и tty dying убивает его. Попробуйте «nohup touch foobar &» и посмотрите, работает ли это и/или «коснитесь foobar/dev/null 2> & 1'. –

+0

Оба «ssh -t localhost» touch foobar < /dev/null >/dev/null 2> & 1 & ''и' ssh -t localhost' nohup touch foobar & ''производят такое же поведение. – user414310

+0

Не удается создать файл? Я получаю файл, созданный здесь даже для оригинальной версии для записи. –

ответ

22

О, это хорошо.

Это связано с тем, как работают группы процессов, как работает bash при вызове в качестве неинтерактивной оболочки с -c и эффектом & в командах ввода.

Ответ предполагает, что вы знакомы с тем, как работает управление заданиями в UNIX; если вы этого не сделаете, вот представление высокого уровня: каждый процесс принадлежит к группе процессов (процессы в той же группе часто помещаются там как часть командного конвейера, например, cat file | sort | grep 'word' будет размещать процессы с cat(1), sort(1) и grep(1) в одна и та же группа процессов). bash - это процесс, подобный любому другому, и он также относится к группе процессов. Группы процессов являются частью сеанса (сеанс состоит из одной или нескольких групп процессов). В сеансе имеется не более одной группы процессов, называемой группой процессов переднего плана и, возможно, многими группами фонового процесса. Группа процессов переднего плана управляет терминалом (если имеется терминал управления, подключенный к сеансу); руководитель сеанса (bash) перемещает процессы от фона на передний план и от переднего плана до фона с tcsetpgrp(3). Сигнал, отправленный в группу процессов, доставляется каждому процессу в этой группе.

Если концепция групп процессов и управления заданиями является совершенно новой для вас, я думаю, вам нужно будет прочитать об этом, чтобы полностью понять этот ответ. Огромный ресурс для изучения этого - глава 9 of Расширенное программирование в среде UNIX (3-е издание).

Это, как говорится, давайте посмотрим, что здесь происходит. Мы должны скомпоновать каждый кусочек головоломки.

В обоих случаях удаленная сторона ssh вызывает bash(1) с -c. Флаг -c вызывает bash(1) для запуска в качестве неинтерактивной оболочки. Из: страница руководства

Интерактивная оболочка один запускается без аргументов без опций и без опции -c которой стандартный ввод и ошибок оба подключены к клеммам (как определено isatty (3)), или один запустил с опцией -i.PS1 установлен, а $ - включает i, если bash имеет значение , что позволяет сценарию оболочки или загрузочному файлу протестировать это состояние .

Кроме того, важно знать, что управление заданиями отключено, когда Баш запускается в режиме неинтерактивного. Это означает, что bash не будет создавать отдельную группу процессов для запуска команды, поскольку управление заданиями отключено, нет необходимости перемещать эту команду между передним и задним фонами, поэтому она может просто оставаться в той же группе процессов, что и bash , Это произойдет, если вы принудительно назначили PTY на ssh с -t.

Однако использование & имеет побочный эффект, из-за которого оболочка не ожидает завершения команды (даже если управление заданиями отключено). Из: страница руководства

Если команда завершается оператором & управления, оболочка выполняет команду в фоновом режиме в подоболочки. Оболочка делает , не дожидаясь завершения команды, а статус возврата равен 0. Команды, разделенные символом a; выполняются последовательно; оболочка ждет для каждой команды, чтобы завершить по очереди. Статус возврата - это выход статус последней выполненной команды.

Таким образом, в обоих случаях, Баш не будет ждать выполнения команды, и touch(1) будет выполнен в той же группе процессов, как bash(1).

Теперь подумайте, что произойдет, когда лидер сеанса выйдет. Цитируя setpgid(2) страницы руководства:

Если сеанс имеет контрольный терминал, а CLOCAL флаг для этого терминала не установлен, и терминал зависание происходит, то лидер сеанса отправляется SIGHUP. Если лидер сеанса выйдет, то сигнал SIGHUP также будет отправлен каждому процессу в процессе переднего плана группы управляющего терминала.

(выделено мной)

Когда вы не используете -t

Когда вы не используете -t, нет выделения PTY на удаленной стороне, так Баш не и фактически не создается новый сеанс. Поскольку sshd работает как демон, процесс bash, который forked + exec() 'd, не будет иметь управляющий терминал. Таким образом, даже если оболочка завершается очень быстро (вероятно, до touch(1)), нет SIGHUP, отправленных в группу процессов, потому что bash не был лидером сеанса (и нет управляющего терминала). Так что все работает.

При использовании -t

-t силы распределения PTY, что означает, что SSH удаленная сторона будет вызывать setsid(2), выделить псевдо-терминал + вилка новый процесс с forkpty(3), соединить вход ведущего устройства PTY, и вывод на конечные точки сокета, которые приводят к вашей машине, и, наконец, выполнить bash(1). forkpty(3) открывает рабочую сторону PTY в раздвоенном процессе, который станет bash; поскольку нет контрольного терминала для текущего сеанса, и открывается терминальное устройство, устройство PTY становится управляющим терминалом для сеанса, а bash становится лидером сеанса.

Затем происходит то же самое: touch(1) выполняется в той же группе процессов и т. Д., Yadda yadda. Дело в том, что на этот раз является лидером сеанса и контрольным терминалом. Итак, поскольку bash не беспокоится о ожидании из-за &, когда он выходит, SIGHUP доставляется в группу процессов, а touch(1) умирает преждевременно.

О nohup

nohup(1) здесь не работает, потому что есть еще условие гонки. Если bash(1) прекращается до nohup(1) имеет возможность создать необходимую обработку сигнала и перенаправление файла, это не будет иметь никакого эффекта (что, вероятно, что происходит)

Возможное исправление

Насильно повторного включения управления заданиями исправляет его. В bash вы делаете это с set -m. Это работает:

ssh -t localhost 'set -m ; touch foobar &' 

Или сила Баш ждать touch(1) для завершения:

ssh -t localhost 'touch foobar & wait `pgrep touch`' 
+0

Спасибо за то, что вы дали такое четкое и подробное объяснение, очень ценили. – user414310

+1

@ user414310 Рад, что я мог помочь. Должен сказать, мне понравился этот вопрос. Спасибо за такой замечательный вопрос :) –

Смежные вопросы