Ao executar um comando em tty
um terminal, ele retorna /dev/pts/10
.
Além disso, existem os arquivos /dev/stdout
/dev/stdin
e /dev/stderr
. Interagir com eles diretamente mostra os resultados no terminal.
user@laptop:build$ tty
/dev/pts/10
user@laptop:build$ echo "Test" > /dev/stdout
Test
user@laptop:build$ echo "Test" > /dev/stdin
Test
user@laptop:build$ echo "Test" > /dev/stderr
Test
Além disso, qualquer aplicativo cli, iniciado a partir do shell, terá os descritores de arquivo abertos para stdout
/ stderr
/ stdin
. ou seja, se você executar um script que imprime algo, imprimir é equivalente a escrever em stdout
.
Até agora stdout
// foram as únicas interfaces com as quais o shell trabalha stderr
. stdin
Isso também vale para os aplicativos.
Algum componente do sistema operacional está eventualmente movendo os dados gravados stdout
no terminal, caso contrário, eu não veria nada impresso.
Quando e onde uma conexão entre stdout/stdin/stderr
e o terminal acontece para que a interação com std*
realmente resulte em algo no terminal?
Minha suposição aproximada que eu gostaria de ter desafiado é:
/dev/stdout
,/dev/stdin
e/dev/stderr
são criados pelo shell em execução, eles não existem sem um shell.O shell configura o canal de comunicação com o arquivo de dispositivo real que representa o terminal (
/dev/pts/10
) e expõe as funcionalidades do terminal por meio/dev/stdout
de ,/dev/stdin
e/dev/stderr
. Dessa forma, o shell fornece aos aplicativos uma interface de arquivo simples, em vez de todos os aplicativos se preocuparem em como trabalhar o arquivo do dispositivo para impressão simples.
Atualizar
Apesar /dev/pts/10
de ser um pseudoterminal, darei mais valor às respostas que conseguirem dar a resposta sem introduzir o conceito de pseudoterminal. Estou vindo de uma perspectiva que apenas distrairá a resposta à pergunta:
Quando e onde uma conexão entre
stdout/stdin/stderr
e o terminal acontece para que a interação com/dev/std*
realmente resulte em algo no terminal?
/dev/pts/10
é a extremidade escrava de um par de dispositivos pseudoterminais . Na outra ponta está o programa que abriu o dispositivo clone mestre/dev/ptmx
e recebeu/dev/pts/10
como par (cada vez que você abre/dev/ptmx
, obtém um escravo diferente). A conexão entre/dev/ptmx
e/dev/pts/10
é basicamente um tubo bidirecional com uma torção .Ao abrir um aplicativo emulador de terminal:
/dev/ptmx
e obtém o nome da outra extremidade. Configura a outra extremidade e a abre,O shell não faz nada para configurar esses três descritores de arquivo, ele os herda de seu processo pai. Da mesma forma que seus filhos herdarão os descritores de arquivo do shell.
Observação : Em um sistema Linux
/dev/stdin
,/dev/stdout
e/dev/stderr
são arquivos reais, que por uma série de links simbólicos apontam para/proc/<pid>/0
,/proc/<pid>/1
e/proc/<pid>/2
, que por sua vez apontam para o dispositivo de entrada/saída real: no seu caso/dev/pts/10
.A existência desses três fluxos padrão é garantida pela biblioteca C.
Edit : para resolver sua pergunta atualizada, vamos esclarecer alguns pontos da resposta:
/proc/pts/*
é lido pelo terminal que o criou e exibido, tudo lido/proc/pts/*
vem de um dispositivo de entrada conectado ao terminal,/dev/stdout
geralmente é um link simbólico para/proc/self/fd/1
, enquanto/dev/stdin
um link simbólico para/proc/self/fd/0
. O sistema de arquivos virtual/proc
tem o cuidado de mostrar a cada aplicativo/proc/self
como um link simbólico para/proc/<pid>
, onde<pid>
está o id do processo do aplicativo./proc/<pid>/fd
apontam para os arquivos, pipes e outras coisas abertas por um aplicativo ou herdadas de seu pai. Cada aplicativo tem a garantia de ter três descritores de arquivo abertos:0
para ler a entrada,1
para gravar a saída,2
para gravar erros. No seu caso é/dev/pts/10
.Se você não redirecionar a saída para outro arquivo, todos os comandos executados pelo shell serão gravados no terminal. A exceção a essa regra é se o grupo de processos do seu comando for diferente do grupo de processos em primeiro plano do terminal, a gravação falhará e
SIGTTOU
será enviada ao comando. Esse comportamento pode ser controlado comstty tostop
estty -tostop
:Um programa compatível com POSIX pode esperar herdar os descritores de arquivo #0, #1 e #2 (também conhecidos pelas constantes de programação
stdin
estdout
,stderr
respectivamente) de seu processo pai, em um estado já aberto e pronto para uso .No caso mais simples, de um programa de linha de comando em uma sessão logada no console de texto, sem nenhum redirecionamento aplicado, essa cadeia de herança remonta ao
getty
processo que inicializou o dispositivo TTY para a sessão de login.Ao fazer login usando uma GUI, o processo do gerenciador de exibição (
gdm/kdm/sddm/lightdm/xdm/<whatever>dm
) geralmente definirá a entrada e a saída/dev/null
padrão e o erro padrão para$HOME/.xsession-errors
ou algo semelhante para o primeiro processo da sessão, e esses descritores de arquivo também são herdados por todos os programas GUI iniciados na sessão, seja como parte do ambiente da área de trabalho, ou começou a usar menus ou ícones da área de trabalho.Para, por exemplo, sessões SSH, o
sshd
processo que bifurcou para inicializar a sessão teria alocado um par de dispositivos pseudo-TTY, apontado osstdin/out/err
descritores de arquivo para metade dele e, em seguida,exec()
editado o shell do usuário. O outro lado dessa bifurcação manterá a outra metade do par de dispositivos pseudo-TTY e manipulará a criptografia/descriptografia do tráfego de entrada/saída entre a rede e o dispositivo pseudo-TTY, até que a sessão termine.Quando um emulador de terminal é iniciado em uma sessão GUI, ele se comporta essencialmente da mesma forma que o
sshd
processo ao inicializar uma nova sessão: ele aloca um pseudo-TTY,fork()
s e uma cópia configura a sessão, incluindo descritores de arquivo apontados #0, #1 e #2 para o pseudo-TTY e, finalmenteexec()
, é o shell do usuário, e o outro lado do fork permanecerá lidando com a tarefa de realmente manter a representação visual da janela do terminal.Então, em poucas palavras, o (pseudo?) dispositivo TTY foi conectado a stdin/stdout/stderr pela coisa que inicializou sua sessão de terminal, e todos os processos que podem estar entre isso e seu aplicativo simplesmente os transmitiram em uma cadeia de herança, não fazendo nada com esses descritores de arquivo, apenas deixando-os passar para o processo filho como está.
Quando os operadores de redirecionamento são usados na linha de comando do shell, como o shell é
fork()
uma cópia temporária de si mesmo em preparação para realmenteexec()
executar o comando, logo após afork()
cópia temporária fechará o respectivo descritor de arquivo, abrirá a coisa especificada pelo operador de redirecionamento em seu lugar , e, em seguida,exec()
o comando para que ele herde o(s) descritor(es) de arquivo stdin/out/err modificado(s).Em alguns sistemas do estilo Unix, os
/dev/std*
dispositivos podem ser manipulados pelo shell. Mas o Linux os torna um pouco mais "reais".No Linux,
/dev/stdin
e/dev/stdout
são/dev/stderr
apenas links simbólicos antigos apontando para o sistema de/proc
arquivos:Esses links são criados à medida que o sistema
udev
de arquivos baseado em RAM/dev
é inicializado na inicialização do sistema. Eles são apenas links simbólicos simples, nada mágicos lá.Mas
/proc
é um sistema de arquivos totalmente virtual que reflete o estado dos processos no sistema em tempo real, e por isso possui várias propriedades "mágicas":/proc/self
é um link simbólico que aponta para o/proc/<PID>
diretório do processo que o analisa:/proc/<PID>/fd
é um diretório que contém links simbólicos com seus nomes correspondentes aos descritores de arquivo abertos pelo processo com<PID>
, e apontando para qualquer que seja o descritor de arquivo associado .Então, quando um processo
/dev/pts/10
tenta acessar/dev/stdin
, o link simbólico aponta para ele/proc/self/fd/0
... e quando/proc/self/fd/0
é acessado, o/proc
driver do sistema de arquivos olha a tabela de processos do kernel, usa-a para encontrar a tabela de descritores de arquivos do processo que está acessando , e apresenta/proc/self/fd/0
como um link simbólico para/dev/pts/10
... justamente porque esse processo atualmente tem/dev/pts/10
associado ao seu descritor de arquivo #0.No Solaris 11, os
/dev/std*
dispositivos são links simbólicos para o/dev/fd/
diretório, que é similarmente "mágico":Aqui, o
/dev
driver do sistema de arquivos Solaris implementa a mágica usando nós de dispositivo no/dev/fd
diretório em vez de redirecionar para o sistema de/proc
arquivos, como o Linux faz por motivos históricos.Executando um comando no shell
Quando um comando é executado no shell, é isso que acontece.
fork
: shell em como executar em ambos os processos.std in/out/err
: mas se não houver redirecionamento, ele não faz nada. O novo processo os herdou do shell original e o shell já tinha os valores corretos.exec
para executar um novo programa: esse novo programa herdará valores parastd in/out/err
e substituirá o novo shell.Este novo shell é muito transitório (agora mencionado nos documentos, pois é apenas um detalhe de implementação). Não é o mesmo que um sub-shell.
O novo comando abre
/dev/stdin
Quando o novo programa é aberto
/dev/stdin
, o código do sistema de arquivos no kernel vê que este é um link simbólico para/proc/self/fd/0
ele, então vê que/dev/self
é um link simbólico para/proc/nnnn
onde nnnn é o pid do processo, então isso aponta para/proc/nnnn/fd/0
qual aponta para o arquivo, por exemplo/dev/pts/10
. A abertura/dev/stdin
criará um novo descritor de arquivo. Não é necessário abrirdev/stdin
normalmente, pois o descritor de arquivo 0 já aponta para o arquivo.(Você só precisará fazer isso se o programa não for escrito para ler stdin, mas puder ler de um arquivo.) (tudo isso também vale para stdout e stderr.)
Os arquivos
/proc
não são arquivos reais (não armazenados em nenhum lugar); Estes são criados dinamicamente quando acessados (nunca gravados em disco), por um sistema de arquivos, que procura os dados de estruturas de dados no kernel (não de um disco).