quais são as relações entre meu terminal de controle atual e `/dev/tty`?

quais são as relações entre meu terminal de controle atual e `/dev/tty`?

No Lubuntu 18.04, executo um shell no lxterminal. Seu terminal de controle é o escravo pseudoterminal atual:

$ tty
/dev/pts/2

Gostaria de saber quais são as relações entre meu terminal de controle atual /dev/pts/2e o /dev/tty.

  1. /dev/ttyatua como meu terminal de controle atual /dev/pts/2:

    $ echo hello > /dev/tty
    hello
    
    $ cat < /dev/tty
    world
    world
    ^C
    
  2. Mas eles parecem ser arquivos não relacionados, em vez de um ser um link simbólico ou hardlink para o outro:

    $ 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
    

Para sessões diferentes com terminais de controle diferentes, /dev/ttyé garantido que sejam seus terminais de controle. Como podem ser diferentes terminais de controle, sem ser um link simbólico ou hardlink?

Então, quais são suas relações e diferenças? Qualquer ajuda é muito apreciada!

Esta postagem é originada de uma anteriorA saída do comando `tty` e o arquivo `/dev/tty` referem-se ao terminal de controle do processo bash atual?

Responder1

A ttypágina de manual na seção 4afirma o seguinte:

O arquivo/dev/ttyé um arquivo de caracteres com número maior 5 e número menor 0, geralmente no modo 0666 e proprietário.grupo root.tty. É sinônimo de terminal de controle de um processo, se houver.

Em adição aioctl(2)solicitações suportadas pelo dispositivo ao qual tty se refere, oioctl(2)solicitação TIOCNOTTYé suportada.

TIOCNOTTY

Desconecte o processo de chamada de seu terminal de controle.

Se o processo for o líder da sessão, então SIGHUPos SIGCONTsinais serão enviados para o grupo de processos em primeiro plano e todos os processos na sessão atual perderão seu tty de controle.

Esseioctl(2)call funciona apenas em descritores de arquivo conectados a /dev/tty. É usado por processos daemon quando são invocados por um usuário em um terminal. O processo tenta abrir/dev/tty. Se a abertura for bem-sucedida, ele se desconecta do terminal usando TIOCNOTTY, enquanto se a abertura falhar, obviamente não está conectado a um terminal e não precisa se desconectar.

Isso explicaria em parte por que /dev/ttynão existe um link simbólico para o terminal de controle: ele suportaria um adicional ioctle pode não haver um terminal de controle (mas um processo sempre pode tentar acessar /dev/tty). Mas a documentação está incorreta: o adicional ioctlnão está acessível apenasatravés da /dev/tty(verresposta de Mosvy, o que também dá uma explicação mais sensata para a natureza de /dev/tty).

/dev/ttypode representar diferentes terminais de controle, sem ser um link, pois o driver que o implementa determina qual é o terminal de controle do processo chamador, se houver.

Você pode pensar nisso como /dev/ttysendo o terminal de controle e, portanto, oferecendo funcionalidades que só fazem sentido para um terminal de controle, enquanto /dev/pts/2etc. são terminais simples, um dos quais pode ser o terminal de controle para um determinado processo.

Responder2

/dev/ttyé um dispositivo de caracteres "mágico" que, quando aberto, retornará um identificador para o terminal atual.

Supondo que o terminal de controle seja /dev/pts/1, um descritor de arquivo aberto via /dev/pts/1e um descritor aberto via /dev/ttyse referirão ao mesmo dispositivo; qualquer operação de gravação, leitura ou outra operação de arquivo funcionará da mesma forma em qualquer um deles.

Em particular, eles aceitarão o mesmo conjunto de ioctls, e TIOCNOTTYnão é um ioctl extra disponível apenas via/dev/tty.

ioctl(fd, TIOCNOTTY)funciona da mesma forma em qualquer descritor de arquivo referente a um terminal, desde que seja o terminal controlador do processo que o chama.

Não importa se o descritor foi obtido abrindo /dev/tty, /dev/pts/1, /dev/ptmx(nesse caso o ioctl atuará em seu correspondenteescravo), ou mais recentemente, por uma chamada para ioctl(master, TIOCGPTPEER, flags).

Exemplo:

$ 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

Além disso, não "separará" realmente o processo atual do tty; o processo ainda será capaz de lê-lo, um ^Cno terminal irá matá-lo, etc. Seu único efeito em um processo que não é um líder de sessão é que o tty não estará mais acessível via /dev/tty, e não será mais listado como o tty controlador em /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

[o 7º campo /proc/<pid>/staté o ID do dispositivo do tty de controle, consulte proc(5)]

Se o processo que o chama for o líder da sessão, ele realmente separará a sessão do tty e enviará um par SIGHUP/ SIGCONTpara o grupo de processos em primeiro plano da sessão. Mas mesmo assim, o terminal iránãoserá fechado e o processo ainda poderá lê-lo, se sobreviver ao 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/ttynão é um link simbólico como /dev/stdin=> /dev/fd/0=> /proc/self/fd/0=> /dev/pts/0porque foi inventado muito antes dos sistemas de arquivos dinâmicos virtuais como o procfs (e muito antes dos links simbólicos em geral). E muitos programas passaram a depender de sua semântica específica (por exemplo, /dev/ttyfalhando ENODEVquando o terminal de controle não está acessível).

informação relacionada