cc31a Desarrollo de Software de Sistemas

Patricio Poblete (ppoblete@dcc.uchile.cl)

Entrada/Salida en el Terminal

El terminal posee características que hacen necesario darle un tratamiento especial, con un control más específico que lo que provee el modelo general de entrada/salida visto hasta ahora.

Antiguamente, esto se hacía con la función ioctl, pero esto ha sido reemplazado en POSIX.

En Unix, un terminal equivale a lo que sería una puerta asíncrona serial en un computador real.


        TERMINAL --- MODEM --- SIST. TELEFÓNICO --- MODEM --- HOST

La transmisión en Unix es full duplex. Esto significa que tanto el terminal como el host pueden transmitir simultáneamente. A menudo, esto también significa que por cada caracter que el host recibe, lo envía de inmediato de vuelta hacia el terminal como eco.

Esto último no es obligatorio. En lugar de eso:

Control del terminal

Se hace mediante una estructura de tipo


        struct termios

Las operaciones permitidas son:

Uso de tcgetattr:


        #include <termios.h>
        #include <unistd.h>
        
        status=tcgetattr(fd, p);
            /* status==0 ==> OK, -1 ==> error
               fd es un file descriptor abierto, debe ser un tty
               p es un puntero a un struct termios */

¿Cómo saber si un fd es un tty?


        #include <unistd.h>
        
        s=ttyname(fd);
            /* retorna un puntero al nombre del terminal,
               o bien NULL si no es un tty */

Uso de tcsetattr:


        status=tcsetattr(fd, option, p);
            /* option puede ser
                TCSANOW:   el cambio es inmediato
                TCSADRAIN: cuando se termine de transmitir el output ya escrito
                TCSAFLUSH: igual a DRAIN + ignorar input pendiente */

Estructura termios:

Ejemplo: Lectura de password.

  1. Escribir "Password: "

  2. Descartar caracteres tipeados antes del prompt

  3. Apagar el eco

  4. Leer password

  5. Restaurar el eco

password.c


        #include <termios.h>
        #include <stdio.h>
        #include<sys/types.h>
        #include <unistd.h>
        
        int getpw(char *buf, unsigned size)
            /* retorna número de caracteres leídos, o -1 si hubo error */
          {
            struct termios attr;
            int n;
        
            printf("Password: ");
            fflush(stdout);
        
            /* apagar eco */
            if(tcgetattr(STDIN_FILENO, &attr)!=0)
                return -1;
            attr.c_lflag &= ~ECHO;
            if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr)!=0)
                return -1;
        
            n=read(STDIN_FILENO, buf, size);
            buf[n-1]='\0';
        
            /* encender eco */
            attr.c_lflag |= ECHO;
            if(tcsetattr(STDIN_FILENO, TCSANOW, &attr)!=0)
                return -1;
        
            return n;
          }
        
        #define MAXPW 100
        main()
          {
            char pw[MAXPW+1];
        
            sleep(5);
            if(getpw(pw, MAXPW)>=0)
                printf("\n\tpassword=%s\n", pw);
          }

c_iflag

IGNPARIgnora bytes con error de paridad
INPCKActiva chequeo de paridad
ISTRIPBorra bit de paridad
INLCRNL==>CR
IGNCRIgnora CR
ICRNLCR==>NL
IXONEl terminal controla el flujo con XON/XOFF (^S/^Q)
IXOFFEl computador controla el flujo

c_cflag

CLOCALIgnorar líneas de status del modem
CREADHabilitar recepción
CSIZECS5, ..., CS8 (bits/byte)
CSTOPB0==>enviar 1 stop bit, 1==>enviar 2 stop bits (raro)
HUPCLColgar la línea al logout o cuando todos los procesos mueren
PARENBActivar paridad (generación y chequeo)
PARODD0==>even, 1==>odd (sólo si PARENB está activo)

c_lflag

ECHOHacer eco
ECHOEERASE borra el último carácter
ECHOKKILL (^U) borra la última línea
ECHONLHacer echo de \n incluso si estamos sin eco
ICANONActiva procesamiento de ERASE y KILL
ISIGGenera signals con caracteres INTR, QUIT, SUSP
TOSTOPEnvía señal SIGTTOU al proceso que intenta escribir en este terminal y no está en el foreground

c_cc


        char c_cc[NCCS];

Si ICANON está activo:

Subíndice
VEOFEOF char (^D)
VEOLEnd of line (^J)
VERASEERASE (^H o DEL)
VINTRInterrupt (^C o DEL, genera SIGINT)
VKILLKill char (^U)
VQUITQuit char (^\, genera core?)
VSUSP^Z
VSTART^Q
VSTOP^S

Si ICANON no está activo:

Subíndice
VINTRInterrupt (^C o DEL, genera SIGINT)
VMINVTIME==0 ==> número de caracteres a leer
VTIME!=0 ==> timeout después de VTIME/10 segundos
VQUITQuit char (^\, genera core?)
VTIMEVMIN==0 ==> el read se satisface con el primer caracter, o bien timeout después de VTIME/10
VMIN!=0 ==> timeout
VMIN==0 && VTIME==0 ==> read retorna lo que haya, sin esperar
VSTART^Q
VSTOP^S
VSUSP^Z

Velocidad del terminal


        speed_t cfgetispeed(p)
        speed_t cfgetospeed(p) /* p es de tipo "struct termios *" */
        
        status=cfsetispeed(p, speed);
        status=cfsetospeed(p, speed);
            /* speed= B50, B75, ..., B19200, B38400 */
            /* speed==B0 ==> hangup */

Funciones de control


        tcsendbreak(fd, duracion); /* duracion==0 ==> 250-500 ms */
        
        tcdrain(fd);
            /* Espera hasta que todos los datos escritos via write ya hayan salido */
            /* Si se usa fprintf, hay que hacer fflush primero */
        
        tcflush(fd, option);
            /* Descarta datos en entrada/salida */
            /* option= TCIFLUSH, TCOFLUSH, TCIOFLUSH */