
На Lubuntu 18.04 я запускаю оболочку в lxterminal. Ее управляющий терминал — текущий подчиненный псевдотерминал:
$ tty
/dev/pts/2
Я хотел бы узнать, какие отношения существуют между моим текущим управляющим терминалом /dev/pts/2
и /dev/tty
.
/dev/tty
действует как мой текущий управляющий терминал/dev/pts/2
:$ echo hello > /dev/tty hello $ cat < /dev/tty world world ^C
Но, похоже, это не связанные между собой файлы, а не символическая или жесткая ссылка на другой:
$ ls -lai /dev/tty /dev/pts/2 5 crw--w---- 1 t tty 136, 2 May 31 16:38 /dev/pts/2 13 crw-rw-rw- 1 root tty 5, 0 May 31 16:36 /dev/tty
Для разных сеансов с разными управляющими терминалами, если
/dev/tty
гарантированно будут их управляющие терминалы. Как это может быть разными управляющими терминалами, не будучи символической ссылкой или жесткой ссылкой?
Так в чем их отношения и различия? Любая помощь будет высоко оценена!
Этот пост произошел от более раннегоОтносятся ли вывод команды `tty` и файл `/dev/tty` к управляющему терминалу текущего процесса bash?
решение1
Страница tty
руководства в разделе 4утверждает следующее:
Файл/dev/ttyэто символьный файл с главным номером 5 и младшим номером 0, обычно с режимом 0666 и владельцем.группой root.tty. Это синоним управляющего терминала процесса, если таковой имеется.
В добавок к
ioctl(2)
запросы, поддерживаемые устройством, на которое ссылается tty,ioctl(2)
запросTIOCNOTTY
поддерживается.
TIOCNOTTY
Отсоедините вызывающий процесс от его управляющего терминала.
Если процесс является лидером сеанса, то
SIGHUP
сигналыSIGCONT
отправляются группе процессов переднего плана, и все процессы в текущем сеансе теряют свой управляющий tty.Этот
ioctl(2)
вызов работает только на файловых дескрипторах, подключенных к /dev/tty. Он используется процессами-демонами, когда они вызываются пользователем на терминале. Процесс пытается открыть/dev/tty. Если открытие прошло успешно, он отсоединяется от терминала с помощьюTIOCNOTTY
, а если открытие не удалось, он, очевидно, не присоединен к терминалу и ему не нужно отсоединяться.
Это частично объясняет, почему /dev/tty
нет символической ссылки на управляющий терминал: он бы поддерживал дополнительный ioctl
, а управляющего терминала может и не быть (но процесс всегда может попытаться получить доступ к /dev/tty
). Однако документация неверна: дополнительный ioctl
не только доступенс помощью /dev/tty
(видетьответ mosvy, что также дает более разумное объяснение природы /dev/tty
).
/dev/tty
может представлять различные управляющие терминалы, не будучи при этом связью, поскольку драйвер, который ее реализует, определяет, какой управляющий терминал вызывающего процесса имеется, если таковой имеется.
Вы можете рассматривать его как /dev/tty
управляющий терминал, предлагающий функциональность, которая имеет смысл только для управляющего терминала, в то время как /dev/pts/2
и т. д. являются простыми терминалами, один из которых может оказаться управляющим терминалом для данного процесса.
решение2
/dev/tty
это «магическое» символьное устройство, которое при открытии возвращает дескриптор текущего терминала.
Предполагая, что управляющим терминалом является /dev/pts/1
, файловый дескриптор, открытый через /dev/pts/1
, и один открытый через /dev/tty
будут ссылаться на одно и то же устройство; любая операция записи, чтения или другая файловая операция будет работать одинаково на любом из них.
В частности, они будут принимать тот же набор ioctl, и TIOCNOTTY
не являются дополнительным ioctl, доступным только через/dev/tty
.
ioctl(fd, TIOCNOTTY)
работает одинаково для любого файлового дескриптора, ссылающегося на терминал, при условии, что это управляющий терминал процесса, который его вызывает.
Не имеет значения, был ли дескриптор получен путем открытия /dev/tty
, /dev/pts/1
, /dev/ptmx
(в этом случае ioctl будет действовать на его соответствующийраб), или совсем недавно, по призыву ioctl(master, TIOCGPTPEER, flags)
.
Пример:
$ cat <<'EOT' >tiocnotty.c
#include <sys/ioctl.h>
#include <unistd.h>
#include <err.h>
int main(int ac, char **av){
if(ioctl(0, TIOCNOTTY)) err(1, "io(TIOCNOTTY)");
if(ac < 2) return 0;
execvp(av[1], av + 1);
err(1, "execvp %s", av[1]);
}
EOT
$ cc -W -Wall tiocnotty.c -o tiocnotty
$ ./tiocnotty
$ ./tiocnotty </dev/tty
$ tty
/dev/pts/0
$ ./tiocnotty </dev/pts/0
Кроме того, это на самом деле не «отсоединит» текущий процесс от tty; процесс по-прежнему сможет читать с него, a ^C
на терминале убьет его и т. д. Его единственный эффект на процесс, который не является лидером сеанса, заключается в том, что tty больше не будет доступен через /dev/tty
, и он больше не будет указан как управляющий tty в /proc/PID/stat
:
$ ./tiocnotty cat
^C
$ ./tiocnotty cat
^Z
[2]+ Stopped ./tiocnotty cat
$ ./tiocnotty cat
foo
foo
^D
$ ./tiocnotty cat /dev/tty
cat: /dev/tty: No such device or address
$ ./tiocnotty awk '{print$7}' /proc/self/stat
0
[7-е поле /proc/<pid>/stat
— это идентификатор устройства управляющего tty, см. proc(5)
]
Если вызывающий его процесс является лидером сеанса, он действительно отсоединит сеанс от tty и отправит пару SIGHUP
/ SIGCONT
в группу процессов переднего плана из сеанса. Но даже в этом случае терминал будетнетбыть закрыт, и процесс все равно сможет читать из него, если он переживет SIGHUP
:
$ script /dev/null -c 'trap "" HUP; exec ./tiocnotty cat'
Script started, file is /dev/null
lol
lol
^C^C^C^C^C # no controlling tty anymore
wtf
wtf
^D # but still reading fine from it
Script done, file is /dev/null
/dev/tty
не является символической ссылкой, как /dev/stdin
=> /dev/fd/0
=> /proc/self/fd/0
=>, /dev/pts/0
потому что она была изобретена задолго до виртуальных динамических файловых систем, таких как procfs (и задолго до символических ссылок в целом). И многие программы стали зависеть от ее конкретной семантики (например, /dev/tty
сбой ENODEV
при недоступности управляющего терминала).