Em Unix-like sistemas operacionais, os fluxos de entrada, saída e erro padrão são identificados pelos descritores de arquivos 0
, 1
, 2
. No Linux, eles são visíveis no proc
sistema de arquivos em /proc/[pid]/fs/{0,1,2}
. Na verdade, esses arquivos são links simbólicos para um dispositivo pseudoterminal no /dev/pts
diretório
Um pseudoterminal (PTY) é um par de dispositivos virtuais, um mestre pseudoterminal (PTM) e um escravo pseudoterminal (PTS) (coletivamente chamado de par pseudoterminal ), que fornecem um canal IPC, semelhante a um canal bidirecional entre um programa que espera estar conectado a um dispositivo terminal e a um programa de driver que use o pseudoterminal para enviar e receber informações do programa anterior.
Um ponto importante é que o escravo pseudo-terminal aparece como um terminal comum, por exemplo, pode ser alternado entre os modos não-canônico e canônico (o padrão), no qual interpreta certos caracteres de entrada, como gerar um SIGINT
sinal quando um caractere de interrupção (normalmente gerado pressionando Ctrl+ Cno teclado) é gravado no mestre pseudoterminal ou fazendo com que o próximo read()
retorne 0
quando um caractere de fim de arquivo (normalmente gerado por Ctrl+ D) é encontrado. Outras operações suportadas pelos terminais estão ativando ou desativando o eco, definindo o grupo de processos em primeiro plano etc.
Os pseudoterminais têm vários usos:
Eles permitem que programas gostem ssh
de operar programas orientados a terminal em outro host conectado via rede. Um programa orientado ao terminal pode ser qualquer programa que normalmente seria executado em uma sessão interativa do terminal. A entrada, saída e erro padrão de tal programa não podem ser conectados diretamente ao soquete, pois os soquetes não suportam a funcionalidade relacionada ao terminal acima mencionada.
Eles permitem programas como expect
dirigir um programa interativo orientado ao terminal a partir de um script.
Eles são usados por emuladores de terminal, como xterm
para fornecer funcionalidade relacionada ao terminal.
Eles são usados por programas como screen
multiplexar um único terminal físico entre vários processos.
Eles são usados por programas como script
para gravar todas as entradas e saídas que ocorrem durante uma sessão de shell.
PTYs no estilo Unix98 , usados no Linux, são configurados da seguinte maneira:
O programa do driver abre o multiplexador mestre do pseudo-terminal em dev/ptmx
, no qual recebe um descritor de arquivo para um PTM, e um dispositivo PTS é criado no /dev/pts
diretório Cada descritor de arquivo obtido pela abertura /dev/ptmx
é um PTM independente com seu próprio PTS associado.
Os programas do driver chamam fork()
para criar um processo filho, que por sua vez executa as seguintes etapas:
A criança chama setsid()
para iniciar uma nova sessão, da qual a criança é líder da sessão. Isso também faz com que a criança perca seu terminal de controle .
A criança continua a abrir o dispositivo PTS que corresponde ao PTM criado pelo programa do driver. Como a criança é líder de sessão, mas não possui terminal de controle, o PTS se torna o terminal de controle da criança.
A criança usa dup()
para duplicar o descritor de arquivo para o dispositivo escravo nele, entrada, saída e erro padrão.
Por fim, a criança chama exec()
para iniciar o programa orientado ao terminal que deve ser conectado ao dispositivo pseudoterminal.
Nesse momento, tudo o que o programa do driver grava no PTM aparece como entrada para o programa orientado ao terminal no PTS e vice-versa.
Ao operar no modo canônico, a entrada para o PTS é armazenada em buffer linha por linha. Em outras palavras, assim como nos terminais regulares, a leitura do programa de um PTS recebe uma linha de entrada somente quando um caractere de nova linha é gravado no PTM. Quando a capacidade do buffer estiver esgotada, outras write()
chamadas serão bloqueadas até que parte da entrada tenha sido consumida.
No kernel Linux, as chamadas de sistema relacionado arquivo open()
, read()
, write()
stat()
etc. são implementados na camada Virtual sistema de arquivos (VFS), que fornece uma interface de sistema de arquivos uniforme para programas de espaço de usuário. O VFS permite que diferentes implementações do sistema de arquivos coexistam no kernel. Quando os programas de espaço do usuário chamam as chamadas de sistema mencionadas acima, o VFS redireciona a chamada para a implementação apropriada do sistema de arquivos.
Os dispositivos PTS abaixo /dev/pts
são gerenciados pela devpts
implementação do sistema de arquivos definida em /fs/devpts/inode.c
, enquanto o driver TTY que fornece o ptmx
dispositivo no estilo Unix98 é definido em drivers/tty/pty.c
.
O buffer entre dispositivos TTY e as disciplinas de linha TTY , como pseudo-terminais, é fornecida uma estrutura de buffer mantida para cada dispositivo tty, definido eminclude/linux/tty.h
Antes da versão 3.7 do kernel, o buffer era um buffer flip :
#define TTY_FLIPBUF_SIZE 512
struct tty_flip_buffer {
struct tq_struct tqueue;
struct semaphore pty_sem;
char *char_buf_ptr;
unsigned char *flag_buf_ptr;
int count;
int buf_num;
unsigned char char_buf[2*TTY_FLIPBUF_SIZE];
char flag_buf[2*TTY_FLIPBUF_SIZE];
unsigned char slop[4];
};
A estrutura continha armazenamento dividido em dois buffers de tamanho igual. Os buffers foram numerados 0
(primeira metade de char_buf/flag_buf
) e 1
(segunda metade). O driver armazenou dados no buffer identificado por buf_num
. O outro buffer pode ser liberado para a disciplina de linha.
O buffer foi 'invertido' alternando buf_num
entre 0
e 1
. Quando buf_num
alterado, char_buf_ptr
e flag_buf_ptr
foi definido como o início do buffer identificado por buf_num
, e count
foi definido como 0
.
Desde a versão 3.7 do kernel, os buffers flip TTY foram substituídos por objetos alocados por meio de kmalloc()
anéis organizados . Em uma situação normal para uma porta serial acionada por IRQ em velocidades típicas, seu comportamento é praticamente o mesmo do antigo buffer de buffer; dois buffers acabam alocados e o kernel alterna entre eles como antes. No entanto, quando há atrasos ou a velocidade aumenta, a nova implementação do buffer tem um desempenho melhor, pois o buffer pool pode crescer um pouco.