stringtranslate.com

Interfaz de terminal POSIX

La interfaz de terminal POSIX es una abstracción generalizada que comprende tanto una interfaz de programación de aplicaciones para programas como un conjunto de expectativas de comportamiento para los usuarios de una terminal , tal como se define en el estándar POSIX y la Especificación Única de Unix . Es un desarrollo histórico a partir de las interfaces de terminal de BSD versión 4 y Unix de Séptima Edición .

Conceptos generales subyacentes

Hardware

En los sistemas Unix, una multiplicidad de dispositivos de E/S se consideran "terminales". [1] [2] Estos incluyen:

Inteligencia y capacidades de la terminal

Inteligencia: los terminales son tontos, no inteligentes

A diferencia de sus contemporáneos mainframe y minicomputadora [ cita requerida ] , el sistema Unix original fue desarrollado únicamente para terminales tontas , y ese sigue siendo el caso hoy en día. [6] Una terminal es un dispositivo orientado a caracteres, que comprende flujos de caracteres recibidos desde y enviados al dispositivo. [6] [7] Aunque los flujos de caracteres están estructurados, incorporando caracteres de control , códigos de escape y caracteres especiales, el protocolo de E/S no está estructurado como lo estaría el protocolo de E/S de terminales inteligentes . No hay especificaciones de formato de campo. No hay transmisión en bloque de pantallas enteras (formularios de entrada) de datos de entrada.

Por el contrario, los mainframes y minicomputadoras en arquitecturas cerradas comúnmente utilizan terminales orientadas a bloques .

Capacidades: terminfo, termcap, curses, et al.

Las "capacidades" de un terminal comprenden varias funciones de terminal tontas que van más allá de lo que está disponible en un teletipo puro, y que los programas pueden utilizar. Comprenden (principalmente) códigos de escape que se pueden enviar o recibir del terminal. Los códigos de escape enviados al terminal realizan varias funciones que un terminal CRT (o emulador de terminal de software) es capaz de hacer y un teletipo no, como mover el cursor del terminal a posiciones en la pantalla, limpiar y desplazarse por toda o parte de la pantalla, encender y apagar dispositivos de impresión conectados, teclas de función programables, cambiar colores y atributos de visualización (como video inverso ) y configurar cadenas de título de visualización. Los códigos de escape recibidos del terminal significan cosas como tecla de función , tecla de flecha y otras pulsaciones de teclas especiales ( tecla de inicio , tecla de fin , tecla de ayuda , tecla PgUp , tecla PgDn , tecla de inserción , tecla de borrar , etc.). [8] [9]

Estas capacidades están codificadas en bases de datos que son configuradas por un administrador de sistema y a las que se accede desde programas a través de la biblioteca terminfo (que reemplaza a la antigua biblioteca termcap ), sobre la que a su vez se construyen bibliotecas como las bibliotecas curses y ncurses . Los programas de aplicación utilizan las capacidades de la terminal para proporcionar interfaces de usuario textuales con ventanas, cuadros de diálogo, botones, etiquetas, campos de entrada, menús, etc. [10] [11]

Control de variables ambientales: TERMet al.

El conjunto particular de capacidades para el terminal que utiliza la entrada y salida de un programa (que reconoce terminales) se obtiene de la base de datos en lugar de estar integrado en programas y bibliotecas, y está controlado por la TERM variable de entorno (y, opcionalmente para las bibliotecas termcap y terminfo, las variables de entorno TERMCAPy TERMINFO, respectivamente). [10] Esta variable la establece cualquier programa de monitorización de terminal que genere los programas que luego utilizan ese terminal para su entrada y salida, o a veces de forma explícita. Por ejemplo:

Control de trabajo

Los terminales proporcionan funciones de control de trabajos. De manera interactiva, el usuario en el terminal puede enviar caracteres de control que suspenden el trabajo que se está ejecutando actualmente, volviendo al shell de control de trabajos interactivo que generó el trabajo, y puede ejecutar comandos que colocan trabajos en "segundo plano" o que cambian otro trabajo en segundo plano al primer plano (reanudándolo si es necesario). [14] [15]

