Introducción
Conocer cómo funciona la interacción entre un ordenador UNIX y los
diferentes tipos de terminales desde los que podemos conectarnos a él
no es fundamental para el usuario medio, pero sí es interesante para
resolver algunos tipos de problemas habituales, para saber para qué
sirve la variable
TERM, etc. Este es un tema relativamente complejo
y sobre el que existe poca documentación clara y asequble, de ahí el
motivo de este documento.
Es interesante, en primer lugar, hacer notar la diferencia entre el trabajo
en un PC y en UNIX. En un PC el teclado, la pantalla y el ratón forman
parte de la máquina misma, de forma que el ordenador controla todos
ellos y sabe perfectamente qué tecla se ha pulsado en cada momento o
cómo controlar la pantalla. Aunque también en este entorno pueden
surgir problemas de configuración, siempre serán menos numerosos
que en UNIX.
Cuando trabajamos en UNIX o en cualquier otro sistema operativo
multiusuario, nuestro teclado y nuestra pantalla no suelen estar
conectados fisicamente al ordenador, salvo en el caso de las versiones
UNIX de PC o estaciones gráficas; pero incluso en esos casos, si
nos conectamos a otros ordenadores, empiezan a surgir los temas
de los que vamos a hablar aquí.
Principios básicos
En los
primeros tiempos de UNIX (y por tanto es el que orientó a sus diseñadores
en la forma de gestionar terminales), hoy no es el más habitual. Me refiero
a un simple terminal "tonto" (también llamados terminales ASCII,
terminales VT, dumb terminals, etc) conectado mediante un
cable serie RS232 al propio ordenador. Por un cable serie sólo
viajan bytes, uno detrás de otro, tanto en dirección del terminal
al ordenador (cosa que ocurre cuando pulsamos las teclas) como en el
sentido contrario (caso de lo que sale por pantalla). Veamos qué
ocurre cuando pulsamos una tecla normal, como la de la letra "a":
- El terminal envía por el cable serie el código ASCII de la letra "a" (97) por el cable serie.
- El ordenador lo recibe y se lo pasa al driver de terminales. Un driver es una parte del sistema operativo que gestiona un tipo concreto de dispositivos, sean terminales, discos, unidades de cinta, etc. Cada driver controla todos los dispositivos del mismo tipo que tenga el ordenador.
- El driver de terminales comprueba de qué terminal concreto
ha llegado el carácter, y a continuación qué parámetros están
configurados para él (estos parámetros se configuran con el
comando
stty). Más adelante hablaremos de esto. El caso más normal es que el byte que ha llegado se entregue al programa que se está ejecutando en el terminal y asimismo se devuelva por el cable serie al terminal (eco). Este al ver que llega el byte 97, ve que corresponde al código ASCII de la letra "a" y hace aparecer ese carácter por la pantalla. Un caso en que no ocurre así en cuando al conectarnos se nos pide la clave y vemos que al escribirla no aparece por la pantalla. Es así porque el programa encargado de pedir el nombre de usuario y la clave (el programa login) cuando pide la clave desactiva el parámetro eco del terminal. - El programa que se ejecuta en el terminal ha recibido también el código 97, comprueba que es el carácter "a" y hace con él lo que considere adecuado. Como aclaración, los programas que escribe un usuario normalmente no leen letra por letra, sino que llaman a funciones del lenguaje de programación que sea, por ejemplo para leer una línea del teclado y dicha función se encarga de leer caracteres hasta que el usuario pulsa la tecla Intro.
Vemos que lo que parece tan sencillo en realidad implica varios pasos.
En particular sorprende al principio el hecho de que cada pulsación
del usuario casi siempre viaja dos veces (excepto cuando se ha desactivado
el eco): de la terminal al ordenador y vice-versa. Como curiosidad,
diremos que el sistema operativo debe interrumpir lo que esté haciendo
cada vez que recibe un carácter de algún terminal para que el
driver de terminales, que a fin de cuentas es una parte del
propio sistema operativo, se haga cargo de él. Aún más grave es
cuando estamos conectados a UNIX a través de la red, en vez de por un
cable serie directo. En este caso cada pulsación y cada respuesta
implican un paquete TCP, cuya manipulación requiere más tiempo de
CPU.
Esto en realidad no es tan grave como parece, debido a la rapidez de la
CPU, aunque otros sistemas
operativos solucionan estos inconvenientes con terminales más inteligentes
que se encargan de todos los detalles de bajo nivel y entregan al
sistema operativo líneas o incluso pantallas completas, pero UNIX
tiene la ventaja de que este esquema le permite funcionar con casi
cualquier tipo de terminal.
Podemos comprobar también la importancia del código ASCII. Puesto
que por los cables sólo viajan bytes (números, a fin de cuentas),
tiene que existir un acuerdo entre todas las máquinas para saber
qué letra corresponde a cada número y vice-versa. He ahí el sentido
de este código.
Como hemos dicho, la conexión por terminales asíncronos conectados
directamente al ordenador no es actualmente lo más habitual. Hoy las redes
mandan. Sin embargo, lo fundamental de lo explicado más arriba no
varía, por tanto seguiremos hablando sobre este modelo y más adelante
explicaremos otras formas de conexión.
El teclado
Ya hemos dicho que todo lo que pulsamos en el teclado y todo lo que
el ordenador envía a nustra pantalla viajan por el mismo cable serie.
Aunque nos conectemos a través de red local y por tanto no exista ese
cable serie, ocurre lo mismo, en el sentido de que existe un único
canal lógico bidireccional entre nuestro teclado y pantalla y el ordenador.
Ya hemos explicado también el proceso que ocurre cuando pulsamos una
tecla como la letra "a". Lo mismo sucede al pulsar cualquier otra tecla
que corresponda a un carácter incluido en el código ASCII. Surgen ahora
dos dificultades:
- Las teclas como letras acentuadas, ñ, y otras no incluidas en el código ASCII.
- Las teclas especiales como las de movimiento del cursor, teclas de función, etc.
Caracteres especiales
Un inconveniente del código ASCII es que define códigos numéricos
de un byte para los caracteres. Esto da un máximo de 265. Teniendo
en cuenta que históricamente los canales de comunicación que se
usaban usaban sólo 7 bits en vez de 8, y que este código se
definió para acomodarse a cualquier canal, sólo usa 7 bits, lo
que permite un máximo de 128 caracteres. De ellos, no todos corresponden
a letras; también hay códigos especiales como el espacio (32),
el salto de línea (10), el retorno de carro (13), el salto de página
(12), y muchos otros. Por tanto los únicos caracteres que contempla
este código son las letras mayúsculas y minúsculas, los dígitos
(0 a 9) y pocos más. Eso es suficiente para el idioma inglés,
pero no es suficiente para muchos otros idiomas. En particular
los caracteres españoles como letras acentuadas, ñ, interrogación
y admiración abiertas, no están en ese código.
Para suplir estas deficiencias, se definieron otros códigos para
contemplar otros idiomas. Uno de los más extendidos es el ISO 8859/1,
también llamado ISO Latin 1. Es éste un código de 8 bits (por tanto
soporta hasta 256 códigos) que incluye los mismos que ASCII más los
caracteres usados por la mayoría de los lenguajes occidentales. Casi
todas las variantes de UNIX usan internamente este código; entonces,
¿ qué problema hay ?.
Por un lado, el teclado que estemos usando debe estar correctamente
configurado para que cuando yo pulse la secuencia de teclas que
componen la "á" ("a" acentuada): ' seguido de a, genere
el código ISO Latin 1 y envíe el ordenador ese código. Eso se hace:
- Terminales asíncronas
- Basta con configurarlas indicándoles que usen el teclado español.
- PCs
- Sea cual sea la forma en que nos conectemos desde un PC a UNIX (existen varias), el PC se debe configurar para que use teclado español (normalmente con el comando
keyb). - Terminal X o estación gráfica
- En este caso es responsabilidad de los administradores de sistemas su correcta configuración, que es algo más compleja.
Por otro lado, la línea de comunicación que tengamos con la máquina
UNIX debe funcionar a 8 bits, como ya hemos dicho. Si esta
línea está compuesta de varios pasos (como puede ser cuando nos
conectamos desde casa con un modem), todos ellos deben funcionar
a 8 bits. Como curiosidad, la causa más común de que si enviamos un
mensaje con caracteres españoles por correo electrónico estos
no lleguen correctamente a su destino es que el mensaje puede
pasar por varios ordenadores UNIX antes de ser entregado y alguno
de ellos puede hacer la transmisión a 7 bits (el estandar de correo
electrónico no obliga a usar 8 bits). Hay que decir también que
aunque el mensaje llegue bien el destinatario debe tener su terminal
bien configurada según lo que estamos explicando en este documento a fin
de leerlo correctamente.
Puede ocurrir que la línea de comunicación funcione a 8 bits pero el
driver de terminales del sistema operativo que controla nuestra
terminal sólo funcione a 7 bits. Esto se controla mediante uno de los
parámetros del driver que mencionamos anteriormente y que se
manipulan mediante el comando
stty. Este caso concreto se
arregla con el comando:
stty -istrip cs8
Teclas especiales
Las teclas de movimiento del cursor o las teclas de función no tienen
ningún código asignado. De hecho al pulsar una de esas teclas se generan
varios bytes que viajan por la línea de comunicación al ordenador.
Este debe reconocer esa secuencia y saber qué tecla hemos pulsado. El
primer byte de la secuencia siempre es el mismo, el 27 (ESC). Cuando
el programa que estemos usando recibe un código 27, sabe que probablemente
hemos pulsado una tecla especial, y por tanto analiza los códigos
siguientes para saber qué tecla es. El problema aquí es que no todos los
terminales tienen las mismas teclas y la misma tecla en distintos
terminales no siempre genera los mismos códigos en todos ellos.
Aquí existe infinidad
de terminales de distintas marcas y no es posible establecer un estandar
como ASCII o ISO Latin 1.
Esto se resuelve en UNIX mediante una pequeña base de datos que contiene
las secuencias de las teclas especiales para muchos tipos de terminales.
Esta base de datos figura tradicionalmente en el fichero
/etc/termcap
(de terminal capabilites) y en los UNIX derivados de System V
en el sistema llamado terminfo.
Los programas que usan las teclas especiales (como los editores y los
programas que usan menús) necesitan saber qué tipo de terminal tenemos,
para obtener la información que necesita de esa base de datos. Esto lo
hace obteniendo el valor de la variable TERM. Si ésta contiene
el valor vt220, el programa leerá al principio de su ejecución
de la base de datos la información sobre ese terminal para saber qué
códigos generan sus teclas especiales y así poder reconocerlas cuando
las reciba (en esa base de datos no sólo hay información sobre el teclado
sino también sobre los códigos de manejo de la pantalla, como veremos
más adelante).
He aquí la explicación del sentido y la importancia de la variable TERM.
El usuario debe preocuparse de que se corresponda con el tipo de terminal
que realmente tiene; si no es así, las aplicaciones a pantalla completa
no funcionarán bien. En la Universida de Córdoba está implantado el
Sistema LOGIN que configura la entrada de cada usuario a los
sistemas UNIX y que tiene en cuenta entre otras cosas este aspecto, por
lo que es difícil que surjan problemas de este tipo.
La pantalla
Con la pantalla ocurre algo parecido al teclado. Cuando un programa
que funcione a toda pantalla (como editores, programas con menús, etc)
necesita borrar la pantalla o poner una línea en video inverso debe
enviar una secuencia de códigos a la misma (a través de la línea de
comunicación que existe entre nosotros y el ordenador). Como ya habrás
adivinado, estos códigos no son iguales para todos los tipos de terminales.
El programa debe saber, también a través de la variable
TERM la terminal
que estamos usando para leer de la base de datos de terminales (sea
/etc/termcap o terminfo) los códigos de manejo de nuestra
pantalla.
Muchas veces ocurre que al ejecutar un editor la pantalla se llena de
caracteres raros o desordenados. La causa más común es que la variable
TERM no tenga el valor correcto.
Disciplina de línea
Ya hemos dicho en un par de ocasiones que el driver de terminales
tiene unos parámetros que se pueden configurar. A la forma en que
funciona según esos parámetros se conoce con el nombre de
disciplina de línea, y el comando
Hasta que el usuario no pulsa la tecla INTRO, el programa no recibe nada. Es así porque el driver del terminal guarda lo que el usuario va escribiendo en un buffer y hasta que no recibe el carácter de la tecla INTRO no valida el contenido (y entonces entrega dicho contenido al programa que lo esté esperando).
Mientras el usuario escribe, puede pulsar una serie de teclas especiales conocidas por el driver para editar lo que escribe. Las más importantes son (entre paréntesis ponemos los valores más habituales, donde la notación
Es la tecla que sirve para borrar los últimos caracteres escritos.
stty sirve para modificar
su comportamiento. Ya hemos indicado que en el sistema operativo existe
un único driver para cada tipo de dispositivo, por tanto sólo existe
uno para controlar todas las terminales. Sin embargo cada terminal
tiene su propio conjunto de parámetros, es decir, su propia
disciplina de línea, y cada usuario puede ejecutar el comando
stty para cambiar la suya. Pongamos un ejemplo:
Supongamos que hemos escrito un programa en el que en un momento dado
pedimos al usuario que introduzca algún dato por el teclado. Esto
es muy común y en el lenguaje C se suele hacer mediante una función
como scanf o gets. Cuando el usuario está ejecutando el programa
y llega a este punto, escribe lo que se le pide. Supongamos que se
equivoca y tiene que usar la tecla de borrado para hacer las correcciones.
En cuanto pulsa la tecla INTRO, lo que ha escrito es recibido por el
programa. Hay que hacer notar varias cosas:Hasta que el usuario no pulsa la tecla INTRO, el programa no recibe nada. Es así porque el driver del terminal guarda lo que el usuario va escribiendo en un buffer y hasta que no recibe el carácter de la tecla INTRO no valida el contenido (y entonces entrega dicho contenido al programa que lo esté esperando).
Mientras el usuario escribe, puede pulsar una serie de teclas especiales conocidas por el driver para editar lo que escribe. Las más importantes son (entre paréntesis ponemos los valores más habituales, donde la notación
^u, por ejemplo, significa pulsar la tecla Control y la u):
erase (^h o ^?)Es la tecla que sirve para borrar los últimos caracteres escritos.
kill (^u)intr (^c)
Es la tecla que, cuando es recibida por el driver,
corta el programa que se esté ejecutando en el terminal.
Según esto, si el usuario está escribiendo algo y quiere anularlo, y volver a empezar, debe pulsar ^u. El parámetro que debe cambiar más a menudo el usuario es el de la tecla de borrado (erase), debido a que algunos terminales generan un código al pulsar la tecla de borrado y otros generan otro diferente; unos generan el código 8 (^h) y otros el 127 (^?). Si el driver está configurado para que la tecla de borrado sea ^? y nos conectamos desde una terminal cuya tecla de borrado genera el código 8 ^h, cuando queramos borrar ocurrirá lo siguiente:
en un lugar da ^HVemos que al intentar borrar la letra "a" para poner una "e", en vez de borrarse aparece el código generado por nuestra tecla de borrado, que no es el que espera el driver para borrar. Igualmente sucede a menudo el caso contrario: el driver tiene configurada para borrar la tecla que genera el 8 (^h) y la tecla de borrar de nuestro terminal genera el 127 (^?). Entonces aparecería:en un lugar da^?Esto ocurre a menudo porque hay gran cantidad de terminales de ambos tipos. La forma de solucionarlo es la siguiente:stty erase <-Donde hemos puesto<-para indicar que ahí se pulsa nuestra tecla de borrado. Si esto ocurre a menudo o siempre que nos conectamos, podemos poner una de las dos líneas siguientes en nuestro fichero.cshrc:stty erase "^h"stty erase "^?"De esta forma, literalmente, también se puede hacer, pero aquí tenemos que conocer el código que genera nuestra tecla de borrado. De la misma forma podemos cambiar los caracteres para las operacioneskillointr.
Todo lo explicado hasta ahora lo controla el driver de terminales,
que intercepta todo lo que le llega al programa del usuario. Este no
puede alterar ese comportamiento si no es modificando los parámetros
del driver. Por ejemplo, un programa como un editor debe reconocer
cuándo ha pulsado el usuario las teclas de movimiento del cursor
inmediatamente, sin esperar hasta que el usuario pulse INTRO. Para
ello debe modidificar los parámetros del driver, o sea, la disciplina
de línea. La forma de hacer esto desde un programa C no es la misma
en todas las versiones de UNIX, pero normalmente está explicado en las
páginas de manual de
termio o de termios, según la versión.
La librería curses incluye muchas funciones para situar el
cursor en pantalla, crear ventanas, etc. Ver su página de manual para
más información.
Un caso que no se explica con esto son los programas como muchos juegos,
en los que el programa no está esperando hasta que el usuario pulse
algo desde el teclado, sino que la acción sucede continuamente y el
usuario pulsa ocasionalmente teclas para diversas acciones. Un programa
de este tipo por supuesto debe enular el parámetro que hace que la
entrada del usuario sólo se reciba cuando éste pulsa INTRO (como
los editores, programas de menú, etc), sino también tener la posibilidad
de averiguar regularmente si el usuario ha pulsado algo, y si no seguir
con lo que esté haciendo. Este no es un problema ya de terminales
sino de la función del sistema operativo que se esté usando para leer
la entrada del terminal. Normalmente estas funciones se quedan esperando
hasta que el driver de terminales entregue algo (o sea, hasta que el
usuario pulse alguna tecla o INTRO, según sus parámetros). El
comportamiento que nos interesa en este caso se llama
lectura no bloqueante,
y existen distintos mecanismos en UNIX para utilizarlo.
Control de flujo
Otra de las cosas de las que se encarga el driver de terminales
es el control de flujo, entendido aquí como la posibilidad del usuario
de interrumpir momentaneamente la salida a pantalla (por ejemplo, si ésta
es muy abundante y no da tiempo a leerla) y hacer luego que continue.
Hay dos caracteres para eso, que mencionamos junto con sus nombres
para el comando
stty (entre paréntesis, sus valores por defecto):
stop(^s)- Al pulsar esa tecla, la salida a pantalla se detiene.
start(^q)- Al pulsarla, se desbloqeua la salida.
Esto produce una de las confusiones más comunes a los usuarios, que
pulsan sin darse cuenta
^s y piensan que la pantalla se ha bloqueado.
Cuando ocurra este síntoma, lo primero que se debe intentar es pulsar
^q.
Otras opciones del comando
stty
stty -a- Nos informa de todos los parámetros actuales de nuestra terminal. Al principio salen las teclas asignadas y algunos otros valores. A continuación, algunos parámetros que pueden estar activados o no. Si no lo están, aparecen con un guión delante. Así,
icanonindica que ese parámetro está activado, pero-istripindica que este otro no lo está. - La opción
-echosirve para desactivar el eco al terminal. Esto se explicó al principio de este documento. La opciónechovuelve a activarlo. Esto se puede usar desde una shell-script que tenga que pedir una clave y que ésta no aparezca en pantalla. stty -istrip cs8- Este caso ya lo comentamos antes. Se usa cuando sospechamos que el driver está funcionando a 7 bits en vez de a 8. El síntoma es que no vemos caracteres acentuados, ñ, etc, ni tampoco podemos escribirlos.
stty rows 24 cols 80- Con eso indicamos el tamaño de la pantalla. Sólo es necesario a veces, sobre todo al conectarnos a otro ordenador si al usar un editor no sale bien (en ese caso, por supuesto lo primero que hay que mirar es que la variable
TERMtenga el valor adecuado).
stty echo, stty -echo
Ya hemos comentado en varias ocasiones el comando
stty para
adecuar a nuestro gusto la disciplina de línea de nuestro terminal.
En la página de manual del comando (así como en las de termio o
termios) se mencionan todas las opciones. Vamos a comentar ahora
algunas de las más usuales.
También hemos dicho que los editores y otros programas a toda pantalla
alteran la disciplina de línea para leer inmediatamente las pulsaciones
del usuario. Cuando uno de estos programas termina, debe dejar el
terminal en el estado en el que estaba inicialmente, que es el adecuado
para el uso de una shell interactiva. Sin embargo, puede que el programa
aborte repentinamente o lo cortemos con ^c. En ese caso la terminal
no queda en el estado apropiado. Síntomas típicos son que no vemos
lo que escribimos, que al pulsar INTRO el indicador de la
shell aparece al lado y no en la línea siguiente, etc. La forma
de arreglarlo es: ^jstty sane ^j
Exactamente esa secuencia, sin pulsar INTRO hasta después de ella.
El comando
stty sane pone al terminal en un estado "sano". El
^j es porque UNIX usa el código 10 como salto de línea, por tanto
es el código que espera para aceptar una orden. La tecla INTRO
suele generar el 13, y una de las cosas que hace el driver de
terminales es cambiarlo por un 10. Como en este caso el driver
se ha quedado "indispuesto", tampoco hace eso. Al pulsar ^j
generamos directamente un carácter 10.
Emuladores de terminal y otros tipos de conexión
Como hemos dicho, la conexión por terminales asíncronos conectados
directamente al ordenador no es actualmente lo más habitual.
Aunque todo lo dicho hasta ahora se aplica a cualquier tipo de
conexión con un sistema UNIX, vamos a comentar algunas
peculiaridades de otros tipos de conexión.
Un emulador de terminal es un programa que nos permite trabajar
como si estuviéramos en una terminal pero sin estarlo. Por ejemplo,
un PC no es un terminal en el sentido que hemos considerado hasta
ahora, es decir, cada tecla no envía un código y la pantalla no
se gestiona con códigos, sino directamente por los programas que
se ejecutan en el PC. Cuando conectamos el PC a un ordenador UNIX
por red local o mediante un cable serie, se necesita un programa
que "simule" el comportamiento de una terminal. Además deben
simular a un tipo de terminal más o menos estandar, uno que esté
registrado en la base de datos de terminales de los sistemas
UNIX, como ya mencionamos. Ejemplos de estos programas
para PC son Kermit, ProComm, HyperTerminal, etc.
Casi todos ellos simulan a terminales VT (vt100, vt200, etc) que
son los más ampliamente utilizados. Por tanto, al conectarnos
con ellos a UNIX, si la terminal no funciona correctamente habrá
que poner el valor de la variable
TERM a vt110 o
vt200 probablemente. Estos programas suelen tener una
configuración en la que podemos elegir el tipo de emulación, así
como otros parámetros como si deben funcionar a 7 u 8 bits, qué
código genera la tecla de borrado (suelen dar dos opciones:
BS, que es el código 8 o ^h y DEL, que es el 127 o
^?).
En una terminal X o en una estación gráfica ocurre lo mismo. En este
caso se suelen usar los programas xterm o cmdtool (éste
último es propio de los ordenadores SUN). Los valores que debe tomar
la variable TERM en estos casos es xterm y sun-cmd,
respectivamente. Al ejecutar uno de estos programas, la variable
TERM se pone automaticamente, por lo que no hay problema. Pero
si luego nos conectamos a otro ordenador con rlogin, su
valor se perderá y puede que haya que ponerlo.
- telnet y rlogin
- El programa telnet está disponible en muchos tipos de ordenadores y sistemas operativos diferentes; permite que nos conectemos a través de una red local a otro ordenador, normalmente UNIX (también podemos conectarnos por telnet a otros tipos de sistemas operativos). rlogin es semejante, sin embargo es practicamente exclusivo de UNIX. En primer lugar, hay que decir que rlogin nos conecta a otro ordenador a 7 bits. Esto significa que los caracteres acentuados, ñ, etc no podremos verlos ni escribirlos. Para que se conecte a 8 bits hay que ejecutar:
rlogin ordenador -8 - Terminales X y estaciones gráficas
- pp Lo único que comentaremos aquí es que puede haber problemas con los caracteres especiales debidos no sólo a los factores comentados en este documento, sino relacionados con tipos de letra y configuración de teclados, que en estos casos es algo complicada. Si surgen este tipo de problemas, deben ponerse en contacto con los administradores de sistemas.
No hay comentarios:
Publicar un comentario