какова связь между моим текущим управляющим терминалом и `/dev/tty`?

какова связь между моим текущим управляющим терминалом и `/dev/tty`?

На Lubuntu 18.04 я запускаю оболочку в lxterminal. Ее управляющий терминал — текущий подчиненный псевдотерминал:

$ tty
/dev/pts/2

Я хотел бы узнать, какие отношения существуют между моим текущим управляющим терминалом /dev/pts/2и /dev/tty.

  1. /dev/ttyдействует как мой текущий управляющий терминал /dev/pts/2:

    $ echo hello > /dev/tty
    hello
    
    $ cat < /dev/tty
    world
    world
    ^C
    
  2. Но, похоже, это не связанные между собой файлы, а не символическая или жесткая ссылка на другой:

    $ 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при недоступности управляющего терминала).

Связанный контент