Disciplinas de línea

Estrictamente hablando, en Unix un dispositivo terminal comprende el controlador de dispositivo tty subyacente , responsable del control físico del hardware del dispositivo a través de instrucciones de E/S y el manejo de solicitudes de interrupción del dispositivo para entrada y salida de caracteres, y la disciplina de línea . Una disciplina de línea es independiente del hardware del dispositivo real, y la misma disciplina de línea se puede utilizar para un dispositivo concentrador de terminal responsable de múltiples terminales de control que para un pseudoterminal. De hecho, la disciplina de línea (o, en el caso de BSD, AIX y otros sistemas, las disciplinas de línea ) son las mismas en todos los dispositivos terminales. Es la disciplina de línea la que es responsable del eco local, la edición de línea, el procesamiento de modos de entrada, el procesamiento de modos de salida y el mapeo de caracteres. Todas estas cosas son independientes del hardware real, ya que tratan con las abstracciones simples proporcionadas por los controladores de dispositivo tty: transmitir un carácter, recibir un carácter, establecer varios estados de hardware. [16] [17]

En los sistemas Unix de séptima edición , BSD y derivados, incluidos macOS y Linux , cada dispositivo terminal se puede cambiar entre múltiples disciplinas de línea. [18] En el sistema AT&T STREAMS , las disciplinas de línea son módulos STREAMS que se pueden insertar y extraer de una pila de E/S STREAMS. [19]

Historia

La interfaz de terminal POSIX se deriva de las interfaces de terminal de varios sistemas Unix.

Primeros Unix: Unix de séptima edición

La interfaz de terminal proporcionada por Unix 32V y Seventh Edition Unix, y también presentada por BSD versión 4 como el antiguo controlador de terminal , era simple, en gran parte orientada a los teletipos como terminales. La entrada se ingresaba una línea a la vez, y el controlador de terminal en el sistema operativo (y no los terminales mismos) proporcionaba capacidades de edición de línea simples. El núcleo mantenía un búfer en el que se realizaba la edición. Las aplicaciones que leían la entrada del terminal recibían el contenido del búfer solo cuando returnse presionaba la tecla en el terminal para finalizar la edición de línea. La tecla enviada desde el terminal al sistema borraba ("mataba") todo el contenido actual del búfer de edición y normalmente se mostraba como un símbolo ' @ ' seguido de una secuencia de nueva línea para mover la posición de impresión a una nueva línea en blanco. La tecla enviada desde la terminal al sistema borraría el último carácter del final del buffer de edición, y normalmente se mostraría como un símbolo ' # ', que los usuarios tendrían que reconocer como indicativo de un "borrado" del carácter precedente (los teletipos no son físicamente capaces de borrar caracteres una vez que han sido impresos en el papel). [20] [21] [22] [23] [18]@#

Desde un punto de vista de programación, un dispositivo terminal tenía velocidades de transmisión y recepción en baudios , caracteres de "borrado" y "eliminación" (que realizaban la edición de línea, como se explicó), caracteres de "interrupción" y "salida" (generando señales para todos los procesos para los cuales el terminal era un terminal de control), caracteres de "inicio" y "detención" (usados ​​para el control de flujo del módem ), un carácter de "fin de archivo" (que actuaba como un retorno de carro excepto que la llamada del sistema lo descartaba del búfer read()y, por lo tanto, potencialmente causaba que se devolviera un resultado de longitud cero) y varias banderas de modo básico que determinaban si el eco local era emulado por el controlador de terminal del núcleo, si el control de flujo del módem estaba habilitado, las longitudes de varios retrasos de salida, el mapeo para el carácter de retorno de carro y los tres modos de entrada. [24]

Los tres modos de entrada fueron:

Modo de línea (también llamado modo "cocinado")

