SSH-соединения, работающие в фоновом режиме, не завершаются, если несколько соединений были запущены одной и той же оболочкой.

Как указывали другие здесь и на StackOverflow , это может быть просто удвоение y от имя утилиты yacc , используемой для создания уникального пространства имен.

Стандарт POSIX резервирует пространства имен, начинающиеся с yy и YY для yacc с следующим абзацем :

Имена имеют произвольную длину, состоят из букв, точек (. ), подчеркивания ( _ ) и неначальных цифр. Прописные и строчные буквы различны. Соответствующие приложения не должны использовать имена, начинающиеся с yy или YY , поскольку синтаксический анализатор yacc использует такие имена . Многие из имен появляются в окончательном выводе yacc , и поэтому они должны быть выбраны в соответствии с любыми дополнительными правилами, созданными компилятором C, которые будут использоваться. В частности, они появляются в операторах #define .

К сожалению, документы POSIX очень плохо отражают любую форму истории, объясняющую, почему все стало так, как есть в настоящее время.

6
03.08.2017, 03:16
3 ответа

Приоритетные процессы и управление доступом к терминалу

Чтобы понять, что происходит, вам нужно немного узнать о совместном использовании терминалов. Что происходит, когда две программы одновременно пытаются читать данные с одного и того же терминала? Каждый входной байт случайным образом попадает в одну из программ. (Не случайно, так как в ядре для принятия решения используется ГСЧ, а просто случайно, поскольку на практике это непредсказуемо. )То же самое происходит, когда две программы читают из канала или любого другого типа файла, который представляет собой поток байтов, перемещаемых из одного места в другое (сокет, символьное устройство, … ), а не байт массив, в котором любой байт может быть прочитан несколько раз (обычный файл, блочное устройство ). Например, запустите оболочку в терминале, узнайте имя терминала и запустите cat.

$ tty
/dev/pts/18
$ cat

Затем с другого терминала запустите cat /dev/pts/18. Теперь введите терминал и посмотрите, как строки иногда идут в один из процессов cat, а иногда в другой. Строки отправляются целиком, когда терминал находится в режиме приготовления.Если вы поместите терминал в необработанный режим, то каждый байт будет отправлен независимо.

Это грязно. Наверняка должен быть механизм, определяющий, что одна программа получает терминал, а другая нет. Ну, есть! Он срабатывает в типичных случаях, но не в сценарии, который я установил выше. Этот сценарий необычен, потому что cat /dev/pts/18не был запущен из /dev/pts/18. Необычно получить доступ к терминалу из программы, которая не была запущена внутри этого терминала. В обычном случае вы запускаете оболочку в терминале и запускаете программы из этой оболочки. Тогда правило состоит в том, что программа на переднем плане получает терминал, а программы на заднем плане — нет. Это известно как управление доступом к терминалу . Как это работает:

  • Каждый процесс имеет управляющий терминал(или не имеет его, как правило, потому, что у него нет дескриптора открытого файла, который является терминалом ).
  • Когда процесс пытается получить доступ к управляющему терминалу, если процесс не находится на переднем плане, ядро ​​блокирует его. (Применяются условия. Доступ к другим терминалам не регламентируется.)
  • Оболочка решает, кто является процессом переднего плана. (На самом деле группа процессов переднего плана. )Он вызывает tcsetpgrp, чтобы сообщить ядру, кто должен быть на переднем плане.

Это работает в типичных случаях. Запустите программу в оболочке, и эта программа станет процессом переднего плана. Запустите программу в фоновом режиме (с помощью &), и программа не будет работать на переднем плане. Когда оболочка отображает подсказку, она выводит себя на передний план. Когда вы возобновляете приостановленное задание с помощью fg, оно становится приоритетным. С bgэто не так.

Если фоновый процесс пытается прочитать данные с терминала, ядро ​​отправляет ему сигнал SIGTTIN. Действие сигнала по умолчанию — приостановить процесс (, например SIGSTOP ).Родитель процесса может узнать об этом, вызвавwaitpidс флагом WSTOPPED; когда дочерний процесс получает сигнал, который приостанавливает его, вызов waitpidв родительском процессе возвращается и сообщает родителю, что это был за сигнал. Вот как оболочка знает, что нужно напечатать «Stopped (tty input )». Это говорит вам, что эта работа приостановлена ​​​​из-за SIGTTIN.

Поскольку процесс приостановлен, с ним ничего не произойдет, пока он не будет возобновлен или уничтожен (с сигналом, который процесс не перехватывает, потому что, если процесс установил обработчик сигнала, он не запустится, поскольку процесс приостановлен ). Вы можете возобновить процесс, отправив ему SIGCONT, но это ничего не даст, если процесс читает с терминала, он немедленно получит другой SIGTTIN. Если вы возобновите процесс с помощью fg, он перейдет на передний план, и чтение будет выполнено успешно.

