En Unix-como sistemas operativos, los flujos de entrada, salida y error estándar se identifican con los descriptores de archivo 0
, 1
, 2
. En Linux, estos son visibles bajo el proc
sistema de archivos en /proc/[pid]/fs/{0,1,2}
. Estos archivos son en realidad enlaces simbólicos a un dispositivo pseudoterminal en el /dev/pts
directorio.
Un pseudoterminal (PTY) es un par de dispositivos virtuales, un pseudoterminal master (PTM) y un pseudoterminal slave (PTS) (colectivamente denominado un par pseudoterminal ), que proporcionan un canal IPC, algo así como un tubo bidireccional entre un programa que espera para conectarse a un dispositivo terminal y a un programa de controlador que utiliza el pseudoterminal para enviar información y recibir información del programa anterior.
Un punto clave es que el esclavo pseudoterminal aparece como un terminal normal, por ejemplo, se puede alternar entre el modo no canónico y el canónico (el valor predeterminado), en el que interpreta ciertos caracteres de entrada, como generar una SIGINT
señal cuando un carácter de interrupción (normalmente generado presionando Ctrl+ Cen el teclado) se escribe en el maestro pseudoterminal o provoca que el siguiente read()
regrese 0
cuando se encuentra un carácter de fin de archivo (normalmente generado por Ctrl+ D). Otras operaciones compatibles con los terminales están activando o desactivando los ecos, configurando el grupo de procesos en primer plano, etc.
Los pseudoterminales tienen varios usos:
Permiten programas como ssh
operar programas orientados a terminal en otro host conectado a través de una red. Un programa orientado a terminal puede ser cualquier programa, que normalmente se ejecutaría en una sesión de terminal interactiva. La entrada, salida y error estándar de un programa de este tipo no se pueden conectar directamente a un zócalo, ya que los zócalos no son compatibles con la funcionalidad relacionada con el terminal mencionada anteriormente.
Permiten programas como expect
manejar un programa interactivo orientado a terminal desde un script.
Los utilizan los emuladores de terminal, como xterm
para proporcionar funcionalidad relacionada con el terminal.
Son utilizados por programas como screen
para multiplexar una sola terminal física entre múltiples procesos.
Son utilizados por programas como script
para registrar todas las entradas y salidas que ocurren durante una sesión de shell.
Los PTY de estilo Unix98 , utilizados en Linux, se configuran de la siguiente manera:
El programa controlador abre el multiplexor maestro pseudo-terminal dev/ptmx
en el cual recibe un descriptor de archivo para un PTM, y se crea un dispositivo PTS en el /dev/pts
directorio. Cada descriptor de archivo obtenido al abrir /dev/ptmx
es un PTM independiente con su propio PTS asociado.
El controlador programa las llamadas fork()
para crear un proceso secundario, que a su vez realiza los siguientes pasos:
El niño llama setsid()
para comenzar una nueva sesión, de la cual el niño es el líder de la sesión. Esto también hace que el niño pierda su terminal de control .
El niño procede a abrir el dispositivo PTS que corresponde al PTM creado por el programa del controlador. Como el niño es un líder de sesión, pero no tiene un terminal de control, el PTS se convierte en el terminal de control del niño.
El niño utiliza dup()
para duplicar el descriptor de archivo para el dispositivo esclavo en su entrada, salida y error estándar.
Por último, el niño llama exec()
para iniciar el programa orientado a terminal que se va a conectar al dispositivo pseudoterminal.
En este punto, cualquier cosa que el programa del controlador escriba en el PTM, aparece como entrada al programa orientado a la terminal en el PTS, y viceversa.
Cuando se opera en modo canónico, la entrada al PTS se almacena línea por línea. En otras palabras, al igual que con los terminales regulares, la lectura del programa de un PTS recibe una línea de entrada solo cuando se escribe un carácter de nueva línea en el PTM. Cuando se agota la capacidad de almacenamiento en búfer, se write()
bloquean más llamadas hasta que se haya consumido parte de la entrada.
En el núcleo de Linux, los archivos relacionados con las llamadas al sistema open()
, read()
, write()
stat()
etc se implementan en la capa virtual del sistema de archivos (VFS), que proporciona una interfaz de sistema de archivos uniforme para los programas de espacio de usuario. El VFS permite que diferentes implementaciones de sistemas de archivos coexistan dentro del núcleo. Cuando los programas de espacio de usuario llaman a las llamadas del sistema antes mencionadas, el VFS redirige la llamada a la implementación apropiada del sistema de archivos.
Los dispositivos PTS bajo /dev/pts
son administrados por la devpts
implementación del sistema de archivos definida en /fs/devpts/inode.c
, mientras que el controlador TTY que proporciona el ptmx
dispositivo de estilo Unix98 se define en drivers/tty/pty.c
.
El almacenamiento en búfer entre dispositivos TTY y disciplinas de línea TTY , como pseudoterminales, proporciona una estructura de búfer mantenida para cada dispositivo tty, definida eninclude/linux/tty.h
Antes de la versión 3.7 del kernel, el búfer era un búfer invertido :
#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];
};
La estructura contenía almacenamiento dividido en dos tampones de igual tamaño. Los tampones estaban numerados 0
(primera mitad de char_buf/flag_buf
) y 1
(segunda mitad). El controlador almacenó datos en el búfer identificado por buf_num
. El otro amortiguador podría ser vaciado a la disciplina de línea.
El búfer se 'volcó' al alternar buf_num
entre 0
y 1
. Cuando se buf_num
modificó char_buf_ptr
y flag_buf_ptr
se estableció en el comienzo del búfer identificado por buf_num
y count
se estableció en 0
.
Desde la versión 3.7 del núcleo, los buffers de volteo TTY se han reemplazado por objetos asignados a través de kmalloc()
organizados en anillos . En una situación normal para un puerto serie controlado por IRQ a velocidades típicas, su comportamiento es prácticamente el mismo que con el antiguo búfer de volteo; dos buffers terminan asignados y el kernel cambia entre ellos como antes. Sin embargo, cuando hay retrasos o aumenta la velocidad, la nueva implementación del búfer funciona mejor ya que el grupo de búferes puede crecer un poco.