En el modo de línea, la disciplina de línea realiza todas las funciones de edición de línea y reconoce los caracteres de control de "interrupción" y "salida" y los transforma en señales enviadas a los procesos. Los programas de aplicación que leen desde la terminal reciben líneas completas, después de que el usuario haya completado la edición de línea presionando Enter. [21] [25]

modo cbreak

El modo cbreak es uno de los dos modos de caracteres por vez. ( Stephen R. Bourne se refirió a él en broma (Bourne 1983, p. 288) como un modo "a medio cocinar" y, por lo tanto, "raro"). La disciplina de línea no realiza ninguna edición de línea, y las secuencias de control para las funciones de edición de línea se tratan como una entrada de caracteres normal. Los programas de aplicación que leen desde la terminal reciben caracteres inmediatamente, tan pronto como están disponibles en la cola de entrada para ser leídos. Sin embargo, los caracteres de control de "interrupción" y "salida", así como los caracteres de control de flujo del módem, aún se manejan de manera especial y se eliminan del flujo de entrada. [26] [27]

modo crudo
El modo sin procesar es el otro de los dos modos de caracteres por vez. La disciplina de línea no realiza ninguna edición de línea, y las secuencias de control tanto para las funciones de edición de línea como para los diversos caracteres especiales ("interrupción", "salida" y control de flujo) se tratan como una entrada de caracteres normal. Los programas de aplicación que leen desde el terminal reciben caracteres inmediatamente y reciben el flujo de caracteres completo sin modificaciones, tal como llegó desde el dispositivo terminal. [28] [26] [27]

La interfaz programática para consultar y modificar todos estos modos y caracteres de control era la ioctl()llamada del sistema . (Esta reemplazó a las llamadas del sistema stty()y gtty()de la Sexta Edición de Unix.) [29] [30] Aunque los caracteres "borrar" y "matar" eran modificables desde sus valores predeterminados de y , durante muchos años fueron los valores predeterminados preestablecidos en los controladores de dispositivos de terminal, y en muchos sistemas Unix, que solo modificaban la configuración del dispositivo de terminal como parte del proceso de inicio de sesión, en los scripts de inicio de sesión del sistema que se ejecutaban después de que el usuario hubiera ingresado el nombre de usuario y la contraseña, cualquier error en los mensajes de inicio de sesión y contraseña tenía que corregirse utilizando los caracteres clave de edición históricos heredados de los terminales de teletipo. [23]#@

BSD: el advenimiento del control de tareas

Con los Unix BSD llegaron el control de trabajos y un nuevo controlador de terminal con capacidades extendidas. [18] Estas extensiones comprendían caracteres especiales adicionales (nuevamente modificables mediante programación):

La interfaz programática para consultar y modificar todos estos modos adicionales y caracteres de control seguía siendo la ioctl()llamada al sistema, que sus creadores (Leffler et al. 1989, p. 262) describieron como una "interfaz bastante desordenada". Se mantuvo toda la funcionalidad original de Unix de la Séptima Edición y se agregó la nueva funcionalidad mediante ioctl()códigos de operación adicionales, lo que dio como resultado una interfaz programática que claramente había crecido y que presentaba cierta duplicación de funcionalidad. [31]

Sistema III y Sistema V

El Sistema III introdujo una nueva interfaz de programación que combinaba las operaciones separadas de la Séptima Edición ioctl()para obtener y establecer indicadores y para obtener y establecer caracteres de control en llamadas que usaban una termioestructura para contener tanto indicadores como caracteres de control y que podían obtenerlos en una sola operación y establecerlos en otra sola operación. También dividió algunos de los indicadores de la interfaz de la Séptima Edición en múltiples indicadores separados y agregó algunas capacidades adicionales, aunque no admitía el control de trabajos ni las mejoras del modo cocinado de 4BSD. [32] Por ejemplo, reemplazó los modos "cocinado", "cbreak" y "sin procesar" de la Séptima Edición con diferentes abstracciones. El reconocimiento de caracteres generadores de señales es independiente del modo de entrada, y solo hay dos modos de entrada: canónico y no canónico. (Esto permite un modo de entrada de terminal que no está presente en la Séptima Edición y BSD: modo canónico con la generación de señales deshabilitada).