Теперь вы понимаете, что происходит, когда вы запускаете catв фоновом режиме:

$ cat &
$ 
[1] + Stopped (tty input)        cat
$ 

Случай SSH

Теперь давайте проделаем то же самое с SSH.

$ ssh localhost sleep 999999 &
$ 
$ 
$ 
[1] + Stopped (tty input)        ssh localhost sleep 999999
$ 

Нажатие Enter иногда переходит к оболочке (, которая находится на переднем плане ), а иногда к процессу SSH (, после чего он останавливается SIGTTIN ). Почему? Если sshчитал с терминала, он должен немедленно получить SIGTTIN, а если нет, то почему он получает SIGTTIN?

Происходит следующее: процесс SSH вызывает системный вызов select, чтобы узнать, доступны ли входные данные для любого из интересующих его файлов (или готов ли выходной файл для приема дополнительных данных ). Источники ввода включают в себя, по крайней мере, терминал и сетевую розетку. В отличие от read, selectне запрещен для фоновых процессов, и sshне получает SIGTTIN при вызове select. Цель select— выяснить, доступны ли данные, ничего не нарушая.В идеале selectвообще не изменило бы состояние системы, но на самом деле это не совсем так. Когда selectсообщает процессу SSH, что ввод доступен в файловом дескрипторе терминала, ядро ​​должно зафиксировать отправку ввода, если процесс впоследствии вызывает read. (Если бы это было не так и процесс вызвал read, то в этот момент входные данные могли бы быть недоступны, поэтому возвращаемое значение из selectбыло бы ложью. )Таким образом, если ядро ​​решает направить некоторый ввод в процесс SSH, оно принимает решение к моменту возврата системного вызова select. Затем SSH вызывает read, и в этот момент ядро ​​​​видит, что фоновый процесс пытался читать с терминала, и приостанавливает его с помощью SIGTTIN.

Обратите внимание, что вам не нужно запускать несколько подключений к одному и тому же серверу. Одного достаточно. Несколько подключений просто увеличивают вероятность возникновения проблемы.

Решение :не читать с терминала

Если вам нужен сеанс SSH для чтения с терминала, запустите его на переднем плане.

Если вам не нужен сеанс SSH для чтения с терминала, убедитесь, что его ввод не поступает с терминала. Есть два способа сделать это:

  • Вы можете перенаправить ввод:

    ssh … </dev/null
    
  • Вы можете указать SSH не перенаправлять терминальное соединение с помощью -nили -f.(-nэквивалентно </dev/null; -fпозволяет самому SSH читать с терминала, т.е. чтобы прочитать пароль, но сама команда не откроет терминал.)

    ssh -n …
    

Обратите внимание, что отключение между терминалом и SSH должно происходить на клиенте. Процесс sleep, работающий на сервере, никогда не будет читать данные с терминала, но SSH не может об этом узнать. Если клиент получает ввод на стандартном вводе, он должен перенаправить его на сервер, который сделает данные доступными в буфере на случай, если приложение когда-либо решит прочитать их (и если приложение вызовет select,будет сообщено, что данные доступны ).

7
27.01.2020, 20:24

Вы можете найти помощь на справочной странице:

 -n      Redirects stdin from /dev/null (actually, prevents reading from stdin).  This must be used when ssh is run in the
         background.  A common trick is to use this to run X11 programs on a remote machine.  For example, ssh -n
         shadows.cs.hut.fi emacs & will start an emacs on shadows.cs.hut.fi, and the X11 connection will be automatically
         forwarded over an encrypted channel.  The ssh program will be put in the background.  (This does not work if ssh
         needs to ask for a password or passphrase; see also the -f option.)

Если это по-прежнему не помогает, я бы попробовал-T(отключить псевдо -выделение tty ), просто по прихоти.

4
27.01.2020, 20:24

Apparently, if the same shell launches multiple ssh connections to the same server, they won't return after executing the command they're given but will hang (Stopped (tty input)) for ever.

Это обычное поведение при одновременном доступе к TTY. Весь процесс уже находится в фоновом режиме, и когда он пытается записать вывод, ему не разрешается доступ к TTY, и он получает сигнал (SIGTTOU), который не перехватывается процессом bash, поэтому выполняется действие по умолчанию (Stop).

Вариант -n, как описано в другом ответе , или перенаправление ввода-вывода в некоторые файлы, вам поможет. Не уверен, что есть что-то еще, чтобы описать, но если да, пожалуйста, уточните.

1
27.01.2020, 20:24

Теги

Похожие вопросы