Los sucesores del Sistema III, incluido el Sistema V , utilizaron la misma interfaz.

POSIX: Consolidación y abstracción

Uno de los principales problemas que el estándar POSIX abordó con su definición de una interfaz de terminal general fue la plétora de interfaces programáticas. Aunque en el momento de la creación del estándar el comportamiento de las terminales era bastante uniforme de un sistema a otro, ya que la mayoría de los Unix habían adoptado las nociones de disciplinas de línea y las capacidades de control de tareas de BSD, la interfaz programática con las terminales a través de la ioctl()llamada al sistema era un caos. Los distintos Unix proporcionaban ioctl()operaciones diferentes, con nombres (simbólicos) diferentes y diferentes indicadores. El código fuente portable tenía que contener una cantidad significativa de compilación condicional para dar cabida a las diferencias entre las plataformas de software, aunque todas fueran teóricamente Unix. [33]

El estándar POSIX reemplaza el ioctl()sistema por completo, con un conjunto de funciones de biblioteca (que, por supuesto, pueden implementarse en segundo plano mediante ioctl()operaciones específicas de la plataforma) con nombres y parámetros estandarizados. La termioestructura de datos de System V Unix se utilizó como plantilla para la termiosestructura de datos POSIX, cuyos campos permanecieron prácticamente inalterados, excepto que ahora se utilizaban tipos de datos alias para especificar los campos, lo que permitía que los implementadores los trasladaran fácilmente a múltiples arquitecturas de procesador, en lugar de requerir explícitamente los tipos de datos unsigned shorty charde los lenguajes de programación C y C++ (que podrían ser tamaños inconvenientes en algunas arquitecturas de procesador). [33] [34]

POSIX también introdujo soporte para el control de trabajos, con una termiosestructura que contiene caracteres de suspensión y suspensión retrasada además de los caracteres de control soportados por System III y System V. No agregó ninguna de las extensiones de modo cocinado de BSD, aunque SunOS 4.x, System V Release 4 , Solaris , HP-UX , AIX , BSD más nuevos, macOS y Linux las han implementado como extensiones a termios.

Qué define la norma

Control de terminales y grupos de procesos

Cada proceso del sistema tiene un único terminal de control o no tiene ningún terminal de control. Un proceso hereda su terminal de control de su padre, y las únicas operaciones sobre un proceso son adquirir un terminal de control, por parte de un proceso que no tiene terminal de control, y renunciar a él, por parte de un proceso que tiene un terminal de control. [33]

No se define ninguna forma portátil de adquirir un terminal de control, ya que el método se define por implementación. El estándar define el O_NOCTTYindicador para la open()llamada al sistema , que es la forma de evitar lo que de otro modo sería la forma convencional de adquirir un terminal de control (un proceso sin terminal de control, open()un archivo de dispositivo de terminal que no sea ya el terminal de control de algún otro proceso, sin especificar el O_NOCTTYindicador [35] ), pero deja su semántica convencional opcional.

Cada proceso también es miembro de un grupo de procesos. Cada dispositivo terminal registra un grupo de procesos que se denomina su grupo de procesos en primer plano . Los grupos de procesos controlan el acceso al terminal y la entrega de señales. Las señales generadas en el terminal se envían a todos los procesos que son miembros del grupo de procesos en primer plano del terminal. read()y write()las operaciones de E/S en un terminal por parte de un proceso que no es miembro del grupo de procesos en primer plano del terminal harán que se envíen señales ( SIGTTINy SIGTTOUrespectivamente) al proceso que las invoca, y pueden hacerlo opcionalmente (respectivamente). Varias funciones de biblioteca que alteran el modo terminal tienen el mismo comportamiento que write(), excepto que siempre generan las señales, incluso si esa funcionalidad está desactivada para write()sí misma. [36] [37]

La termiosestructura de datos

La estructura de datos utilizada por todas las llamadas a la biblioteca de terminal es la termiosestructura [38] cuya definición en lenguaje de programación C y C++ es la siguiente: [34]

struct termios { tcflag_t c_iflag ; // Modos de entrada tcflag_t c_oflag ; // Modos de salida tcflag_t c_cflag ; // Modos de control tcflag_t c_lflag ; // Modos locales cc_t c_cc [ NCCS ] ; // Caracteres de control } ;                       

El orden de los campos dentro de la termiosestructura no está definido, y las implementaciones pueden agregar campos no estándar. [34] De hecho, las implementaciones deben agregar campos no estándar para registrar las velocidades de entrada y salida en baudios. Estos se registran en la estructura, en un formato definido por la implementación, y se accede a ellos a través de funciones de acceso, en lugar de mediante la manipulación directa de los valores de campo, como es el caso de los campos de la estructura estandarizada. [39]

Los alias de los tipos de datos tcflag_ty cc_t, así como la constante simbólica NCCSy las constantes simbólicas para los distintos indicadores de modo, los nombres de los caracteres de control y las velocidades en baudios, se definen en un encabezado estándar termios.h. (Esto no debe confundirse con el encabezado de nombre similar termio.hde System III y System V, que define una termioestructura similar y muchas constantes simbólicas con nombres similares. Esta interfaz es específica de System III y System V, y el código que la utiliza no necesariamente será transferible a otros sistemas). [40]

Los campos de la estructura son (en resumen, para más detalles véase el artículo principal [ aclaración necesaria ] ):

c_iflag
Indicadores de modo de entrada para controlar la paridad de entrada, la traducción de nueva línea de entrada, el control de flujo del módem , la limpieza de 8 bits y la respuesta a una condición de "ruptura" (del puerto serie) [34]
c_oflag
Indicadores de modo de salida para controlar el posprocesamiento de salida definido por la implementación, la traducción de nueva línea de salida y los retrasos de salida después de que se hayan enviado varios caracteres de control [41] [27]
c_cflag
Indicadores de control de hardware del terminal para controlar el dispositivo terminal real en lugar de la disciplina de línea: la cantidad de bits en un carácter, el tipo de paridad, el control de suspensión y el control de flujo de línea serial [42]
c_lflag
Indicadores de control local para controlar la disciplina de línea en lugar del hardware del terminal: modo canónico, modos de eco, reconocimiento y manejo de caracteres de generación de señales, y habilitación de la generación de la SIGTTOUseñal por la write()llamada del sistema [39]

Las funciones de la biblioteca son (en resumen, para más detalles consulte el artículo principal [ aclaración necesaria ] ):

tcgetattr()
Consultar la configuración de atributos actual de un dispositivo terminal en una termiosestructura [43]
tcsetattr()
Establece la configuración de atributos actual de un dispositivo terminal desde una termiosestructura, opcionalmente esperando que se vacíe la salida en cola y vaciando la entrada en cola [43]
cfgetispeed()
Consultar la velocidad en baudios de entrada desde los campos definidos por la implementación en una termiosestructura [44]
cfgetospeed()
Consultar la velocidad en baudios de salida desde los campos definidos por la implementación en una termiosestructura [44]
cfsetispeed()
Establece la velocidad en baudios de entrada en los campos definidos por la implementación en una termiosestructura [44]
cfsetospeed()
Establece la velocidad en baudios de salida en los campos definidos por la implementación en una termiosestructura [44]
tcsendbreak()
envía una señal de "interrupción" del módem a un terminal de dispositivo en serie [45]
tcdrain()
esperar a que se agote la salida en cola [45]
tcflush()
descartar entrada en cola [45]
tcflow()
control de flujo de cambios [45]
tcgetpgrp()
Consultar el grupo de procesos en primer plano del terminal [46]
tcsetpgrp()
Establece el grupo de procesos en primer plano de la terminal [46]

Caracteres especiales

El c_cc[]miembro de la matriz de la termiosestructura de datos especifica todos los caracteres especiales (modificables mediante programación). Los índices de la matriz son constantes simbólicas, una para cada tipo de carácter especial, como se muestra en la tabla de la derecha. (Otras dos entradas de la matriz son relevantes para el procesamiento de entrada en modo no canónico y se analizan a continuación). [43]

Los caracteres especiales que no se pueden modificar mediante programación son el salto de línea (ASCII LF) y el retorno de carro (ASCII CR). [47]

Procesamiento de entrada

El procesamiento de entrada determina el comportamiento de la read()llamada al sistema en un dispositivo terminal y las características de edición de línea y generación de señales de la disciplina de línea. A diferencia del caso de Unix de Séptima Edición y BSD versión 4, y al igual que el caso de System III y System V, la edición de línea opera en uno de sólo dos modos: modo canónico y modo no canónico. La diferencia básica entre ellos es cuándo, desde el punto de vista de los requisitos de bloqueo/no bloqueo de la read()llamada al sistema (especificados con el O_NONBLOCKindicador en el descriptor de archivo mediante open()o fcntl()), los datos "están disponibles para lectura". [48]

Procesamiento en modo canónico

En el modo canónico, los datos se acumulan en un búfer de edición de línea y no se vuelven "disponibles para lectura" hasta que el usuario (en la terminal) finaliza la edición de línea enviando un carácter delimitador de línea . Los caracteres delimitadores de línea son caracteres especiales, y son fin de archivo , fin de línea y avance de línea (ASCII LF). Los dos primeros se pueden configurar mediante programación, mientras que el último es fijo. Los dos últimos se incluyen en el búfer de edición de línea, mientras que el primero no. [49]

En términos más estrictos, se acumulan cero o más líneas en el búfer de edición de línea, separadas por delimitadores de línea (que pueden o no descartarse una vez que read()se llega al momento de leerlos), y la edición de línea opera sobre la parte del búfer de edición de línea que sigue al último delimitador de línea (si lo hay) en el búfer. Por lo tanto, por ejemplo, el carácter de "borrado" (cualquiera que haya sido programado) borrará el último carácter en el búfer de línea solo hasta (pero sin incluir) un delimitador de línea anterior. [49]

Procesamiento en modo no canónico

En el modo no canónico, los datos se acumulan en un búfer (que puede ser o no el búfer de edición de línea; algunas implementaciones tienen colas separadas de "entrada procesada" y "entrada sin procesar") y se vuelven "disponibles para lectura" según los valores de dos parámetros de control de entrada, los miembros c_cc[MIN]y de la estructura de datos. Ambos son cantidades sin signo (porque se requiere que sea un alias para un tipo sin signo). El primero especifica un número mínimo de caracteres y el segundo especifica un tiempo de espera en décimas de segundo. [50] Hay cuatro posibilidades:c_cc[TIME]termioscc_t

c_cc[TIME]y c_cc[MIN]ambos son cero
En este caso, los datos en el búfer están "disponibles para lectura" inmediatamente y read()retorna inmediatamente con cualquier dato que esté en el búfer (potencialmente retornando cero si no hay datos disponibles). [51]
c_cc[TIME]es distinto de cero y c_cc[MIN]es cero
En este caso, los datos del búfer están "disponibles para su lectura" una vez transcurrido el tiempo de espera especificado, y el temporizador se activa al iniciarse la read()llamada al sistema o si se recibe un solo carácter. En otras palabras, read()espera un tiempo total máximo especificado y puede devolver cero datos, y devuelve cualquier dato tan pronto como se recibe. [51]
c_cc[TIME]es cero y c_cc[MIN]no es cero
En este caso, los datos en el búfer están "disponibles para lectura" después de que se haya recibido en el búfer la cantidad especificada de caracteres. En otras palabras, read()espera una cantidad mínima de datos (que puede ser mayor que la que el llamador está dispuesto a leer en la llamada del sistema), no devolverá datos cero y puede esperar indefinidamente. [51]
c_cc[TIME]y c_cc[MIN]ambos son distintos de cero
En este caso, los datos en el búfer están "disponibles para lectura" después de que se haya recibido la cantidad especificada de caracteres en el búfer o haya expirado el tiempo de espera desde que se ingresó el último carácter. No hay tiempo de espera para el primer carácter. En otras palabras, read()espera una cantidad mínima de datos (que puede ser mayor que lo que el llamador está dispuesto a leer en la llamada del sistema), no devolverá datos cero, puede esperar indefinidamente, pero no esperará más que el tiempo de espera especificado si al menos un carácter está en el búfer para ser leído. [51]

Procesamiento de salida

El procesamiento de salida no ha cambiado mucho con respecto a sus orígenes en System III/System V. Los indicadores de control del modo de salida determinan varias opciones:

Notas

  1. ^ abc Christian 1988, pág. 11.
  2. ^ Bourne 1983, pág. 6.
  3. ^ Ataúd 1991, pág. 820.
  4. ^ Ataúd 1991, pág. 23-24.
  5. ^ Leffler y col. 1989, pág. 259.
  6. ^ desde Coffin 1991, pág. 24.
  7. ^ Leffler y col. 1989, pág. 37–38.
  8. ^ Afzal 2008, pág. 419.
  9. ^ Frisch 2002, pág. 770.
  10. ^ desde Coffin 1991, pág. 115.
  11. ^ Ataúd 1991, pág. 372.
  12. ^ Ataúd 1991, pág. 779.
  13. ^ Ataúd 1991, pág. 751–752.
  14. ^ Leffler y col. 1989, pág. 265.
  15. ^ Leffler y col. 1989, pág. 103.
  16. ^ Leffler y otros 1989, pág. 38.
  17. ^ Leffler y col. 1989, pág. 260–261.
  18. ^ abc Leffler y otros 1989, pág. 262.
  19. ^ Cristiano 1988, pág. 395.
  20. ^ Bourne 1983, pág. 8.
  21. ^ Véase Bourne 1983, pág. 130-131.
  22. ^Ab Bourne 1983, pág. 287.
  23. ^Ab Christian 1988, pág. 26.
  24. ^ Bourne 1983, págs. 132-133.
  25. ^ Leffler y col. 1989, pág. 259–260.
  26. ^Ab Bourne 1983, pág. 288.
  27. ^ abcdefg Leffler y col. 1989, pág. 260.
  28. ^ Bourne 1983, pág. 132.
  29. ^ Bourne 1983, pág. 133.
  30. ^ Cristiano 1988, pág. 393.
  31. ^ Leffler y col. 1989, pág. 262–263.
  32. ^ "Fuente de la página del manual tty(4) de System III" . Consultado el 5 de octubre de 2012 .
  33. ^ abc Zlotnick 1991, pág. 157.
  34. ^ abcd Zlotnick 1991, pág. 163.
  35. ^ Bourne 1983, pág. 130.
  36. ^ Zlotnick 1991, pág. 158.
  37. ^ Zlotnick 1991, págs. 173-174.
  38. ^ Zlotnick 1991, pág. 162.
  39. ^ por Zlotnick 1991, pág. 166.
  40. ^ Zlotnick 1991, págs. 162-163.
  41. ^ Zlotnick 1991, pág. 164.
  42. ^ Zlotnick 1991, pág. 165.
  43. ^ abc Zlotnick 1991, pág. 167.
  44. ^ abcde Zlotnick 1991, pág. 169.
  45. ^ abcd Zlotnick 1991, pág. 172.
  46. ^ por Zlotnick 1991, pág. 174.
  47. ^ por Zlotnick 1991, pág. 159.
  48. ^ Zlotnick 1991, pág. 160.
  49. ^ por Zlotnick 1991, pág. 160–161.
  50. ^ Zlotnick 1991, pág. 161.
  51. ^ abcd Zlotnick 1991, pág. 161–162.
  52. ^ Bourne 1983, págs. 287-288.

Fuentes

Lectura adicional