
            Guia Linux de Programacion (GULP)

             |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||_||*
 *||||||||
            ||||||||||||||||||||||||||||||||||||_||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||*
 *|_||||||||||||||||||||@
             |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||_||||*
 *||||||||||||||||||||||@
               ||||||||||||||||||||||||||||||||_||||||_||||||||||_|||_||||||||||||||||_||||||||_|||||||||||||||||||||||*
 *_||_||_|||||||||||||||@
               ||||||||||||||||=||_|_|||||||||||||||||||||||||||||||||||||||||||||||_||||||||||||_|||||||||||||||||_|||*
 *_|_|||||_|||||||||||||@
                ||||||||||||||||||||||||||||_|||||||_||||||||||||||||||||||||_|||||||||_|||_||||_||_||||__||_||||__|||_*
 *|||__|||||||||_|||||||@
                  ||||||||||||||||||||||||||=||||||||||||||||_|||_||||_||_||||_|__||||||=||||||||||_|_||_|||||_||||||||*
 *|||||_|||_||||||||||||@
                   |||||||||||||||||||||||||||||||||||_|||||||||||||||||_||||||__|||||||||||||||__||||||||||||||_|__|||*
 *|||||||||||_|||||||_||@
                   |||||||||||||||||||||||||||||||||||||||||||||||||||||_|_|||_||_||||||||_||||||||||||_||__|_||_||||||*
 *|_|||||||||||||||||||_@
                     ||||||||||||||||||||||||||||||||||||||||||||||||||||||_|_|_||||||||||||||||||||||_||||||||||||||||*
 *||_|||_||||_||||||_|||@
                      |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||_|_||_|_|||||||||_|||||||||_*
 *||||||_||||||_|||||__|@
                     |||||||||||||||||||||||||||||||||||||||||||||||||||||||||=||||||||||||_|_||||||||=|_|||||||||_|_||*
 *||||||||||||||||_||___@
                    _||||||||||||||||||||_|||||_||||_____________________|__||_||_
         ===========|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||==|=||==|||===_||_|_|_|
         ===========|||||||||||||||||||||||||||||||||||||||||||||||||||||==||||||||||_||_|_|__|_|||_|||||||||||__|_||||*
 *|||||_|_||||||||||||__@
                   =|||||||||||||||||||||||||||==||||_|||_|_|||_||||_|||||_|||_||||||||||__||||_|||||||||||||||||||||||*
 *||||||_|||||_|_||||||_@
                ====||||||||||||||||||||||||==||||=|=||=|======|=|||=|=||||=||||||=||__|||__|_||__||_||______||____||__*
 *||||||||||||||||||||||@
                             |||||||_|||||||||_|||||_|||__|||_||_|____||_|||_|_|||||||||||_||__|||||||||||||||__||||_||*
 *|_|||_|||_||||__|||||_@
                              _||_|||__|||__|||||||_|||||_|__||||||||_|||||||||||||||||||____|_||__||__|_|_||____|||__|*
 *||_|||||||||||__|_|__|@
                               __||||_|||_||_|||___|||_|__||||__||__|||||||||_||||||||||||_|||||||_||||||_|||||__||__|_*
 *_|||_|||||_|___|||_|||@
                                |||||_|||||||||||__||___|||||||||||||_||||__|||_||||_|__|_|_|___|||_|||||_|___|||||_|||*
 *__|||__|__||||||||||_|@
                                _|||_|||||||__||__|||||||||||_||||_|||_||||__||_|_|||||||||||_|||||||_||||||_||_||||_||*
 *|||__|||||||_||||__|||@
                                  __|||||_|||__|__|||_|||_|||||||_||||||||______|||||__||||||_|||__|___|||||_||||||_|||*
 *|||_|||__||||_|_|_|__|@
                                   |_|||_||||||||||||_||_||||__|___|||_|___|||||||||||||||||||||||||_||||||||||||||||||*
 *|||||||||_|||||||||||_@
                                    |||||||||||_|_||||||___||_|_||__||__||__|_||||____|_|||||_||_||_|||___|_||||___|_||*
 *|||||||||_||||||||||||@
                                     _|||_|||||||_|_||||__||||_|||____||__|_|||_|___||||||||____||||||_||||_||__||__|__*
 *||||__|||_|
                                      _|||_||||_____||||||||||||_|||||__|||__|__|_||_||_|_||||__||_|__|_||||||
                                       |_||_|_|__||||__||||||||_||||||_|_|||||||_||||||__||_|_||||||
                                        |_||_|_||||||___|_||__||||
                                         |||


                                   Sven Goldt

                              Sven van der Meer

                                 Scott Burkett

                                  Matt Welsh



                                   Version 0.4

                                  Marzo 1995
   0...Nuestro objetivo permanente: mejorar nuestro conocimiento de C, explorar ex-

tra"nos comandos Unix y to boldly code where no one has man page 4.

Indice  General

1   El sistema operativo Linux                                             7


2   El nucleo de Linux                                                       9


3   El paquete libc de Linux                                               11


4   Llamadas al sistema                                                    13


5   Una llamada multiuso: "ioctl"                                        15


6   Comunicacion entre procesos en Linux                              17

    6.1   Introduccion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  17

    6.2   Pipes UNIX Semi-duplex   . . . . . . . . . . . . . . . . . . . . . .  17

          6.2.1  Conceptos basicos   . . . . . . . . . . . . . . . . . . . . . .  17

          6.2.2  Creacion de tuberias en C  . . . . . . . . . . . . . . . . . .  19

          6.2.3  Tuberias, la forma facil de hacerlo   . . . . . . . . . . . . .  24

          6.2.4  Operaciones atomicas con tuberias  . . . . . . . . . . . . .  28

          6.2.5  Notas acerca de las tuberias semi-duplex:   . . . . . . . . .  29

    6.3   Tuberias con Nombre (FIFO - First In First Out) . . . . . . . . .  29

          6.3.1  Conceptos basicos   . . . . . . . . . . . . . . . . . . . . . .  29

          6.3.2  Creacion de una FIFO  . . . . . . . . . . . . . . . . . . . .  29

          6.3.3  Operaciones con FIFOs   . . . . . . . . . . . . . . . . . . .  31

          6.3.4  Acciones Bloqueantes en una FIFO . . . . . . . . . . . . .  32

          6.3.5  La Infame Se"nal SIGPIPE  . . . . . . . . . . . . . . . . . .  33

    6.4   IPC en Sistema V   . . . . . . . . . . . . . . . . . . . . . . . . . .  33

          6.4.1  Conceptos fundamentales   . . . . . . . . . . . . . . . . . .  33

          6.4.2  Colas de Mensajes  . . . . . . . . . . . . . . . . . . . . . .  35

          6.4.3  Semaforos  . . . . . . . . . . . . . . . . . . . . . . . . . . .  52

          6.4.4  Memoria Compartida   . . . . . . . . . . . . . . . . . . . .  71


7   Programacion del Sonido                                              79

    7.1   Programacion del altavoz interno  . . . . . . . . . . . . . . . . . .  79

    7.2   Programacion de una Tarjeta de sonido   . . . . . . . . . . . . . .  80


8   Graficos en modo caracter                                             81

    8.1   Funciones E/S en la libc  . . . . . . . . . . . . . . . . . . . . . . .  82

          8.1.1  Salida con Formato . . . . . . . . . . . . . . . . . . . . . .  82


                                         1

2                                                            INDICE GENERAL



         8.1.2  Entrada con Formato   . . . . . . . . . . . . . . . . . . . .  84

   8.2   La Libreria Termcap  . . . . . . . . . . . . . . . . . . . . . . . . .  85

         8.2.1  Introduccion   . . . . . . . . . . . . . . . . . . . . . . . . .  85

         8.2.2  Encontrar la descripcion del terminal . . . . . . . . . . . .  86

         8.2.3  Lectura de una descripcion de terminal   . . . . . . . . . .  86

         8.2.4  Capacidades de Termcap   . . . . . . . . . . . . . . . . . .  87

   8.3   Ncurses - Introduccion  . . . . . . . . . . . . . . . . . . . . . . . .  92

   8.4   Inicializacion   . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  94

   8.5   Ventanas   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  95

   8.6   Salida   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  98

         8.6.1  Salida con Formato . . . . . . . . . . . . . . . . . . . . . .  99

         8.6.2  Insercion de Caracteres/Lineas  . . . . . . . . . . . . . . .  99

         8.6.3  Borrado de Caracteres/Lineas . . . . . . . . . . . . . . . .100

         8.6.4  Cajas y Lineas   . . . . . . . . . . . . . . . . . . . . . . . .100

         8.6.5  Caracter de Fondo  . . . . . . . . . . . . . . . . . . . . . .102

   8.7   Entrada  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .102

         8.7.1  Entrada con Formato   . . . . . . . . . . . . . . . . . . . .103

   8.8   Opciones   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .104

         8.8.1  Opciones en la entrada . . . . . . . . . . . . . . . . . . . .104

         8.8.2  Atributos de la terminal  . . . . . . . . . . . . . . . . . . .106

         8.8.3  >Como se usa?   . . . . . . . . . . . . . . . . . . . . . . . .107

   8.9   >Como borrar ventanas y lineas?  . . . . . . . . . . . . . . . . . .109

   8.10  Actualizacion de la imagen an la terminal  . . . . . . . . . . . . .110

   8.11  Atributos de video y colores  . . . . . . . . . . . . . . . . . . . . .111

   8.12  Coordenadas del cursor y de las ventanas   . . . . . . . . . . . . .115

   8.13  Moviendonos por alli  . . . . . . . . . . . . . . . . . . . . . . . . .116

   8.14  Pads  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .117

   8.15  Soft-labels  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .118

   8.16  Miscelanea   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .118

   8.17  Acceso de Bajo Nivel . . . . . . . . . . . . . . . . . . . . . . . . .119

   8.18  Volcado de Pantalla   . . . . . . . . . . . . . . . . . . . . . . . . .120

   8.19  Emulacion Termcap   . . . . . . . . . . . . . . . . . . . . . . . . .120

   8.20  Funciones Terminfo . . . . . . . . . . . . . . . . . . . . . . . . . .120

   8.21  Funciones de Depurado   . . . . . . . . . . . . . . . . . . . . . . .121

   8.22  Atributos Terminfo  . . . . . . . . . . . . . . . . . . . . . . . . . .121

         8.22.1 Atributos Logicos . . . . . . . . . . . . . . . . . . . . . . .121

         8.22.2 Numeros . . . . . . . . . . . . . . . . . . . . . . . . . . . .122

         8.22.3 Cadenas  . . . . . . . . . . . . . . . . . . . . . . . . . . . .123

   8.23  Esquema de las Funciones de [N]Curses   . . . . . . . . . . . . . .130


9  Programacion de los Puertos de E/S                               135

   9.1   Programacion del Raton  . . . . . . . . . . . . . . . . . . . . . . .137

   9.2   Programacion del Modem  . . . . . . . . . . . . . . . . . . . . . .138

   9.3   Programacion de la Impresora . . . . . . . . . . . . . . . . . . . .138

   9.4   Programacion del Joystick  . . . . . . . . . . . . . . . . . . . . . .138

INDICE GENERAL                                                            3



10  Conversion de Aplicaciones a Linux                                139
    10.1  Introduccion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .139
    10.2  Gestion de Se"nales  . . . . . . . . . . . . . . . . . . . . . . . . . .140
          10.2.1 Se"nales en SVR4, BSD, y POSIX.1  . . . . . . . . . . . . .140
          10.2.2 Opciones de Se"nales en Linux  . . . . . . . . . . . . . . . .141
          10.2.3 signal en Linux  . . . . . . . . . . . . . . . . . . . . . . . .141
          10.2.4 Se"nales soportadas por Linux  . . . . . . . . . . . . . . . .142
    10.3  E/S de Terminal   . . . . . . . . . . . . . . . . . . . . . . . . . . .142
    10.4  Control e Informacion de Procesos   . . . . . . . . . . . . . . . . .143
          10.4.1 Rutinas kvm   . . . . . . . . . . . . . . . . . . . . . . . . .143
          10.4.2 ptrace y el sistema de ficheros /proc   . . . . . . . . . . . .143
          10.4.3 Control de Procesos en Linux  . . . . . . . . . . . . . . . .144
    10.5  Compilacion Condicional Portable   . . . . . . . . . . . . . . . . .145
    10.6  Comentarios Adicionales  . . . . . . . . . . . . . . . . . . . . . . .146


11  Llamadas al sistema en orden alfabetico                           147


12  Abreviaturas                                                           153


   o  Copyright
      La Guia Linux de Programacion es Oc 1994, 1995 de Sven Goldt
      Sven Goldt, Sachsendamm 47b, 10829 Berlin, Alemania
      < goldt@math.tu - berlin.de > .
      El capitulo 8 es Oc 1994, 1995 de Sven van der Meer < vdmeer@cs.tu -
      berlin.de > .
      El capitulo 6 es Oc 1995 de Scott Burkett < scottb@IntN et.net > .
      El capitulo 10 es Oc 1994, 1995 de Matt Welsh < mdw@cs.cornell.edu > .
      Tenemos  que  dar  especialmente  las  gracias  a  John  D.  Harper  <
      jharper@uiuc.edu > por revisar en profundidad esta guia.
      Se concede permiso para reproducir este documento, en todo o en parte,
      bajo las siguientes condiciones:


        1. Esta nota de Copyright debe incluirse sin modificaciones.

        2. Comparta con los autores cualquier ganancia que obtenga.

        3. Los autores no se hacen responsables de cualquier da"no producido
           en aplicacion de los contenidos de este libro.


   o  Copyright (nota original)
      The Linux Programmer's Guide is Oc 1994, 1995 by Sven Goldt
      Sven Goldt, Sachsendamm 47b, 10829 Berlin, Germany
      < goldt@math.tu - berlin.de > .
      Chapter 8 is Oc  1994, 1995 by Sven van der Meer < vdmeer@cs.tu -
      berlin.de > .
      Chapter 6 is Oc 1995 Scott Burkett < scottb@IntN et.net > .
      Chapter 10 is Oc 1994, 1995 Matt Welsh < mdw@cs.cornell.edu > .
      Special thanks goes to John D. Harper < jharper@uiuc.edu > for proo-
      freading this guide.
      Permission to reproduce this document in whole or in part is subject to
      the following conditions:


        1. The copyright notice remains intact and is included.

        2. If you make money with it the authors want a share.

        3. The authors are not responsible for any harm that might arise by
           the use of it.


   o  Notas sobre la version castellana
      Esta guia, como cuarto trabajo importante del Proyecto LuCAS, obedece
      a la demanda de guias de programacion para Unix/Linux que venimos ob-
      servando desde tiempos recientes. Sin embargo, lamentamos que nuestra
      traduccion sea tan incompleta como la version original en Ingles: cierta-
      mente nos gustaria completarla, sin embargo no hemos podido recibir los
      permisos necesarios para ello de algunos de sus autores originales, al es-
      tar actualmente ilocalizables. El proyecto LuCAS agradece el trabajo de
      traduccion realizado inicialmente por Pedro Pablo Fabrega1, que abarca
      buena parte del libro. Ademas, agradecemos la colaboracion prestada por
__________________________________1
    Pedro Pablo esta disponible en pfabrega@arrakis.es


                                        4

INDICE GENERAL                                                            5



      Ignacio Arenaza, Cesar Ballardini y Luis Francisco Gonzalez2, quienes se
      han ocupado de la traduccion del resto del libro.
      Nota: Version de la traduccion: 0.11 alpha
      Juan Jose Amor3, Mayo de 1998.


    o Prologo
      Esta guia esta lejos de completarse.
      La primera edicion fue la version 0.1, de septiembre de 1994. Se baso en
      las llamadas al sistema debido a la escased de informacion al respecto.
      Esta previsto completarla con la descripcion de las funciones de libreria
      y cambios importantes en el nucleo, asi como incursiones en areas como
      redes, sonido, graficos y entrada/salida asincrona. Asimismo, se incluiran
      en un futuro apuntes sobre como construir librerias dinamicas y acerca
      de interesantes herramientas para el programador.
      Esta guia solo sera un exito gracias a la ayuda en forma de informacion o
      de envio de nuevos capitulos.


    o Introduccion
      En cierta ocasion me dispuse a instalar Linux en mi PC para aprender
      mas acerca de administracion del sistema. Intente instalar un servidor de
      SLIP pero no trabaje con mgetty ni con el shadow.  Tuve que parchear
      el sliplogin y funciono hasta las nuevas versiones de Linux 1.1. Nadie me
      explico que habia pasado. No habia documentacion acerca de los cambios
      desde el nucleo 0.99 salvo los resumenes que hacia Russ Nelson, si bien
      estos no me ayudaban demasiado a resolver mis problemas.

      La Guia Linux de Programacion pretende servir para lo que su nombre
      implica_ para ayudar al programador de Linux a entender las peculiari-
      dades de este sistema operativo. Tambien debera serutil para transportar
      programas de otros sistemas operativos al Linux. Por lo tanto, esta guia
      debe describir las llamadas al sistema y los cambios importantes del nucleo
      que puedan afectar a antiguos programas tales como aplicaciones de E/S
      serie o de red.


    Sven Goldt Guia Linux de Programacion
__________________________________2
    Sus direcciones de correo respectivas son: inaki.arenaza@jet.es, cballard@santafe.com.ar
y luisgh@cogs.susx.ac.uk
   3Como siempre, en jjamor@ls.fi.upm.es

6                                                            INDICE GENERAL

Capitulo  1


El  sistema  operativo  Linux


En marzo de 1991 Linus Benedict Torvalds compro un sistema Multitarea Mi-
nix para su AT. Lo uso para desarrollar su propio sistema multitarea que llamo
Linux.  En el mes septiembre de 1991 libero el primer prototipo por e-mail
a algunos otros usuarios de Minix en Internet: asi comenzo el proyecto Linux.
Muchos programadores desde ese punto han apoyado Linux. Han agregado con-
troladores de dispositivos, desarrollado aplicaciones, segun las normas POSIX.
Hoy Linux es muy potente, pero lo mejor es que es gratuito. Se estan realizando
trabajos para transportar Linux a otras plataformas.


                                         7

8                         CAPITULO 1.  EL SISTEMA OPERATIVO LINUX

Capitulo  2


Elucnleo  de  Linux


La base de Linux es el nucleo.  Podria reemplazar todas las librerias,  pero
mientras quede el nucleo, estara todavia Linux.  El nucleo incorpora contro-
ladores de dispositivos, manejo de la memoria, manejo de procesos y manejo
de comunicaciones.  Los gurus del nucleo siguen las pautas POSIX que hacen
la programacion a veces mas facil y a veces mas dificil.  Si su programa se
comporta de forma diferente en un nuevo nucleo de Linux, puede ser porque se
hayan implantado nuevas lineas marcadas por POSIX. Para mas informacion
de la programacion sobre el nucleo de Linux, lea el documento Linux Kernel
Hacker's Guide.
                                         9

10                                    CAPITULO 2.  EL NUCLEO DE LINUX

Capitulo  3


El  paquete  libc  de  Linux


    libc: ISO 8859.1, < linux=param.h >, funciones YP, funciones crypt,
    algunas rutinas shadow basicas (por omision no incluidas),... rutinas
    viejas por compatibilidad en libcompat (por omision no activas),
    mensajes del error en ingles, frances o aleman, rutinas de gestion de
    la pantalla compatibles bsd 4.4lite en libcurses, rutinas compatibles
    bsd en libbsd, rutinas de la manipulacion de la pantalla en
    libtermcap, rutinas del manejo del base de datos en libdbm, rutinas
    matematicas en libm, entradas para ejecutar programas en crt0.o???,
    informacion del sexo del byte en libieee??? (>podia alguien dar
    informacion en lugar de reirse?), espacio de perfiles de usuario, en
    libgmon. Me gustaria que alguno de los desarrolladores de la libreria
    libc de Linux escribiera este capitulo. Todo lo que puedo decir ahora
    es que va ha haber un cambio del formato de ejecutables a.out a elf
    (formato ejecutable y enlazable) que tambien significa un cambio en la
    construccion de bibliotecas compartidas. Normalmente se soportan ambos
    formatos, a.out y elf

    La mayoria de los elementos del paquete libc de Linux estan bajo la Licencia
Publica GNU, aunque algunos estan bajo una excepcion especial de derechos de
copia como crt0.o.  Para distribuciones comerciales binarias esto significa una
restriccion que prohibe el enlace estatico de ejecutables. El enlace dinamico de
ejecutables son de nuevo una excepcion especial y Richard Stallman del FSF
comento:
[. . . ]  Pero me parece que debemos permitir de forma ambigua la distribucion

de ejecutables enlazados dinamicamente *sin* ir acompa"nados de la librerias
bibliotecas, con tal de que los ficheros objeto que forman el ejecutable esten sin
restriccion segun la seccion 5 [. . . ] Por tanto tomare la decision de permitirlo.
La actualizacion del LGPL tendra que esperar hasta que tenga tiempo para hacer
y comprobar una version nueva.
    Sven Goldt Guia Linux de Programacion



                                         11

12                            CAPITULO 3.  EL PAQUETE LIBC DE LINUX

Capitulo  4


Llamadas  al  sistema


Una llamada al sistema es normalmente una demanda al sistema operativo
(nucleo) para que haga una operacion de hardware/sistema especifica o privi-
legiada.  Por ejemplo, en Linux-1.2, se han definido 140 llamadas al sistema.
Las llamadas al sistema como close() se implementan en la libc de Linux. Esta
aplicacion a menudo implica la llamada a una macro que puede llamar a sysca-
ll(). Los parametros pasados a syscall() son el numero de la llamada al sistema
seguida por el argumento necesario.  Los numeros de llamadas al sistema se
pueden encontrar en < linux=unistd.h > mientras que < sys=syscall.h > ac-
tualiza con una nueva libc.  Si aparecen nuevas llamadas que no tienen una
referencia en libc aun, puede usar syscall().  Como ejemplo, puede cerrar un
fichero usando syscall() de la siguiente forma (no aconsejable):


#include  <syscall.h>


extern  int  syscall(int,  ...);


int  my_close(int  filedescriptor)
{
    return  syscall(SYS_close,  filedescriptor);
}


    En la arquitectura i386, las llamadas al sistema estan limitadas a 5 argu-
mentos ademas del numero de llamada al sistema debido al numero de registros
del procesador. Si usa Linux en otra arquitectura puede comprobar el contenido
de < asm=unistd.h > para las macros _syscall, para ver cuantos argumentos
admite su hardware o cuantos escogieron los desarrolladores.  Estas macros
_syscall se pueden usar en lugar de syscall(), pero esto no se recomienda ya que
esa macro se expande a una funcion que ya puede existir en una biblioteca. Por
consiguiente, solo los desarrolladores del nucleo deberian jugar a con las macros
_syscall.  Como demostracion, aqui tenemos el ejemplo de close() usando una
macro _syscall.


#include  <linux/unistd.h>


_syscall1(int,  close,  int,  filedescriptor);


                                         13

14                                 CAPITULO 4.  LLAMADAS AL SISTEMA



La macro _syscall1 expande la funcion close().  Asi tenemos close() dos veces,
una vez en libc y otra vez en nuestro programa. El valor devuelto por syscall()
o un una macro _syscall es -1 si la llamada al sistema fallo y 0 en caso de exito.
Dele un vistazo a la variable global errno para comprobar que ha ocurrido si la
llamada al sistama fallo.
   Las siguiente llamadas al sistema estan disponibles en BSD y SYS V pero
no estan disponibles en Linux:
audit(), auditon(), auditsvc(), fchroot(), getauid(), getdents(), getmsg(), min-
core(), poll(), putmsg(), setaudit(), setauid().
   Sven Goldt Guia Linux de Programacion

Capitulo  5


Una  llamada  multiuso:   "ioctl"


ioctl representa el control de entrada/salida y se usa para manipular un dispo-
sitivo de caracter mediante un descriptor de fichero. El formato de ioctl es:
ioctl(unsigned int fd, unsigned int request, unsigned long argument).
El valor devuelto es -1 si ocurrio un error y un valor mayor o igual que 0 si la
peticion tuvo exito, como cualquier otra llamadas del sistema. El nucleo distin-
gue entre ficheros especiales y regulares.  Los ficheros especiales se encuentran
principalmente en /dev y /proc. Difieren de los ficheros regulares en que escon-
den una interface a un controlador y no un fichero real (regular) que contiene
texto o datos binarios.  Esta es la filosofia UNIX y permite usar operaciones
normales de lectura/escritura en cada fichero. Pero si necesita hacer algo mas
con un fichero especial o un fichero regular que puede hacer el con...  si, ioc-
tl.  Usted necesitara con mas frecuencia ioctl para ficheros especiales que para
ficheros regulares, pero es posible usar ioctl en ficheros regulares tambien.
                                         15

16                   CAPITULO 5.  UNA LLAMADA MULTIUSO: "IOCTL"

Capitulo  6


Comunicacion  entre  procesos



en  Linux


B. Scott Burkett, scottb@intnet.net v1.0, 29 de Marzo de 1995
6.1      Introduccion


Los  medios  IPC  (Inter-process  communication)  de  Linux  proporcionan  un
metodo para que multiples procesos se comuniquen unos con otros. Hay varios
metodos de IPC disponibles para los programadores Linux en C:


    o Pipes UNIX Half-duplex


    o FIFOs (pipes con nombre)


    o Colas de mensajes estilo SYSV


    o Semaforos estilo SYSV


    o Segmentos de memoria compartida estilo SYSV


    o Sockets (estilo Berkeley) (no contemplado por ahora)


    o Pipes Full-duplex (pipes STREAMS) (no contemplado por ahora)


    Estos medios, cuando se usan de forma efectiva, proporciona una base solida
para el desarrollo de cliente/servidor en cualquier sistema UNIX (incluido Li-
nux).
6.2      Pipes UNIX Semi-duplex


6.2.1     Conceptos basicos


Simplemente, una tuberia (pipe) es un metodo de conexion de que une la salida
estandar de un proceso a la entrada estandar de otro. Las tuberias son la mayor
de las herramientas de IPC, han estado presentes desde los primeros origenes


                                         17

18       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



del sistema operativo UNIX. Proporcionan un metodo de comunicaciones en un
sentido (unidirecional, semi-duplex) entre procesos.
   Este mecanismo es ampliamente usado, incluso en la linea de comandos
UNIX (en la shell):


               ls | sort | lp


   Lo anterior es un ejemplo de 'pipeline', donde se toma la salida de un co-
mando ls como entrada de un comando sort, quien a su vez entrega su salida
a la entrada de lp.  Los datos corren por la tuberia semi-duplex, de viajando
(virtualmente) de izquierda a derecha por la tuberia.
   Aunque la mayor parte de nosotros usamos las tuberias casi religiosamente
en las programaciones de scripts de shell, casi nunca nos paramos a pensar en
lo que tiene lugar a nivel del nucleo.
   Cuando un proceso crea una tuberia, el nucleo instala dos descriptores de
ficheros para que los use la tuberia. Un descriptor se usa para permitir un ca-
mino de entrada a la tuberia (write), mientras que la otra se usa para obtener
los datos de la tuberia (read).  A estas alturas, la tuberia tiene un peque"no
uso practico, ya que la creacion del proceso solo usa la tuberia para comuni-
carse consigo mismo. Considere esta representacion de un proceso y del nucleo
despues de que se haya creado una tuberia:

   Del diagrama anterior, es facil ver como se conectan los descriptores. Si el
proceso envia datos por la tuberia (fd0), tiene la habilidad obtener (leer) esa
informacion de fd1. Sin embargo, hay un objetivo mas amplio sobre el esquema
anterior.  Mientras una tuberia conecta inicialmente un proceso a si mismo,
los datos que viajan por la tuberia se mueven por el nucleo.  Bajo Linux en
particular, las tuberias se representan realmente de forma interna con un inodo
valido.  Por supuesto, este inodo reside dentro del nucleo mismo, y no dentro
de los limites de cualquier sistema de archivos fisico. Este punto particular nos
abrira algunas puertas de E/S bastante practicas, como veremos un poco mas
adelante.
   A estas alturas, la tuberia es bastante inutil. Despues de todo >por que el
problema de crear una ca"neria si estamos solo hablando con nosotros mismos?
Ahora, el proceso de creacion bifurca un proceso hijo.  Como un proceso hijo
hereda cualquier descriptor de fichero abierto del padre, ahora tenemos la base
por comunicacion multiprocesos (entre padre e hijo).  Considere este version
actualizada de de nuestro esquema simple:

6.2.  PIPES UNIX SEMI-DUPLEX                                           19


    Arriba, vemos que ambos procesos ahora tienen acceso al descriptor del
fichero que constituye la tuberia.  Esta en esa fase, que se debe tomar una
decision critica. >En que direccion queremos que viajen los datos? >El proceso
hijo envia informacion al padre, o viceversa?  Los dos procesos mutuamente
estan de acuerdo en esta emision, y procede a"cerrar" el extremo de la ca"neria
que no le interesa.  Por motivos discusion, digamos que el hijo ejecuta unos
procesos, y devuelve informacion por la tuberia al padre. Nuestro esquema ya
revisado apareceria como:

    <Ahora la construccion de la tuberia esta completa! Lounico que queda por
hacer es usar la tuberia. Para a acceder a una tuberia directamente, podemos
usar la misma llamada al sistema que se usa para un fichero I/O de bajo nivel.
(las tuberias estan representadas internamente como un inodo valido).
    Para enviarle datos a la tuberia, usamos la llamada al sistema write(), y
para recuperar datos de la tuberia, usamos la llamada al sistema read().  <Re-
cuerde las llamadas del sistema a los ficheros I/O de bajo-nivel se hacen usando
descriptores de fichero!  Sin embargo, tenga presente que ciertas llamadas al
sistema, como por ejemplo lseek(), no trabaja con descriptores a tuberias.



6.2.2     Creacion de tuberias en C


Crear "tuberias" con el lenguaje de programacion C puede ser un poco mas
complejo que en un ejemplo de shell.  Para crear una tuberia simple con C,
hacemos uso de la llamada al sistema pipe().  Toma un argumento solo, que
es una tabla de dos enteros, y si tiene exito, la tabla contendra dos nuevos
descriptores de ficheros para ser usados por la tuberia.  Despues de crear una
tuberia, el proceso tipicamente desdobla a un proceso nuevo (recuerde que el
hijo hereda los descriptores del fichero).
___________________________________________________________________________________

   LLAMADA  AL  SISTEMA:  pipe();


   PROTOTIPO:  int  pipe(  int  fd[2]  );

20       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



     RETORNA:  0   si exito
                -1  si  error:  errno  =  EMFILE  (no  quedan  descriptores  libres)
                                           EMFILE  (tabla  de  ficheros  del  sistema  llena)
                                           EFAULT  (el  vector  fd  no  es  valido)


  NOTAS:  fd[0]  es  para  leer,  fd[1]  es  para  escribir
___________________________________________________________________________________
    El primer del vector fd (elemento 0) esta fijado y abierto para lectura, mien-
tras el segundo entero (elemento 1) esta fijado y abierto para escritura. Visual-
mente hablando, la salida de fd1 se vuelve la entrada para fd0.  Una vez mas,
todo datos que se mueven por la tuberia los hacen por el nucleo.


          #include  <stdio.h>
          #include  <unistd.h>
          #include  <sys/types.h>


          main()
          {
                     int       fd[2];


                     pipe(fd);
                     .
                     .
          }


    Recuerde que un nombre de vector en C es un puntero a su primer miembro.
Es decir,  fd es equivalente a &fd0.   Una vez hemos establecido la tuberia,
entonces desdoblamos (fork) nuestro nuevo proceso hijo:


          #include  <stdio.h>
          #include  <unistd.h>
          #include  <sys/types.h>


          main()
          {
                     int       fd[2];
                     pid_t    childpid;


                     pipe(fd);


                     if((childpid  =  fork())  ==  -1)
                     {
                               perror("fork");
                               exit(1);
                     }
                     .
                     .
          }

6.2.  PIPES UNIX SEMI-DUPLEX                                           21



    Si el padre quiere recibir datos del hijo, debe cerrar fd1, y el hijo debe cerrar
fd0.  Si el padre quiere enviarle datos al hijo, debe cerrar fd0, y el hijo debe
cerrar fd1.  Como los descriptores se comparten entre el padre y hijo, siempre
debemos estar seguros cerrar el extremo de ca"neria que no nos interesa. Como
nota tecnica, nunca se devolvera EOF si los extremos innecesarios de la tuberia
no son explicitamente cerrados.


           #include  <stdio.h>
           #include  <unistd.h>
           #include  <sys/types.h>


           main()
           {
                     int       fd[2];
                     pid_t    childpid;


                     pipe(fd);


                     if((childpid  =  fork())  ==  -1)
                     {
                               perror("fork");
                               exit(1);
                     }


                     if(childpid  ==  0)
                     {
                               /*  El  hijo  cierra  el  descriptor  de  entrada  */
                               close(fd[0]);
                     }
                     else
                     {
                               /*  El  padre  cierra  el  descriptor  de  salida  */
                               close(fd[1]);
                     }
                     .
                     .
           }


    Como se menciono previamente, una vez se ha establecido la tuberia, los
descriptores de fichero se tratan como descriptores a ficheros normales.
/*****************************************************************************
  Parte  de  la  "Guia  Linux  de  Programacion  -  Capitulo  6"
  (C)opyright  1994-1995,  Scott  Burkett
  *****************************************************************************
  MODULO:  pipe.c

22       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



 *****************************************************************************/


#include  <stdio.h>
#include  <unistd.h>
#include  <sys/types.h>


int  main(void)
{
          int       fd[2],  nbytes;
          pid_t    childpid;
          char     string[]  =  "Hola  a  todos!\n";
          char     readbuffer[80];


          pipe(fd);


          if((childpid  =  fork())  ==  -1)
          {
                    perror("fork");
                    exit(1);
          }


          if(childpid  ==  0)
          {
                    /*  Cierre  del  descriptor  de  entrada  en  el  hijo  */
                    close(fd[0]);


                    /*  Enviar  el  saludo  via  descriptor  de  salida  */
                    write(fd[1],  string,  strlen(string));
                    exit(0);
          }
          else
          {
                    /*  Cierre  del  descriptor  de  salida  en  el  padre   */
                    close(fd[1]);


                    /*  Leer  algo  de  la  tuberia...  el  saludo!  */
                    nbytes  =  read(fd[0],  readbuffer,  sizeof(readbuffer));
                    printf("Received  string:  %s",  readbuffer);
          }


          return(0);
}



   A menudo, los descriptores del hijo son duplicados en la entrada o salida
estandares. El hijo puede entonces hacer exec() con otro programa, que hereda
los stream estandar. Observe la llamada al sistema dup():
___________________________________________________________________________________

6.2.  PIPES UNIX SEMI-DUPLEX                                           23



   LLAMADA  AL  SISTEMA:  dup();


   PROTOTIPO:  int  dup(  int  oldfd  );
     RETORNA:  nuevo  descriptor  si  hay exito
                 -1  si  error:  errno  =  EBADF  (oldfd  no  es  un  descriptor  valido)
                                           EBADF  (newfd  se  sale  del  rango)
                                           EMFILE  (Hay  demasiados  descriptores  en  el  proceso  abiertos)


   NOTAS:  iel  antiguo  descriptor  no  se  cierra!  Asi  podemos  intercambiarlos

___________________________________________________________________________________
    Aunque el descriptor viejo y el recien creado se puede intercambiar, normal-
mente cerraremos primero uno de los stream estandar.  La llamada al sistema
dup() usa el numero descriptor mas bajo no utilizado para el nuevo descriptor.
    Considere lo siguiente:


          .
          .
          childpid  =  fork();


          if(childpid  ==  0)
          {
                     /*  Cerrar  la  entrada  estandar  en  el  hijo  */
                     close(0);


                     /*  Duplicar  sobre  esta  la  salida  de  la  tuberia  */
                     dup(fd[0]);
                     execlp("sort",  "sort",  NULL);


          }


    Como el descriptor de fichero 0 (stdin) se cerro, la llamada a dup() duplico el
descriptor de la entrada de la tuberia (fd0) hacia su entrada estandar. Entonces
hacemos una llamada a execlp() recubrir el segmento de texto (codigo) del hijo
con el del programa.  <Desde no hace mucho los programas exec heredan los
stream estandares de sus origenes, realmente hereda el lado de la entrada de la
tuberia como su entrada estandar! Ahora, cualquier cosa que el padre original
procesa lo envia a la tuberia, va en la facilidad de la clase.
    Hay otra llamada al sistema, dup2 (), que se puede usar tambien.  Esta
llamada particular tiene su origen con la Version 7 de UNIX, se realizo por una
version de BSD y ahora es requerida por el estandar POSIX.
___________________________________________________________________________________

   LLAMADA  AL  SISTEMA:  dup2();


   PROTOTIPO:  int  dup2(  int  oldfd,  int  newfd  );
     RETORNA:  nuevo  descriptor  si  hay exito
                 -1  si  error:  errno  =  EBADF  (oldfd  no  es  descriptor  valido)
                                           EBADF  (newfd  esta  fuera  de  rango)

24       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



                                           EMFILE  (demasiados  descriptores  abiertos)


  NOTAS:  iel  descriptor  antiguo  es  cerrado  con  dup2()!

___________________________________________________________________________________
    Con esta particular llamada, tenemos la operacion de cerrado, y la duplica-
cion del descriptor actual, relacionado con una llamada al sistema. Ademas, se
garantiza el ser atomica, que esencialmente significa que nunca se interrumpira
por la llegada de una se"nal. Toda la operacion transcurrira antes de devolverle
el control al nucleo para despachar la se"nal.  Con la llamada al sistema dup()
original, los programadores tenian que ejecutar un close() antes de llamarla.
Esto resultaba de dos llamadas del sistema, con un grado peque"no de vulnera-
bilidad en el breve tiempo que transcurre entre ellas. Si llega una se"nal durante
ese tiempo, la duplicacion del descriptor fallaria. Por supuesto, dup2 () resuelve
este problema para nosotros.
    Considere:


          .
          .
          childpid  =  fork();


          if(childpid  ==  0)
          {
                     /*  Cerrar  entrada  estandar,  duplicando  a  esta  la
                        salida  de  datos  de  la  tuberia  */
                     dup2(0,  fd[0]);
                     execlp("sort",  "sort",  NULL);
                     .
                     .
          }



6.2.3    Tuberias, la forma facil de hacerlo


Si de todo lo visto anteriormente parece enredada la forma de crear y utilizar
tuberias, hay una alternativa:
___________________________________________________________________________________


   FUNCION  DE  LIBRERIA:  popen();


   PROTOTIPO:  FILE  *popen  (  char  *comando,  char  *tipo);
     RETORNA:  si  hay exito,  nuevo  "stream"  de  fichero
                 si  no  hay exito,  NULL  (por  fallo  en  llamada  pipe()  o  fork()  ).


   NOTAS:  crea  una  tuberia,  y  realiza  las  llamadas  fork/exec
            segun  el  "comando"  pasado  como  argumento.

___________________________________________________________________________________
    Esta funcion estandar de la biblioteca crea una tuberia semi-duplex llaman-
do a pipe() internamente.  Entonces adesdobla un proceso hijo, abre una shell
Bourne y ejecuta el argumento "command" en la shell.  La direccion del flujo

6.2.  PIPES UNIX SEMI-DUPLEX                                           25



de datos se determina por el segundo argumento, "type". Puede ser "r" o "w",
para "read" o "write". <No pueden ser ambos!. Bajo Linux la tuberia se abrira
segun el modo especificado por el primer caracter del argumento "type".  Asi,
si trata de pasar "rw", solo lo abre en modo "read".
    Mientras esta funcion de la biblioteca ejecuta realmente parte del trabajo
sucio por usted, hay un inconveniente substancial. Pierde parte del control que
tenia con el uso de la llamada al sistema pipe(), y la manipulacion de fork/exec
por usted mismo.  Sin embargo, como la shell de Bourne se usa directamente,
la expansion de metacaracteres de la shell (incluso plantillas) esta permitida
dentro del argumento "comando".
    Las tuberias que se crean con popen() se debe cerrar con pclose().  Por
ahora, probablemente se habra dado cuenta de que popen/pclose comparten
un parecido llamativo con las funciones I/O stream de fichero normal fopen()
y fclose().
___________________________________________________________________________________

   FUNCION  DE  LIBRERIA:  pclose();


   PROTOTIPO:  int  pclose(  FILE  *stream  );
     RETORNA:  el  codigo  de  retorno  de  la  llamada  wait4()
                 -1  si  el  "stream"  pasado  no  es  valido,  o  la  llamada  wait4()  falla


   NOTAS:  espera  a  que  el  proceso  que  escribe  en  la  tuberia  termine,
   y  luego  cierra  el  "stream".


___________________________________________________________________________________

    La funcion pclose() ejecuta un wait4() en el proceso desdoblado por popen().
Cuando vuelve, destruye la tuberia y el stream de fichero de salida.  Una vez
mas, es sinonimo de la funcion fclose() para ficheros E/S normales de stream.
    Considere este ejemplo, que abre una tuberia al comando sort, y ordena un
array de cadena de caracteres.:


/*****************************************************************************
  Parte  de  la  "Guia  Linux  de  Programacion  -  Capitulo  6"
  (C)opyright  1994-1995,  Scott  Burkett
  *****************************************************************************
  MODULO:  popen1.c
  *****************************************************************************/


#include  <stdio.h>


#define  MAXSTRS  5


int  main(void)
{
          int   cntr;
          FILE  *pipe_fp;
          char  *strings[MAXSTRS]  =  {  "eco",  "bravo",  "alpha",

26       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



                                           "charlie",  "delta"};


          /*  Crea  una  tuberia  de  un  sentido  llamando  a  popen()  */
          if  ((  pipe_fp  =  popen("sort",  "w"))  ==  NULL)
          {
                    perror("popen");
                    exit(1);
          }


          /*  Bucle  de  proceso  */
          for(cntr=0;  cntr<MAXSTRS;  cntr++)  {
                    fputs(strings[cntr],  pipe_fp);
                    fputc('\n',  pipe_fp);
          }


          /*  Cierra  la  tuberia  */
          pclose(pipe_fp);


          return(0);
}


   Como popen() usa la shell para hacer su enlace, <todas las expansiones de
caracteres y metacaracteres de la shell estan disponibles para su uso! Ademas,
tecnicas mas avanzadas tales como redireccion, e incluso la salida por tuberia
se puede utilizar con popen(). Considere el siguiente ejemplo:


          popen("ls  ~scottb",  "r");
          popen("sort  >  /tmp/foo",  "w");
          popen("sort  |  uniq  |  more",  "w");


   Considere este peque"no programa como otro ejemplo de popen(), que abre
dos tuberias (una a la orden ls, el otro a sort):
/*****************************************************************************
 Parte  de  la  "Guia  Linux  de  Programacion  -  Capitulo  6"
 (C)opyright  1994-1995,  Scott  Burkett
 *****************************************************************************
 MODULO:  popen2.c
 *****************************************************************************/


#include  <stdio.h>


int  main(void)
{
          FILE  *pipein_fp,  *pipeout_fp;
          char  readbuf[80];

6.2.  PIPES UNIX SEMI-DUPLEX                                           27

           /*  Crea  una  tuberia  de  un  sentido  llamando  a  popen()  */
           if  ((  pipein_fp  =  popen("ls",  "r"))  ==  NULL)
           {
                     perror("popen");
                     exit(1);
           }


           /*  Crea  una  tuberia  de  un  sentido  llamando  a  popen()  */
           if  ((  pipeout_fp  =  popen("sort",  "w"))  ==  NULL)
           {
                     perror("popen");
                     exit(1);
           }


           /*  Bucle  de  proceso  */
           while(fgets(readbuf,  80,  pipein_fp))
                     fputs(readbuf,  pipeout_fp);


           /*  Cierre  de  las  tuberias  */
           pclose(pipein_fp);
           pclose(pipeout_fp);


           return(0);
}


    Para nuestra demostracion final de popen(), creamos un programa generico
que abre una tuberia entre una orden pasada y un nombre de fichero:
/*****************************************************************************
  Parte  de  la  "Guia  Linux  de  Programacion  -  Capitulo  6"
  (C)opyright  1994-1995,  Scott  Burkett
  *****************************************************************************
  MODULO:  popen3.c
  *****************************************************************************/


#include  <stdio.h>


int  main(int  argc,  char  *argv[])
{
           FILE  *pipe_fp,  *infile;
           char  readbuf[80];


           if(  argc  !=  3)  {
                     fprintf(stderr,  "USO:   popen3  [comando]  [archivo]\n");
                     exit(1);

28       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



          }


          /*  Abrir  el  fichero  de  entrada  */
          if  ((  infile  =  fopen(argv[2],  "rt"))  ==  NULL)
          {
                    perror("fopen");
                    exit(1);
          }


          /*  Crear  una  tuberia  de  un  sentido  llamando  a  popen()  */
          if  ((  pipe_fp  =  popen(argv[1],  "w"))  ==  NULL)
          {
                    perror("popen");
                    exit(1);
          }


          /*  Bucle  de  proceso  */
          do  {
                    fgets(readbuf,  80,  infile);
                    if(feof(infile))  break;


                    fputs(readbuf,  pipe_fp);
          }  while(!feof(infile));


          fclose(infile);
          pclose(pipe_fp);


          return(0);
}


   Pruebe este programa, con las llamadas siguientes:


          popen3  sort  popen3.c
          popen3  cat  popen3.c
          popen3  more  popen3.c
          popen3  cat  popen3.c  |  grep  main



6.2.4    Operaciones atomicas con tuberias


Para que una operacion se considere "atomica",  no se debe interrumpir de
ninguna manera. Todo su funcionamiento ocurre de una vez. La norma POSIX
indica en /usr/include/posix1_lim.h que el tama"no maximo del buffer para
una operacion atomica en una tuberia es:


          #define  _POSIX_PIPE_BUF            512


   Hasta 512 bytes se pueden escribir o recuperar de una tuberia atomicamente.
Cualquier cosa que sobrepase este limite se partira.  Bajo Linux sin embargo,
se define el limite atomico operacional en "linux/limits.h" como:

6.3.  TUBERIAS CON NOMBRE (FIFO - FIRST IN FIRST OUT)        29



           #define  PIPE_BUF          4096



    Como puede ver, Linux adapta el numero minimo de bytes requerido por
POSIX, y se le pueden agregar bastantes.  La atomicidad del funcionamiento
de tuberia se vuelve importante cuando implica mas de un proceso (FIFOS).
Por ejemplo, si el numero de bytes escritos en una tuberia excede el limite
atomico para una simple operacion, y procesos multiples estan escribiendo en
la tuberia, los datos seran "intercalados" o "chunked".  En otras palabras, un
proceso insertaria datos en la tuberia entre la escritura de otro.



6.2.5     Notas acerca de las tuberias semi-duplex:


    o Se pueden crear tuberias de dos direcciones abriendo dos tuberias, y rea-
      signado los descriptores de fichero al proceso hijo.


    o La llamada a pipe() debe hacerse ANTES de la llamada a fork(), o los
      hijos no heredaran los descriptores (igual que en popen()).


    o Con tuberias semi -duplex, cualquier proceso conectado debe compartir el
      ancestro indicado. Como la tuberia reside en el nucleo, cualquier proceso
      que no sea ancestro del creador de la tuberia no tiene forma de direccio-
      narlo. Este no es el caso de las tuberias con nombre (FIFOS).
6.3      Tuberias con Nombre (FIFO - First In First Out)


6.3.1     Conceptos basicos


Una tuberia con nombre funciona como una tuberia normal, pero tiene algunas
diferencias notables.



    o Las tuberias con nombre existen en el sistema de archivos como un archivo
      de dispositivo especial.


    o Los procesos de diferentes padres pueden compartir datos mediante una
      tuberia con nombre.


    o Cuando se han realizados todas las I/O por procesos compartidos,  la
      tuberia con nombre permanece en el sistema de archivos para un uso
      posterior.



6.3.2     Creacion de una FIFO


Hay varias formas de crear una tuberia con nombre. Las dos primeras se pueden
hacer directamente de la shell.



           mknod  MIFIFO  p
           mkfifo  a=rw  MIFIFO

30       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



   Los dos comandos anteriores realizan operaciones identicas, con una excep-
cion.  El comando mkfifo proporciona una posibilidad de alterar los permisos
del fichero FIFO directamente tras la creacion. Con mknod sera necesaria una
llamada al comando chmod.
   Los ficheros FIFO se pueden identificar rapidamente en un archivo fisico por
el indicador "p" que aparece en la lista del directorio.


          $  ls  -l  MIFIFO
          prw-r--r--    1  root       root               0  Dec  14  22:15  MIFIFO|


   Tambien hay que observar que la barra vertical ("simbolo pipe") esta situada
inmediatamente detras del nombre de fichero. Otra gran razon para usar Linux
>eh?
   Para crear un FIFO en C, podemos hacer uso de la llamada del sistema
mknod():
___________________________________________________________________________________

   FUNCION  DE  LIBRERIA:  mknod();


   PROTOTIPO:  int  mknod(  char  *nombre,  mode_t  modo,  dev_t  disp);
     RETURNS:  0  si exito,
                 -1  si  error:  errno  =  EFAULT  (nombre  no  valido)
                                           EACCES  (permiso  denegado)
                                           ENAMETOOLONG  (nombre  demasiado  largo)
                                           ENOENT  (nombre  no  valido)
                                           ENOTDIR  (nombre  no  valido)
                                           (vea  la  pagina  mknod(3)  para  mas  informacion)


   NOTES:  Crea  un  nodo  del  sistema  de  ficheros  (fichero,  dispositivo,  o  FIFO)
___________________________________________________________________________________
    Dejare una discusion mas detallada de mknod() a la pagina del manual,
pero lo podemos considerear un simple ejemplo de la creacion de un FIFO en
C:


                     mknod("/tmp/MIFIFO",  S_IFIFO|0666,  0);


    En este caso el fichero "/tmp/MIFIFO" se crea como fichero FIFO. Los
permisos requeridos son "0666", aunque se ven afectados por la configuracion
de umask de la siguiente forma:


            umask_definitiva  =  permisos_solicitados  &  ~umask_inicial


    Un truco comun es usar la llamada del sisterma umask() para borrar tem-
poralmente el valor de umask:


                     umask(0);
                     mknod("/tmp/MIFIFO",  S_IFIFO|0666,  0);


    Ademas, el tercer argumento de mknod() se ignora salvo que estemos cre-
ando un archivo de dispositivo. En ese caso, se deberia especificar los numeros
mayor y menor del fichero de dispositivo.

6.3.  TUBERIAS CON NOMBRE (FIFO - FIRST IN FIRST OUT)        31



6.3.3     Operaciones con FIFOs


Las operaciones E/S sobre un FIFOson esencialmente las mismas que para las
tuberias normales, con una gran excepcion.  Se deberia usar una llamada del
sistema o_pen" o una funcion de libreria para abrir fisicamente un canal para la
tuberia.  Con las tuberias semi-duplex, esto es innecesario, ya que la tuberia
reside en el nucleo y no en un sistemade archivos fisico.  En nuestro ejemplo
trataremos la tuberia como un stream, abiendolo con fopen(), y cerrandolo con
fclose().
    Consideramos un proceso servidor simple:


/*****************************************************************************
  Parte  de  la  "Guia  Linux  de  Programacion  -  Capitulo  6"
  (C)opyright  1994-1995,  Scott  Burkett
  *****************************************************************************
  MODULO:  fifoserver.c
  *****************************************************************************/


#include  <stdio.h>
#include  <stdlib.h>
#include  <sys/stat.h>
#include  <unistd.h>


#include  <linux/stat.h>


#define  FIFO_FILE         "MIFIFO"


int  main(void)
{
           FILE  *fp;
           char  readbuf[80];


           /*  Crea  el  FIFO  si  no  existe  */
           umask(0);
           mknod(FIFO_FILE,  S_IFIFO|0666,  0);


           while(1)
           {
                     fp  =  fopen(FIFO_FILE,  "r");
                     fgets(readbuf,  80,  fp);
                     printf("Cadena  recibida:  %s\n",  readbuf);
                     fclose(fp);
           }


           return(0);
}


    Como un FIFO bloquea por defecto, ejecute el servidor en segundo plano

32       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



tras compilarlo:



          $  fifoserver&



   Discutiremos la accion de bloqueo de un FIFO en un momento.  Primero
considrearemos el siguiente cliente simple enfrentado a nuestro servidor:

/*****************************************************************************
 Parte  de  la  "Guia  Linux  de  Programacion  -  Capitulo  6"
 (C)opyright  1994-1995,  Scott  Burkett
 *****************************************************************************
 MODULO:  fifoclient.c
 *****************************************************************************/


#include  <stdio.h>
#include  <stdlib.h>


#define  FIFO_FILE         "MIFIFO"


int  main(int  argc,  char  *argv[])
{
          FILE  *fp;


          if  (  argc  !=  2  )  {
                    printf("USO:  fifoclient  [cadena]\n");
                    exit(1);
          }


          if((fp  =  fopen(FIFO_FILE,  "w"))  ==  NULL)  {
                    perror("fopen");
                    exit(1);
          }


          fputs(argv[1],  fp);


          fclose(fp);
          return(0);
}



6.3.4    Acciones Bloqueantes en una FIFO


Normalmente, el bloqueo ocurre en un FIFO. En otro palabras, si se abre el
FIFO para lectura, el proceso esara "bloqueado" hasta que cualquier otro pro-
ceso lo abra para escritura.  Esta accion funciona al reves tambien.  Si este
comportamiento no nos interesa, se puede usar la bandera O_NONBLOCK en
la llamada a open() para desactivar la accion de bloqueo por defecto.

 6.4.  IPC EN SISTEMA V                                                    33



     En el caso de nuestro servidor simple, lo hemos puesto en segundo plano,
 y permito hacer su bloqueo alli.  La alternativa estarian saltar a otra consola
 virtual y ejecutar el cliente, cambiando de un lado a otro para ver la accion
 resultante.



 6.3.5     La Infame Se"nal SIGPIPE


 En unaultima nota, las tuberias deberian tener a un lector y un escritor. Si un
 proceso trata de escribir en una tuberia que no tiene lector, el nucleo enviara la
 se"nal SIGPIPE. Esto es imperativo cuando en la tuberia se ven envueltos mas
 dos procesos.
 6.4      IPC en Sistema V


 6.4.1     Conceptos fundamentales


 Con Unix Sistema V, AT&T introdujo tres nuevas formas de las facilidades IPC
 (colas de mensajes, semaforos y memoria compartida). Mientras que el comite
 POSIX aun no ha completado su estadarizacion de estas facilidades, la mayoria
 de las implementaciones soportan estas.  Ademas, Berkeley (BSD) usa sockets
 como su forma primaria de IPC, mas que los elementos del Sistema V. Linux
 tiene la habilidad de usar ambas formas de IPC (BSD y System V), aunque no
 se discutiran los socket hasta elultimo capitulo.
     La implementacion para Linux del IPC System V fue escrita por Krishna
 Balasubramanian, en balasub@cis.ohio-state.edu.



 Identificadores IPC


 Cada objeto IPC tiene ununico identificador IPC asociado con el.  Cuando
 decimos "objeto IPC", hablamos de una simple cola de mensaje, semaforo o
 segmento de memoria compartida. Se usa este identificador, dentro del nucleo,
 para identificar de formaunica un objeto IPC. Por ejemplo, para acceder un
 segmento particular memoria compartida, lounico que requiere es el valor ID
unico que se le ha asignado a ese segmento.
     La unicidad de un identificador es importante segun el tipo de objeto en
 cuestion.  Para ilustrar esto, supondremos un identificador numerico "12345".
 Mientras no puede haber nunca dos colas de mensajes, con este mismo identifi-
 cador existe la posibilidad que existan una cola de mensajes y un segmento de
 memoria compartida que poseen el mismo identificador numerico.



 Claves IPC


 Para obtener un identificadorunico, debe utilizarse una clave.  Esta debe ser
 conocida por ambos procesos cliente y servidor.  Este es el primer paso para
 construir el entorno cliente/servidor de una aplicacion.
     Cuando  usted  llama  por  telefono  a  alguien,  debe  conocer  su  numero.
 Ademas, la compa"nia telefonica debe conocer como dirigir su llamada al destino.
 Una vez que el receptor responde a su llamada, la conexion tiene lugar.

34       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



   En el caso de los mecanismos IPC de Sistema V, el "telefono" coincide con
el tipo de objeto usado. La "compa"nia telefonica" o el sistema de encaminado,
se puede equiparar con la clave IPC.
   La clave puede ser el mismo valor cada vez, incluyendo su codigo en la propia
aplicacion.  Esta es una desventaja pues la clave requerida puede estar ya en
usa. Por eso, la funcion ftok() nos serautil para generar claves no utilizadas
para el cliente y el servidor.
___________________________________________________________________________________

   FUNCION  DE  LIBRERIA:  ftok();


   PROTOTIPO:  key_t  ftok  (  char  *nombre,  char  proj  );
     RETORNA:  nueva  clave  IPC  si  hay exito
                 -1  si  no  hubo exito,  dejando  errno  con  el  valor  de  la  llamada  stat()
___________________________________________________________________________________
    La clave del valor devuelto de ftok () se genera por la combinacion del
numero del inodo y del numero menor de dispositivo del archivo argumento, con
el caracter identificador del proyecto del segundo argumento. Este no garantiza
la unicidad, pero una aplicacion puede comprobar las colisiones y reintentar la
generacion de la clave.
___________________________________________________________________________________

          key_t    miclave;
          miclave  =  ftok("/tmp/miaplic",  'a');
___________________________________________________________________________________
    En el caso anterior el directorio /tmp/miaplic se combina con la letra
'a'.Otro ejemplo comun es usar el directorio actual:
___________________________________________________________________________________

          key_t    miclave;
          mykey  =  ftok(".",  'a');
___________________________________________________________________________________
    El algoritmo de la generacion de la clave usado esta completamente a la
discrecion del programador de la aplicacion. Mientras que tome medidas para
prevenir ls condiciones criticas, bloqueos, etc, cualquier metodo es viable. Para
nuestros propositos de demostracion, usaremos ftok(). Si suponemos que cada
proceso cliente estara ejecutandose desde ununico directorio "home", las claves
generadas deben bastar por nuestras necesidades.
    El valor clave, sin embargo, se obtiene, se usa una llamada al sistema IPC
para crear u obtener acceso a los objetos IPC.



Comando ipcs


El comando ipcs puede utilizarse para obtener el estado de todos los objetos
IPC Sistema V. La version para Linux de esta utilidad tambien fue preparada
por Krishna Balasubramanian.
___________________________________________________________________________________

ipcs       -q:     Mostrar  solo  colas  de  mensajes
ipcs       -s:     Mostrar  solo  los  semaforos
ipcs       -m:     Mostrar  solo  la  memoria  compartida
ipcs  --help:     Otros  argumentos

6.4.  IPC EN SISTEMA V                                                    35



___________________________________________________________________________________

    Por defecto, se muestran las tres categorias. Considerese el siguiente ejemplo
de salida del comando ipcs:
___________________________________________________________________________________



------  Shared  Memory  Segments  --------
shmid       owner       perms       bytes       nattch     status


------  Semaphore  Arrays  --------
semid       owner       perms       nsems       status


------  Message  Queues  --------
msqid       owner       perms       used-bytes   messages
0            root        660         5              1

___________________________________________________________________________________

    Aqui vemos una simple cola mensaje que tiene un identificador " 0."  Es
propiedad del root, y tiene permisos en octal de 660, o -rw-rw--.  Hay un
mensaje en la cola, y ese mensaje tiene un tama"no del total de 5 bytes.

    Los comandos ipcs son una herramienta muy potente que proporciona una
leve introduccion en los mecanismos de almacenamiento del nucleo para objetos
IPC. Aprendalo,uselo, reverencielo.



El Comando ipcrm


Se puede usar el comando ipcrm para quitar un objeto IPC del nucleo. Mientras
que los objetos IPC se pueden quitar mediante llamadas al sistema en el codigo
del usuario (veremos como en un momento),aparece a menudo la necesidad,
sobre todo en ambientes del desarrollo, de quitar objetos IPC a mano. Su uso
es simple:
___________________________________________________________________________________


ipcrm  <msg  |  sem  |  shm>   <IPC  ID>

___________________________________________________________________________________

    Simplemente especifique si el objeto a eliminar es una cola de mensaje ( em
msg), un semaforo (sem), o un segmento de memoria compartida (shm).  El
identificador de IPC se puede obtenr mediante los comandos ipcs.  Tiene que
especificar el tipo de objeto, como los identificadores sonunicos entre los del
mismo tipo (retome nuestra discusion anterior).



6.4.2    Colas de Mensajes


Conceptos Basicos


Las colas de mensaje se pueden describir mejor como una lista enlazada inte-
rior dentro del espacio de direccionamiento del nucleo.Los mensajes se pueden
enviar a la cola en orden y recuperarlos de la cola en varias maneras diferentes.
Cada cola de mensaje (por supuesto) esta identificada de formaunica por un
identificador IPC.

36       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



Estructuras interna y de datos de usuario


La clave para comprender totalmente tales temas complejos como el IPC Siste-
ma V es familiarizarse con las distintas estrcturas de datos internas que residen
dentro de los confines del nucleo mismo.El acceso directo a algunas de estas es-
tructuras es necesario incluso en las operacions mas primitivas, mientras otros
residen a un nivel mucho mas bajo.



Buffer  de  Mensaje   La primera estructura que veremos es la estructura
msgbuf.  Esta particular estructura de datos puede ser interpretada como una
plantilla  por datos del mensaje. Mientras que un programador puede elegir si
definir estructuras de este tipo, es imperativo que entiende que hay realmente
una estructura del tipo msgbuf. Se declara en linux/msg.h como sigue:
___________________________________________________________________________________

/*  buffer  de  mensaje  para  llamadas  msgsnd  y  msgrcv  */
struct  msgbuf  {
     long  mtype;            /*  tipo  de  mensaje  */
     char  mtext[1];        /*  texto  del  mensaje  */
};
___________________________________________________________________________________
    Hay dos miembros en la estructura msgbuf:


mtype

      El tipo de mensaje, representado por un numero positivo. <Y debe ser un
      numero positivo!


mtext

      Los datos del mensaje en si mismo.


    La habilidad asignarle a un mensaje dado un tipo, esencialmente le da la
capacidad de multiplexar mensajes en una cola sola.  Por ejemplo, al proceso
cliente se puede asignar a un numero magico, que se puede usar como el tipo
de mensaje para mensajes enviados desde un proceso servidor.  El servidor
mismo podria usar algunos otros numeros, que los clientes podrian usar para
enviarle mensajes.  Por otra parte, una aplicacion podria marcar mensajes de
error como tener un mensaje tipo 1, peticion de mensajes podrian ser tipo 2,
etc. Las posibilidades son interminables.
    En otra nota no se confunda por el nombre demasiado descriptivo asignado
al elemento dato del mensaje (mtext).  Este campo no se restringe a contener
solo arrays de caracteres, sino cualquier tipo e dato, en cualquier forma.  El
campo mismo es realmente arbitrario, ya que esta estructura es redefinida por
el programador de la aplicacion. Considere esta redefinicion:
___________________________________________________________________________________

struct  my_msgbuf  {
          long     mtype;             /*  Tipo  de  mensaje  */
          long     request_id;       /*  Identificador  de  peticion  */
          struct   client  info;     /*  Estructura  de  informacion  del  cliente  */
};

6.4.  IPC EN SISTEMA V                                                    37



___________________________________________________________________________________

    Aqui vemos el tipo de mensaje, como antes, pero el resto de la estructura ha
sido reemplazado por otros dos elementos, uno de los cuales es otra estructura.
Esta es la belleza de las colas de mensajes. El nucleo no hace ninguna traduccion

de datos. Se puede enviar cualquier informacion.

    Sin embargo, existe un limite interior del maximo tama"no de un mensaje
dado. En Linux se define este en linux/msg.h como sigue:
___________________________________________________________________________________


#define  MSGMAX   4056    /*  <=  4056  */    /*  Tama~no  maximo  del  mensaje  (bytes)  */

___________________________________________________________________________________

    El mensajes no puede ser mayor de 4,056 bytes en total, incluyendo el miem-
bro mtype, que tiene una longitud de 4 bytes (long).



Estructura msg del Nucleo   El nucleo guarda cada mensaje en la cola dentro
de la estructura msg. Se define en linux/msg.h como sigue:
___________________________________________________________________________________


/*  one  msg  structure  for  each  message  */
struct  msg  {
     struct  msg  *msg_next;    /*  siguiente  mensaje  de  la  cola  */
     long   msg_type;
     char  *msg_spot;            /*  direccion  del  texto  del  mensaje  */
     short  msg_ts;              /*  tama~no  del  texto  del  mensaje  */
};

___________________________________________________________________________________

msg_next

      Es un puntero al siguiente mensaje de la cola. Se almacenan como ouna
      lista simple enlazada en el espacio de direcciones del nucleo.


msg_type

      Este es el tipo de mensaje, como asignado en la estructura msgbuf del

      usuario.


msg_spot

      Un puntero al inicio del cuerpo del mensaje.


msg_ts

      La longitud del texto del mensaje o del cuerpo.



Estructura msqid_ds del nucleo   Cada uno de los tres tipos de objetos IPC
tienen una estructura de datos interna que se mantiene por el nucleo.  Para
las colas de mensaje, es la estructura msqid_ds.  El nucleo crea, almacena, y
mantiene un caso de esta estructura por cada cola de mensaje hace cola creada
en el sistema. Se define en linux/msg.h de la siguiente forma:
___________________________________________________________________________________

38       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



/*  una  estructura  msqid  por  cada  cola  del  sistema  */
struct  msqid_ds  {
     struct  ipc_perm  msg_perm;
     struct  msg  *msg_first;   /*  primer  mensaje  de  la  cola  */
     struct  msg  *msg_last;    /*  ultimo  mensaje  */
     time_t  msg_stime;         /*  ultimo  instante  de  msgsnd  */
     time_t  msg_rtime;         /*  ultimo  instante  de  msgrcv  */
     time_t  msg_ctime;         /*  ultimo  instante  cambio  */
     struct  wait_queue  *wwait;
     struct  wait_queue  *rwait;
     ushort  msg_cbytes;
     ushort  msg_qnum;
     ushort  msg_qbytes;        /*  numero  maximo  de  bytes  en  cola  */
     ushort  msg_lspid;         /*  pid  del  ultimo  msgsnd  */
     ushort  msg_lrpid;         /*  pid  de  la  ultima  recepcion  */
};

___________________________________________________________________________________

    Aunque tendra raramente que usar la mayor parte de los miembros de es-
ta estructura, daremos una descripcion breve de esta para completar nuestra
vision:



msg_perm

      Un caso de la estructura ipc_perm, que se define en linux/ipc.h.  Este
      recoge la informacion del permiso para la cola de mensaje, incluso los
      permisos del acceso, e informacion sobre el creador de la cola (uid, etc).


msg_first

      Enlace al primer mensaje de la cola (cabecera de la lista).


msg_last

      Enlace alultimo mensaje de la cola (cola de la lista).


msg_stime

      Instante (time_t) delultimo mensaje que se envio a la cola.


msg_rtime

      Instante delultimo mensaje recuperado de la cola.


msg_ctime

      Instantedelultimo cambio hecho a la cola. (hablaremos de esto mas tar-
      de).


wwait

      y

6.4.  IPC EN SISTEMA V                                                    39



rwait

      Punteros a la cola de espera del nucleo.  Se usan cuando una operacion
      sobre una cola de mensajes estima que el proceso entra en el estado de
      dormido (es decir, la cola esta llena y el porceso espera una apertura).


msg_cbytes

      Numero total number de bytes que hay en la cola (suma de los tama"nos
      de todos los mensajes).


msg_qnum

      Numero de mansajes actual en la cola.


msg_qbytes

      Maximo numero de bytes en la cola.


msg_lspid

      El PID del proceso que envia elultimo mensaje.


msg_lrpid

      El PID del proceso que recupera elultimo mensaje.



Estructura ipc_perm del nucleo   El nucleo guarda informacion de permisos
para objetos IPC en una estructura de tipo ipc_perm.  Por ejemplo,  en la
estructura interna para una cola de mensaje descrita antes,  el miembro de
msg_perm es de este tipo. Se declara en linux/ipc.h como sigue:
___________________________________________________________________________________


struct  ipc_perm
{
   key_t   key;
   ushort  uid;    /*  euid  y  egid  del  propietario  */
   ushort  gid;
   ushort  cuid;   /*  euid  y  egid  del  creador  */
   ushort  cgid;
   ushort  mode;   /*  modos  de  acceso,  veanse  despues  los  valores  */
   ushort  seq;    /*  numero  de  secuencia  del  slot  */
};
___________________________________________________________________________________
    Todo lo anterior es bastante autoexplicativo.  Guardado junto con la clave
IPC del objeto hay informacion sobre el creador y due"no del objeto (pueden ser
diferentes).Los modos del acceso octal como un unsigned  short. Finalmente,
la secuencia del slot se guarda al final.  Cada vez qye un objeto IPC se cierra
mediante una llamada al sistema llama (destruye), este valor se incrementa por
el maximo numera de objetos IPC que pueden residir en un sistema. >Tendra
que usar este valor? No.
    NOTA:Hay una excelente exposicion de este tema, y los asuntos de segu-
ridad relacionados, en el libro UNIX Network Programming, de Richard
Stevens (pagina 125).

40       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



LLAMADA AL SISTEMA: msgget()


Para crear una nueva cola de mensajes, o acceder a una existente, usaremos la
llamada al sistema msgget().
___________________________________________________________________________________

   LLAMADA  AL  SISTEMA:  msgget();


   PROTOTIPO:  int  msgget  (  key_t  clave,  int  msgflg  );
     RETORNA:  Si  hay exito,  identificador  de  la  cola  de  mensajes
                 -1  si  error:  errno  =  EACCESS  (permiso  denegado)
                                           EEXIST  (No  puede  crearse  la  cola  pues  ya  existe)
                                           EIDRM  (La  cola  esta  marcada  para  borrarse)
                                           ENOENT  (La  cola  no  existe)
                                           ENOMEM  (No  hay  memoria  para  crear  la  cola)
                                           ENOSPC  (Se  ha  superado  el  limite  de  colas)
   NOTAS:
___________________________________________________________________________________

    El primer argumento de msgget() es el valor clave (en nuestro caso devuelto
por una llamada a ftok(). Este valor clave se compara entonces con los valores
clave que existen dentro del nucleo de otras colas de mensaje.  En ese punto
las operaciones de apertura o acceso depende de los contenidos del argumento
msgflg.


IPC_CREAT

      Crea la cola si aun no existe en el nucleo.


IPC_EXCL

      Cuando se usa con IPC_CREAT, falla si la cola ya existe.


    Si usamos solo IPC_CREAT, msgget() retornara el identificador de una cola
nueva, o bien el de la existente con la misma clave. Si usamos ademas IPC_EXCL,
la llamada creara una nueva cola o fallara si la cola con esa clave ya existia. La
opcion IPC_EXCL es pocoutil si no se usa combinada con IPC_CREAT.
    Es posible incluir en la mascara un modo opcional octal, pues cada objeto
IPC tiene un esquema de permisos de acceso similar a cualquier archivo del
sistema Unix.
    Creamos una funcion de envoltura rapida para abriro crear una cola de
mensaje:
___________________________________________________________________________________

int  abrir_cola(  key_t  keyval  )
{
          int       qid;


          if((qid  =  msgget(  keyval,  IPC_CREAT  |  0660  ))  ==  -1)
          {
                     return(-1);
          }

6.4.  IPC EN SISTEMA V                                                    41

           return(qid);
}
___________________________________________________________________________________

    Notese el uso del modo de permisos 0660. Esta peque"na funcion retornara,
bien un identificador entero de la cola de mensajes, o -1 si hubo error. El valor
de la clave (keyval) debe ser elunico argumento de la llamada a la funcion.



LLAMADA AL SISTEMA: msgsnd()


Una vez que tenemos el identificador de la cola, podemos empezar a realizar
operaciones sobre ella. Para entregar un mensaje a una cola, use la llamada al
sistema msgsndl:
___________________________________________________________________________________

   LLAMADA  AL  SISTEMA:  msgsnd();


   PROTOTIPO:  int  msgsnd  (  int  msqid,  struct  msgbuf  *msgp,  int  msgsz,  int  msgflg  );
     RETORNA:  0  si exito
                 -1  si  error:  errno  =  EAGAIN  (la  cola  esta  llena,  y  se  uso  IPC_NOWAIT).
                                           EACCES  (permiso  denegado,  no  se  puede  escribir)
                                           EFAULT  (Direccion  de  memoria  de  msgp  invalida)
                                           EIDRM   (La  cola  de  mensajes  fue  borrada)
                                           EINTR   (Se  recibio  una  se~nal  mientras  se  esperaba  para  escribir)
                                           EINVAL  (Identificador  de  cola  invalido,  tipo
                                                     no  positivo  o  tama~no  de  mensaje  invalido)
                                           ENOMEM  (No  hay  memoria  suficiente  para  copiar  el  buffer)
   NOTAS:
___________________________________________________________________________________

    El primer argumento de msgsnd es nuestro identificador de la cola, devuelto
por un llamada previa a msgget. El segundo argumento, msgp, es un puntero a
nuestro buffer redeclarado y cargado. El argumento msgsz contiene el tama"no
del mensaje en bytes, excluye la longitud del tipo de mensaje (4 byte).
    El argumento msgflg se puede poner a cero (ignorado), o:


IPC_NOWAIT

      Si la cola del mensaje esta llena,  entonces no se escribe en la cola el
      mensaje, y se le devuelve el control la proceso llamador. Si no se especifi-
      ca, entonces el proceso llamador se suspendera (bloqueado) hasta que se
      puede escribir el mensaje.


    Creamos otra funcion de la envoltura por enviar mensajes:
___________________________________________________________________________________

int  enviar_msj(  int  qid,  struct  mymsgbuf  *qbuf  )
{
          int       resultado,  longitud;


          /*  La  longitud  es  esencialmente  el  tama~no  de  la  estructura  menos  sizeof(mtype)  */

42       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



          longitud  =  sizeof(struct  mymsgbuf)  -  sizeof(long);


          if((resultado  =  msgsnd(  qid,  qbuf,  length,  0))  ==  -1)
          {
                    return(-1);
          }


          return(resultado);
}
___________________________________________________________________________________
    Esta peque"na funcion intenta enviar un mensaje almacenado en la direccion
pasada (qbuf) a la cola de mensajes identificada por el numero pasado en el
argumento qid.  Aqui tenemos un programa de ejemplo que utiliza las dos
funciones que hemos desarrollado aqui:
___________________________________________________________________________________


#include  <stdio.h>
#include  <stdlib.h>
#include  <linux/ipc.h>
#include  <linux/msg.h>


main()
{
          int     qid;
          key_t   msgkey;
          struct  mymsgbuf  {
                     long     mtype;             /*  Tipo  de  mensaje  */
                     int       request;          /*  Numero  de  trabajo  */
                     double   salary;            /*  Salario  del  empleado  */
          }  msg;


          /*  Generamos  nuestra  clave  IPC  */
          msgkey  =  ftok(".",  'm');


          /*  Abrir/crear  la  cola  */
          if((  qid  =  abrir_cola(  msgkey))  ==  -1)  {
                     perror("abrir_cola");
                     exit(1);
          }


          /*  Preparar  mensajes  con  datos  arbitrarios  */
          msg.mtype    =  1;          /*  !El  mensaje  debe  ser  numero  positivo!  */
          msg.request  =  1;          /*  Dato  numero  1  */
          msg.salary   =  1000.00;   /*  Dato  numero  2  (!mi  salario  anual!)  */


          /*  !Bombear  mensaje!  */
          if((enviar_msj(  qid,  &msg  ))  ==  -1)  {
                     perror("enviar_msj");

6.4.  IPC EN SISTEMA V                                                    43



                     exit(1);
           }
}

___________________________________________________________________________________

    Tras crear/abrir la cola de mensajes, pasamos a preparar el buffer del men-
saje con datos de prueba (note la falta de datos de tipo caracter para ilus-
trar nuestro punto sobre envio de informacion binaria). Una simple llamada a
enviar_msj envia nuestro mensaje a la cola.

    Ahora que tenemos un mensaje en la cola, probemos en comando ipcs para
comprobar el estado de esta.  Ahora continuaremos con la discusion para ver
como leer informacion del mensaje.  Para ello, se utiliza la llamada al sistema
msgrcv():
___________________________________________________________________________________


   LLAMADA  AL  SISTEMA:  msgrcv();
   PROTOTIPO:  int  msgrcv  (  int  msqid,  struct  msgbuf  *msgp,  int  msgsz,  long  mtype,  int  msgflg  );
     RETURNS:  Numero  de  bytes  copiados  en  la  cola  de  mensajes
                 -1  si  error:  errno  =  E2BIG   (La  longitud  del  mensaje  es  mayor  que  msgsz)
                                           EACCES  (No  hay  permiso  de  lectura)
                                           EFAULT  (La  direccion  del  buffer  msgp  es  incorrecta)
                                           EIDRM   (La  cola  fue  eliminada  durante  la  lectura)
                                           EINTR   (Interrumpido  por  llegada  de  se~nal)
                                           EINVAL  (msgqid  invalida,  o  msgsz  menor  que  0)
                                           ENOMSG  (IPC_NOWAIT  incluido,  y  no  hay  mensajeen  la
                                                     cola  disponible  para  leer)
   NOTAS:
___________________________________________________________________________________

    Obviamente, el primer argumento se usa para especificar la cola utilizada
durante el proceso de recuperacion del mensaje (se deberia haber sido devuelto
por una llamada anterior a msgget). El segundo argumento (msgp) representa la
direccion de una variable buffer de mensaje para guardar el mensaje recuperado.
El tercer argumento, (msgsz), representa el tama"no de la estructura del buffer
del mensaje, excluye la longitud del miembro de mtype. Una vez mas, se puede
calcular este facilmente como:
___________________________________________________________________________________


msgsz  =  sizeof(struct  mymsgbuf)  -  sizeof(long);

___________________________________________________________________________________

    El cuarto argumento (mtype) especifica el tipo de mensaje a recuperar de la
cola.  El nucleo buscara la cola por elultimo mensaje que cuadra con el tipo,
y le devolvera a una copia de el en la direccion apuntada a por el argumento
msgp. Existe un caso especial. Si se pasa el argumento mtype con un valor de
ceros, entonces se devuelve el mensaje mas viejo en la cola, independiente del
tipo.

    Si se pasa como una bandera IPC_NOWAIT, y no hay ningun mensajes
disponibles, la llamada le devuelve ENOMSG al proceso llamador. Por otra parte,
el proceso llamador se bloquea hasta que un mensaje llega a la cola que satisface
el parametro msgrcv().  Si se anula la cola mientras un cliente espera en un

44       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



mensaje, se devuelve EIDRM. Se devuelve EINTR si se coge una se"nal mientras el
proceso esta en medio del bloqueo, y espera la llegada de un mensaje.
   Examinamos una funcion de envoltura rapida para recuperar un mensaje
de_nuestra_cola:___________________________________________________________________


int  leer_msj(  int  qid,  long  type,  struct  mymsgbuf  *qbuf  )
{
          int       resultado,  longitud;


          /*  La  longitud  es  esencialmente  el  tama~no  del  buffer  menos  sizeof(long)  */
          longitud  =  sizeof(struct  mymsgbuf)  -  sizeof(long);


          if((resultado  =  msgrcv(  qid,  qbuf,  length,  type,   0))  ==  -1)
          {
                     return(-1);
          }


          return(resultado);
}
___________________________________________________________________________________
    Despues de terminar de forma efectiva la recuperacion de un mensaje en la
cola, se destruye la entrada del mensaje dentro de la cola.
    El bit MSG_NOERROR del argumento msgflg proporciona algunas ca-
pacidades adicionales. Si el tama"no de los datos del mensaje fisico es mayor que
msgsz, y MSG_NOERROR esta indicado, entonces se trunca el mensaje, y
se devuelven solo msgsz bytes.  Normalmente, la llamada al sistema msgrcv()
devuelve -1 (E2BIG), y el mensaje quedara en la cola para una recuperacion
posterior.  Esta conducta se puede usar para crear otra funcion de envoltu-
ra, que nos permitira "mirar" en la cola, para ver si un mensaje ha llegado y
satisface nuestra demanda, sin sacarlo realmente de la cola:
___________________________________________________________________________________

int  mirar_msj(  int  qid,  long  type  )
{
          int       resultado,  longitud;


          if((resultado  =  msgrcv(  qid,  NULL,  0,  type,   IPC_NOWAIT))  ==  -1)
          {
                     if(errno  ==  E2BIG)
                               return(TRUE);
          }


          return(FALSE);
}
___________________________________________________________________________________
    Arriba, se dara cuenta de la falta de una direccion de buffer y una longitud.
En este caso particular queremos que la llamada falle. Sin embargo, verificamos
por el retorno de E2BIG que indica que existe un mensa del tipo de nuestra

6.4.  IPC EN SISTEMA V                                                    45



peticion. La funcion de envoltura vuelve TRUE en exito, FALSO en otro caso.
Tambien observa el uso de IPC_NOWAIT, que previene el compoeramiento
de bloque visto antes.



LLAMADA AL SISTEMA: msgctl()


Por el desarrollo de las funciones de envoltura anteriores, ahora tiene una aproxi-
macion simple y elegante para crear y utilizar las estructuras internas asociadas
con colas de mensaje en sus aplicaciones. Ahora, volveremos directamente a la
discusion sobre la manipulacion de las estructuras internas asociadas con una
colas de mensaje dada.

    Para realizar operaciones de control en una cola de mensaje, use la llamada
al sistema msgctl().
___________________________________________________________________________________


   LLAMADA  AL  SISTEMA:  msgctl();
   PROTOTIPO:  int  msgctl  (  int  msgqid,  int  cmd,  struct  msqid_ds  *buf  );
     RETORNA:  0  si exito
                 -1  si  error:  errno  =  EACCES  (No  hay  permiso  de  lectura  y  cmd  vale  IPC_STAT)
                                           EFAULT  (Direccion  de  buf  invalida  con  los  comandos  IPC_SET  y
                                                     IPC_STAT)
                                           EIDRM   (La  cola  fue  eliminada  durante  la  operacion)
                                           EINVAL  (msgqid  invalida,  o  msgsz  menor  que  0)
                                           EPERM   (Se  intento  el  comando  IPC_SET  o  IPC_RMID,  pero
                                                     no  tiene  acceso  de  escritura  (alteracion)
                                                     de  la  cola)
   NOTAS:

___________________________________________________________________________________

    Ahora, el sentido comun dice que la manipulacion directa de las estructuras
de datos internas del nucleo podria ocasionar alguna juerga nocturna.  Des-
graciadamente, los deberes resultantes por parte del programador se podrian
clasificar como diversion solo si gusta desecha el subsistema IPC. Usando msgc-
tl() con un conjunto selectivo de ordenes, tiene la posibilidad de manipular esos
elementos, que es menos probable que causen problemas.  Echemos un vistazo
a estos comandos:



IPC_STAT

      Recupera la estructura msqid_ds para una cola, y, la en la direccion del
      argumento buff.


IPC_SET

      Pone el valor del miembro ipc_perm de la estructura msqid_ds para la
      cola. Toma los valores del argumento buf.


IPC_RMID

      Borra la cola del nucleo.

46       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



   Retomamos nuestra discusion sobre las estructuras de datos internas para
colas de mensaje (msqid_ds). El nucleo mantiene una instancia de esta estruc-
tura por cada cola que existe en el sistema.  Usando el comando IPC_STAT,
podemos recuperar una copia de esta estructura para examinarla. Miramos una
funcion de envoltura rapida que recuperara la estructura interna y la copia en
una direccion pasada:
___________________________________________________________________________________

int  leer_queue_ds(  int  qid,  struct  msgqid_ds  *qbuf  )
{
          if(  msgctl(  qid,  IPC_STAT,  qbuf)  ==  -1)
          {
                     return(-1);
          }


          return(0);
}
___________________________________________________________________________________
    Si no podemos copiar el buffer interno, se devuelve -1 a la funcion que hizo la
llamadda. Si todo fue bien, se devuelve un valor 0 (cero), y el buffer pasado debe
contener una copia de la estructura de datos interna para la cola representada
por el identificador de cola pasado (qid).
    >Ahora que tenemos una copia de las estructura de datos interna de una
cola, que se puede manipular, y como se puede alterar?  Elunico elemento
modificable en la estructura de los datos es el miembro ipc_perm. Este contiene
los permisos para la cola, asi como informacion sobre el due"no y creador.  Sin
embargo, losunicos miembros de la estructura ipc_perm que son modificables
son modo, uid, y gid. Puede cambiar el id del usuario del due"no, el id del grupo
del due"no, y los permisos del acceso para la cola.
    Creamos una funcion de envoltura dise"nada para cambiar el modo de una
cola.  Se debe pasar el modo en como un array de caracteres (por ejemplo
"660").
___________________________________________________________________________________

int  cambiar_modo_cola(  int  qid,  char  *modo  )
{
          struct  msqid_ds  tmpbuf;


          /*  Obtener  copia  de  la  actual  estructura  de  datos  interna  */
          leer_queue_ds(  qid,  &tmpbuf);


          /*  Cambiar  los  permisos  usando  un  viejo  truco  */
          sscanf(mode,  "%ho",  &tmpbuf.msg_perm.mode);


          /*  Actualizar  la  estructura  de  datos  interna  */
          if(  msgctl(  qid,  IPC_SET,  &tmpbuf)  ==  -1)
          {
                     return(-1);
          }

6.4.  IPC EN SISTEMA V                                                    47

           return(0);
}
___________________________________________________________________________________

    Recuperamos una copia de la estructura de datos interna actual mediante
una rapida llamada a nuestra funcion de envoltura leer_queue_ds.  Entonces
hacemos una llamada a sscanf() para alterar el miembro modo de la estructura
msg_perm asociada. Sin embargo, no se producen cambios hasta que se usa la
nueva copia para actualizar la version interna. Esto es ejecutado mediante una
llamada a msgctl() usando el comando el IPC_SET.
    <TENGA CUIDADO! <Es posible alterar los permisos en una cola,  y al
hacerlo, puede cerrarse sin darse cuenta. Recuerde, estos objetos IPC no se van
a menos que se quiten propiamente, o el sistema se reinicie.  Asi, aun cuando
no pueda ver una cola con ipcs no significa que no este alli.


          Para ilustrar este punto, una anecdota algo comica parece estar a
      punto.  Mientras daba una clase de ense"nanza sobre UNIX interno
      en la Universidad de Florida Sur, tropece con un bloque bastante
      penoso.  Habia marcado en el servidor del laboratorio la noche de
      antes, para compilar y probar el trabajo de la clase de la semana.
      En el proceso de comprobacion, me di cuenta de que habia hecho
      un typo en la codificacion para alterar los permisos en una cola de
      mensaje.  Cree una cola simple de mensaje, y probe el envio y la
      recepcion problemas. <Sin embargo, cuando intente cambiar el modo
      de la cola de "660" a "600", la accion resultante fue que se cerro con
      llave fuera de mi propia cola!  Como un resultado, no podia probar
      la cola de mensaje en la misma area de mi directorio de fuente.
      Entonces use la funcion ftok() para crear el codigo IPC codifica,
      trataba de acceder a una cola para la que no tenia permisos. Acabe
      y me puse en contacto con el administrador del sistema local en la
      ma"nana siguiente, perdiendo una hora para explicarle a el lo que
      era una cola del mensaje, y porque necesitaba correr los comandos
      ipcrm.


    Despues de recuperar con exito un mensaje de la cola, se quita el mensaje.
Sin embargo, como mencione antes, los objetos IPC quedan en el sistema a me-
nos que se quiten explicitamente, o el sistema sea reiniciado. Por consiguiente,
nuestra cola de mensaje hace cola todavia existe dentro del nucleo, disponible
para usarla despues de que solo un simple mensaje desaparezca. Para comple-
tar el ciclo de vida de una cola de mensaje, se deben quitar con una llamada a
msgctl(), usando el comando IPC_RMID:
___________________________________________________________________________________

int  borrar_cola(  int  qid  )
{
          if(  msgctl(  qid,  IPC_RMID,  0)  ==  -1)
          {
                     return(-1);

 48       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



           }


           return(0);
 }

 ___________________________________________________________________________________

     Esta funcion devuelve 0 si la cola se borro sin problemas, o si no devuelve
 un valor -1.  El borrado de la cola es atomico, y cualquier acceso posterior a
 ella para cualquier cosa fallara.



 msgtool: Un manipulador interactivo de colas de mensajes.


 Pocos pueden negar el beneficio inmediato de tener informacion tecnica exacta
 rapidamente disponible. Tales materiales proporcionan un mecanismo tremen-
 do para el aprendizaje y la exploracion de areas nuevas.  En la misma nota,
 teniendo ejemplos reales para acompa"nar cualquier informacion tecnica, acele-
 rara y reforzara el proceso de aprendizaje.

     Hasta ahora, losunicos ejemplosutiles que se han presentado eran las fun-
 ciones de envoltura para manipular colas de mensaje. Aunque son sumamente
utiles, no se han presentado en una manera que garantice su estudio y experi-
 mentacion. Esto se solucionara con msgtool, una utilidad de la linea de comando
 interactiva parar manipular colas de mensaje IPC. Mientras funciona como una
 herramienta adecuada para refuerzo de la educacion, tambien se puede aplicar
 directamente en asignaciones reales, para proporcionar la funcionalidad de las
 colas de mensaje mediante script de shell normales.



 Vistazo rapido   El programa msgtool cuenta con argumentos de la linea del
 comando para determinar su comportamiento. Este es lo que lo hace especial-
 menteutil cuando es llamado desde script de shell. Se proporcionan todas las
 capacidades, de crear, enviar, y recuperar, a cambiar los permisos y finalmente
 eliminar una cola. Actualmente, usa un array de caracteres para los datos, per-
 mitiendole enviar mensajes textuales. Los cambios para facilitar tipos de datos
 adicionales se queda como un ejercicio para el lector.



 Sintaxis de la linea de comandos



     Envio de mensajes


 msgtool  e  (tipo)  "texto"



     Recepcion de Mensajes


 msgtool  r  (tipo)



     Cambio de los permisos


 msgtool  m  (modo)

6.4.  IPC EN SISTEMA V                                                    49



    Borrado de una cola


msgtool  d

___________________________________________________________________________________
Ejemplos


msgtool   e    1  prueba
msgtool   e    5  prueba
msgtool   e    1  "Esto  es  una  prueba"
msgtool   r    1
msgtool   b
msgtool   m    660
___________________________________________________________________________________


Codigo  Fuente   Seguidamente  ofrecemos  el  codigo  fuente  de  la  utilidad
msgtool.  Debe compilar sin problemas con cualquier revision (decente) del
nucleo que soporte IPC Sistema V. <Asegurese de activar el IPC en el nucleo
cuando lo recompile!
    Como comentario, esta utilidad creara una cola de mensajes si no existe,
independientemente del tipo de accion solicitado.


          NOTA: Puesto que esta utilidad usa ftok() para generar claves
      IPC, pueden encontrarse conflictos de directorios. Si cambia de di-
      rectorio durante la ejecucion de su script, posiblemente no funcione
      bien. Otra solucion seria codificar dentro del programa msgtool un
      path completo a la utilidad, tal como "/tmp/msgtool", o bien pasarle
      dicho path mediante un nuevo argumento de la linea de comandos.

___________________________________________________________________________________


/*****************************************************************************
  Parte  de  la  "Guia  Linux  de  Programacion  -  Capitulo  6"
  (C)opyright  1994-1995,  Scott  Burkett
  *****************************************************************************
  MODULO:  msgtool.c
  *****************************************************************************
  Utilidad  de  manejo  de  las  colas  de  mensajes  del  sistema  IPC  SYSV
  *****************************************************************************/


#include  <stdio.h>
#include  <stdlib.h>
#include  <ctype.h>
#include  <sys/types.h>
#include  <sys/ipc.h>
#include  <sys/msg.h>


#define  MAX_SEND_SIZE  80


struct  mymsgbuf  {

50       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



          long  mtype;
          char  mtext[MAX_SEND_SIZE];
};


void  send_message(int  qid,  struct  mymsgbuf  *qbuf,  long  type,  char  *text);
void  read_message(int  qid,  struct  mymsgbuf  *qbuf,  long  type);
void  remove_queue(int  qid);
void  change_queue_mode(int  qid,  char  *mode);
void  usage(void);
int  main(int  argc,  char  *argv[])
{
          key_t  key;
          int    msgqueue_id;
          struct  mymsgbuf  qbuf;


          if(argc  ==  1)
                    usage();


          /*  Crear  clave  unica  mediante  ftok()  */
          key  =  ftok(".",  'm');


          /*  Abrir  la  cola  --  crearla  si  es  necesario  */
          if((msgqueue_id  =  msgget(key,  IPC_CREAT|0660))  ==  -1)  {
                    perror("msgget");
                    exit(1);
          }


          switch(tolower(argv[1][0]))
          {
                    case  'e':  send_message(msgqueue_id,  (struct  mymsgbuf  *)&qbuf,
                                                 atol(argv[2]),  argv[3]);
                                 break;
                    case  'r':  read_message(msgqueue_id,  &qbuf,  atol(argv[2]));
                                 break;
                    case  'b':  remove_queue(msgqueue_id);
                                 break;
                    case  'm':  change_queue_mode(msgqueue_id,  argv[2]);
                                 break;


                     default:  usage();


          }


          return(0);
}

6.4.  IPC EN SISTEMA V                                                    51

void  send_message(int  qid,  struct  mymsgbuf  *qbuf,  long  type,  char  *text)
{
           /*  Enviar  mensaje  a  la  cola  */
           printf("Enviando  mensaje  ...\n");
           qbuf->mtype  =  type;
           strcpy(qbuf->mtext,  text);


           if((msgsnd(qid,  (struct  msgbuf  *)qbuf,
                     strlen(qbuf->mtext)+1,  0))  ==-1)
           {
                     perror("msgsnd");
                     exit(1);
           }
}


void  read_message(int  qid,  struct  mymsgbuf  *qbuf,  long  type)
{
           /*  Leer  mensaje  de  la  cola  */
           printf("Leyendo  mensaje  ...\n");
           qbuf->mtype  =  type;
           msgrcv(qid,  (struct  msgbuf  *)qbuf,  MAX_SEND_SIZE,  type,  0);


           printf("Tipo:  %ld  Texto:  %s\n",  qbuf->mtype,  qbuf->mtext);
}


void  remove_queue(int  qid)
{
           /*  Borrado  de  la  cola  */
           msgctl(qid,  IPC_RMID,  0);
}


void  change_queue_mode(int  qid,  char  *mode)
{
           struct  msqid_ds  myqueue_ds;


           /*  Obtener  informacion  actual  */
           msgctl(qid,  IPC_STAT,  &myqueue_ds);


           /*  Convertir  y  cargar  el  modo  */
           sscanf(mode,  "%ho",  &myqueue_ds.msg_perm.mode);


           /*  Actualizar  el  modo  */
           msgctl(qid,  IPC_SET,  &myqueue_ds);
}


void  usage(void)

52       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



{
          fprintf(stderr,  "msgtool  -  Utilidad  de  manejo  de  colas  de  mensajes\n");
          fprintf(stderr,  "\nUSO:  msgtool  (e)nviar  <tipo>  <texto>\n");
          fprintf(stderr,  "                   (r)ecibir  <tipo>\n");
          fprintf(stderr,  "                   (b)orrar\n");
          fprintf(stderr,  "                   (m)odo  <modo  octal>\n");
          exit(1);
}


___________________________________________________________________________________


6.4.3    Semaforos


Conceptos Basicos


Los semaforos se pueden describir mejor como contadores que se usan para
controlar el acceso a recursos compartidos por multiples procesos. Se usan con
mas frecuencia como un mecanismo de cierre para prevenir que los procesos
accedan a un recurso particular mientras otro proceso lo esta utilizando.  Los
semaforos son a menudo considerados como el mas dificil asir de los tres tipos
de objetos Sistema V IPC. Para comprender totalmente los semaforos, los dis-
cutiremos brevemente antes de comenzar cualquier llamada al sistema y teoria
operacional.

    El nombre de semaforo es realmente un termino viejo del ferrocarril, que se
usaba para prevenir, en las travesias el cruce en las vias de los viejos carros.
Exactamente lo mismo se puede decir sobre un semaforo.  Si el semaforo esta
abierto (los brazos en alto),  entonces un recurso esta disponible (los carros
cruzarian las vias).  Sin embargo, si el semaforo esta cerrado (los brazos estan
abajo), entonces recursos no estan disponible (los carros deben esperar).

    Mientras que con este este ejemplo simple nos introduce el concepto, es
importante darse cuenta de que los semaforos se llevan a cabo realmente como
conjuntos, en lugar de como entidades solas.  Por supuesto, un conjunto de
semaforos dado puede tener solo un semaforo, como en nuestro ejemplo del
ferrocarril.

    Quizas otra aproximacion al concepto de semaforos, seria pensar en ellos
como contadores de recursos.  Apliquemos este concepto a otro caso del mun-
do real.  Considere un spooler de impresion, capaz de manipular impresoras
multiples,  con cada manejo de la impresora con demandas de la impresion
multiples. Un hipotetico manejador del spool de impresion utilizara un conjun-
to de semaforos para supervisar el acceso a cada impresora.

    Suponemos que en nuestro cuarto de la impresora de la organizacion, tene-
mos 5 impresoreas conectadas. Nuestro manejador del spool asigna un conjunto
de 5 semaforos a el, uno por cada impresora del sistema. Como cada impresora
es capaz de imprimir fisicamente ununico trabajo en un instante, cada uno
de nuestros cinco semaforos de nuestro conjunto se inicializara a un valor de 1
(uno), lo que significa que estan todas en linea, y aceptan trabajos.

    Juan envia que una peticion de impresion al spooler.  El manejador de la
impresion mira los semaforos, y encuentra que el primer semaforo que tiene un

6.4.  IPC EN SISTEMA V                                                    53



valor de uno.  Ante enviar la peticion de Juan al aparato fisico, el manejador
de impresion decrementa el semaforo de la impresora correspondiente con un
valor negativo (-1).  Ahora, el valor de ese semaforo es cero.  En el mundo
de semaforos Sistema V, un valor de cero representa el 100ese semaforo.  En
nuestro ejemplo se no se puede enviar a esa impresora ninguna otra peticion
hasta que sea distinto de cero.

    Cuando el trabajo de Juan ha finalizado, el gestor de impresion incrementa
el varlor del semaforo correspondiente.  Su valor vuelve a ser uno (1), lo que
indica que el recurso vuelve a estar disponible.  Naturalmente,  si los cinco
semaforos tienen valor cero, indica que todas las impresoras estan ocupadas
con peticiones y no se pueden atender mas.

    Aunque este es un ejemplo simple, procure no confundirse con el valor inicial
(1) dado a los semaforos.  En realidad,  cuando se ven como contadores de
recursos, pueden ser iniciados con cualquier valor positivo, y no estan limitados
a valer 0 o 1.  Si las impresoras de nuestro ejemplo fuesen capaces de aceptar
10 trabajos de impresion, habriamos iniciado sus semaforos con el valor 10,
decrementandolo en 1 cada vez que llega un trabajo nuevo y reincrementandolo
al terminar otro. Como descubriremos en este capitulo, el funcionamiento de los
semaforos tiene mucha relacion con el sistema de memoria compartida, actuando
como guardianes para evitar multiples escrituras en la misma zona de memoria.

    Antes de entrar en las llamadas al sistema relacionadas, repasemos varias
estructuras de datos internas usadas en las operaciones con semaforos.



Estructuras de datos internas


Veamos brevemente las estructuras de datos mantenidas por el nucleo para los
conjuntos de semaforos.



Estructura semid_ds del nucleo   Como en las colas de mensajes, el nucleo
mantiene unas estructuras de datos internas especiales por cada conjunto de
semaforos dentro de su espacio de direcciones.   Esta estructura es de tipo
semid_ds y se define en linux/sem.h como sigue:
___________________________________________________________________________________


          /*  Hay  una  estructura  semid_ds  por  cada  juego  de  semaforos  */
          struct  semid_ds  {
                     struct  ipc_perm  sem_perm;         /*  permisos  ..  ver  ipc.h  */
                     time_t             sem_otime;        /*  ultimo  instante  semop  */
                     time_t             sem_ctime;        /*  ultimo  instante  de  cambio  */
                     struct  sem        *sem_base;        /*  puntero  al  primer
                                                                 semaforo  del  array  */
                     struct  wait_queue  *eventn;
                     struct  wait_queue  *eventz;
                     struct  sem_undo   *undo;            /*  deshacer  peticiones
                                                                 del  array*/
                     ushort             sem_nsems;        /*  no.  de  semaforos  del  array  */
          };

54       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



___________________________________________________________________________________

    Como con las colas de mensaje, las operaciones con esta estructura son
ejecutados por llamadas especiales al sistema especial, y no se deben usar di-
rectamente. Aqui tenemos las descripciones de los campos mas interesantes:


sem_perm

      Este es un caso de la estructura ipc_perm, que se define en linux/ipc.h.
      Toma la informacion de los permisos para el conjunto de semaforos, in-
      cluyendo permisos de acceso e informacion sobre el creador del conjunto
      (uid, etc).


sem_otime

      Instante de laultima operacion semop() (un poco mas de esto dentro de
      un momento)


sem_ctime

      Instante delultimo cambio de modo


sem_base

      Puntero al primer semaforo del array (ver siguiente estructura)


sem_undo

      Numero de solicitudes de deshacer en el array (un poco mas dentro de un
      momento)


sem_nsems

      Numero de semaforos en el conjunto (el array)



Estructura sem del nucleo   En la estructura semid_ds, hay un puntero a
la base del array del semaforo. Cada miembro del array es del tipo estructura
sem. Tambien se define en linux/sem.h:
___________________________________________________________________________________



          /*  Una  estructura  por  cada  juego  de  semaforos  */
          struct  sem  {
                     short    sempid;            /*  pid  de  ultima  operacion  */
                     ushort   semval;            /*  valor  actual  */
                     ushort   semncnt;          /*  num.  procesos  esperando
                                                       para  incrementarlo  */
                     ushort   semzcnt;          /*  num.  procesos  esperando
                                                       que  semval  sea  0  */
          };

___________________________________________________________________________________

sem_pid

      El PID (identificador del proceso) que realizo laultima operacion

6.4.  IPC EN SISTEMA V                                                    55



sem_semval

      Valor actual del semaforo


sem_semncnt

      Numero de procesos esperando la disponibilidad del recurso


sem_semzcnt

      Numero de procesos esperando la disponibilidad 100



LLAMADA AL SISTEMA: semget()


Se usa para crear un nuevo conjunto o acceder a uno existente.
___________________________________________________________________________________

   LLAMADA  AL  SISTEMA:  semget();


   PROTOTIPO:  int  semget  (  key_t  key,  int  nsems,  int  semflg  );
     RETORNA:  Identificador  IPC  del  conjunto,  si exito
                 -1  si  error:  errno  =  EACCESS  (permiso  denegado)
                                           EEXIST  (no  puede  crearse  pues  existe  (IPC_EXCL))
                                           EIDRM  (conjunto  marcado  para  borrarse)
                                           ENOENT  (no  existe  el  conjunto  ni  se
                                                     indico  IPC_CREAT)
                                           ENOMEM  (No  hay  memoria  suficiente  para  crear)
                                           ENOSPC  (Limite  de  conjuntos  excedido)
   NOTAS:
___________________________________________________________________________________

    El primer argumento de semget() es el valor clave (en nuestro caso devuelto
por la llamada a ftok()). Este valor clave se compara con los valores clave exis-
tentes en el nucleo para otros conjuntos de semaforos.  Ahora, las operaciones
de apertura o acceso depende del contenido del argumento semflg.


IPC_CREAT

      Crea el juego de semaforos si no existe ya en el nucleo.


IPC_EXCL

      Al usarlo con IPC_CREAT, falla si el conjunto de semaforos existe ya.


    Si  se  usa  IPC_CREAT  solo,  semget(),  bien  devuelve  el  identificador  del
semaforo para un conjunto nuevo creado, o devuelve el identificador para un
conjunto que existe con el mismo valor clave.  Si se usa IPC_EXCL junto con
IPC_CREAT, entonces o se crea un conjunto nuevo, o si el conjunto existe, la lla-
mada falla con -1.IPC_EXCL es inutil por si mismo, pero cuando se combina con
IPC_CREAT, se puede usar como una facilidad garantizar que ningun semaforo
existente se abra accidentalmente para accederlo.
    Como sucede en otros puntos del IPC del Sistema V, puede aplicarse a los
parametros anteriores, un numero octal para dar la mascara de permisos de
acceso de los semaforos. Debe hacerse con una operacion OR binaria.

56       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



   El argumento nsems especifica el numero de semaforos que se deben crear
en un conjunto nuevo.  Este representa el numero de impresores en nuestro
cuarto de impresion ficticio descrito antes. El maximo numero de semaforos en
un conjunto se define en"linux/sem.h" como:


          #define  SEMMSL   32        /*  <=512  max  num  de  semaforos  por  id  */


   Observe que el argumento nsems se ignora si abre explicitamente un con-
junto existente.
   Creemos  ahora  una  funcion  de  cobertura  para  abrir  o  cerrar  juegos  de
semaforos:_________________________________________________________________________


int  abrir_conj_semaforos(  key_t  clave,  int  numsems  )
{
          int       sid;


          if  (  !  numsems  )
                     return(-1);


          if((sid  =  semget(  clave,  numsems,  IPC_CREAT  |  0660  ))  ==  -1)
          {
                     return(-1);
          }


          return(sid);
}
___________________________________________________________________________________
    Vea que se usan explicitamente los permisos 0660.  Esta peque"na funcion
retornara, bien un identificador entero del conjunto de semaforos, o bien un -1
si hubo un error. En el ejemplo del final de esta seccion, observe la utilizacion
del flag IPC_EXCL para determinar si el conjunto de semaforos existe ya o no.



LLAMADA AL SISTEMA: semop()
___________________________________________________________________________________
   LLAMADA  AL  SISTEMA:  semop();
   PROTOTIPO:  int  semop  (  int  semid,  struct  sembuf  *sops,  unsigned  nsops);
     RETURNS:  0  si exito  (todas  las  operaciones  realizadas)
                 -1  si  error:  errno  =  E2BIG  (nsops  mayor  que  max.  numero  de  opers.
                                           permitidas  atomicamente)
                                           EACCESS  (permiso  denegado)
                                           EAGAIN  (IPC_NOWAIT  incluido,  la  operacion  no  termino)
                                           EFAULT  (direccion  no  valida  en  el  parametro  sops)
                                           EIDRM  (el  conj.  de  semaforos  fue  borrado)
                                           EINTR  (Recibida  se~nal  durante  la  espera)
                                           EINVAL  (el  conj.  no  existe,  o  semid  invalido)
                                           ENOMEM  (SEM_UNDO  incluido,  sin  memoria  suficiente  para
                                                     crear  la  estructura  de  retroceso  necesaria)
                                           ERANGE  (valor  del  semaforo  fuera  de  rango)
   NOTAS:

6.4.  IPC EN SISTEMA V                                                    57



___________________________________________________________________________________

    El primer argumento de semget() es el valor clave (en nuestro caso devuelto
por una llamada a semget). El segundo argumento (sops) es un puntero a un
array de operaciones para que se ejecuta en el conjunto de semaforo, mientras
el tercer argumento (nsops) es el numero de operaciones en ese array.
    El argumento sops apunta a un array del tipo sembuf.  Se declara esta
estructura en linux/sem.h como sigue:
___________________________________________________________________________________

          /*  La  llamada  al  sistema  semop  usa  un  array  de  este  tipo  */
          struct  sembuf  {
                     ushort   sem_num;          /*  posicion  en  el  array  */
                     short    sem_op;            /*  operacion  del  semaforo  */
                     short    sem_flg;          /*  flags  de  la  operacion  */
          };
___________________________________________________________________________________

sem_num

      Numero de semaforo sobre el que desea actuar


sem_op

      Operacion a realizar (positiva, negativa o cero)


sem_flg

      Flags (parametros) de la operacion


    Si sem_op es negativo, entonces su valor se resta del valor del semaforo.
Este pone en correlacion con la obtencion de recursos que controla el semaforo

o los monitores de acceso. Si no se especifica IPC_NOWAIT, entonces proceso que
efectua la llamada duerme hasta que los recursos solicitados estan disponible
en el semaforo (otro proceso ha soltado algunos).
    Si sem_op es positivo, entonces su valor se a"nade al semaforo. Este se pone en
correlacion con los recursos devueltos al conjunto de semaforos de la aplicacion.
<Siempre se deben devolver los recursos al conjunto de semaforos cuando ya no
se necesiten mas!
    Finalmente, si sem_op vale cero (0), entonces el proceso que efectua la lla-
mada dormira hasta que el valor del semaforo sea 0. Este pone en correlacion
la espera a un semaforo para obtener un 100ajusta dinamicamente el tama"no
del conjunto de semaforos si obtiene utilizacion plena.
    Para explicar la llamada de semop, volvamos a ver nuestro ejemplo de im-
presion.  Supongamos unaunica una impresora, capaz deunico un trabajo en
un instante. Creamos un conjunto de semaforos conunico semaforo en el (solo
una impresora), e inicializa ese semaforo con un valor de uno (unico un trabajo
en un instante).
    Cada vez que deseemos enviarle un trabajo a esta impresora, primeros ne-
cesitamos asegura que el recurso esta disponible.  Hacemos este para intentar
obtener una unidad del semaforo.  Cargamos un array sembuf para realizar la
operacion:
___________________________________________________________________________________

58       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



          struct  sembuf  sem_lock  =  {  0,  -1,  IPC_NOWAIT  };

___________________________________________________________________________________

    La traduccion de la inicializacion de la anterior estructura indica que un
valor de "-1" se a"nadira al numero del semaforo 0 del conjunto de semaforos.
En otras palabras, se obtendra una unidad de recursos delunico semaforo de
nuestro conjunto (miembro 0).  Se especifica IPC_NOWAIT, asi la llamada
o se produce inmediatamente, o falla si otro trabajo de impresion esta activo
en ese momento.  Aqui hay un ejemplo de como usar esta inicializacion de la
estructura sembuf con la llamada al sistema semop:
___________________________________________________________________________________

          if((semop(sid,  &sem_lock,  1)  ==  -1)
                     perror("semop");

___________________________________________________________________________________

    El tercer argumento (nsops) dice que estamos solo ejecutando una (1) ope-
racion (hay solo una estructura sembuf en nuestra array de operaciones).  El
argumento sid es el identificador IPC para nuestro conjunto de semaforos.
    Cuando nuestro trabajo de impresion ha terminado, debemos devolver los
recursos al conjunto de semaforos, de manera que otros puedan usar la impre-
sora.______________________________________________________________________________


          struct  sembuf  sem_unlock  =  {  0,  1,  IPC_NOWAIT  };

___________________________________________________________________________________

    La traduccion de la estructura anteriormente inicializada indica que un valor
de "1" se agrega a semaforo numero 0 en el conjunto de semaforos.  En otras
palabras, una unidad de recursos se devolvera al conjunto.



LLAMADA AL SISTEMA: semctl()
___________________________________________________________________________________
   LLAMADA  AL  SISTEMA:  semctl();
   PROTOTIPO:  int  semctl  (  int  semid,  int  semnum,  int  cmd,  union  semun  arg  );
     RETURNS:  entero  positivo  si exito
                 -1  si  error:  errno  =  EACCESS  (permiso  denegado)
                                           EFAULT  (direccion  invalida  en  el  argumento  arg)
                                           EIDRM  (el  juego  de  semaforos  fue  borrado)
                                           EINVAL  (el  conj.  no  existe,  o  semid  no  es  valido)
                                           EPERM  (El  EUID  no  tiene  privilegios  para  el  comando
                                           incluido  en  arg)
                                           ERANGE  (Valor  para  semaforo  fuera  de  rango)
   NOTAS:  Realiza  operaciones  de  control  sobre  conjuntos  de  semaforos

___________________________________________________________________________________

    La llamada al sistema semctl se usa para desempe"nar operaciones de control
sobre un conjunto de semaforo. Esta llamada es analoga a la llamada al sistema
msgctl que se usa para operaciones sobre las colas de mensaje. Si usted compara
las listas de argumento de las dos llamadas al sistema,  notara que la lista
para semctl  varia ligeramente con la de msgctl.  La rellamada a semaforos
actualmente implementada semaforos se implementa realmente conjuntos, mas
a entidades simples. Con las operaciones de semaforo operaciones, no solo hace

6.4.  IPC EN SISTEMA V                                                    59



que se necesite pasar la clave IPC, sino que el semaforo destino dentro de el
conjunto tambien.

    Las llamados al sistema utilizan un argumento cmd, para la especificacion
del comando para ser realizado sobre el objeto IPC . La diferencia que resta
esta en el argumento final a ambas llamadas.  En msgctl, el argumento final
representa una copia de las estructuras de datos internos usado por el nucleo.
Recuerde que nosotros usamos esta estructura para recuperar informacion in-
terna sobre una cola de mensaje, asi como tambien para colocar o cambiar
permisos y propietario de la cola.  Con semaforos, se soportan los comandos
operacionales adicionales, asi requieren unos tipos de estructuras de datos mas
complejos como el argumento final. El uso de un tipo union confunde muchos
programadores novatos de los semaforo de forma considerable.  Nosotros estu-
diaremos esta estructura cuidadosamente, en un esfuerzo para impedir cualquier
confusion.

    El argumento cmd representa el comando a ejecutar con el conjunto. Como
puede ver, incluye los conocidos comandos IPC_STAT/IPC_SET, junto a otros
especificos de conjuntos de semaforos:


IPC_STAT

      Obtiene la estructura semid_ds de un conjunto y la guarda en la direccion
      del argumento buf en la union semun.


IPC_SET

      Establece el valor del miembro ipc_perm de la estructura semid_ds de un
      conjunto. Obtiene los valores del argumento buf de la union semun.


IPC_RMID

      Elimina el conjunto de semaforos.


GETALL

      Se usa para obtener los valores de todos los semaforos del conjunto. Los
      valores enteros se almacenan en un array de enteros cortos sin signo,
      apuntado por el miembro array de la union.


GETNCNT

      Devuelve el numero de procesos que esperan recursos.


GETPID

      Retorna el PID del proceso que realizo laultima llamada semop.


GETVAL

      Devuelve el valor de uno de los semaforos del conjunto.


GETZCNT

      Devuelve el numero de procesos que esperan la disponibilidad del 100%
      de recurso.

60       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



SETALL

      Coloca todos los valores de semaforos con una serie de valores contenidos
      en elmiembro array de la union.


SETVAL

      Coloca el valor de un semaforo individual con el miembro val de la union.


   El argumento arg representa un ejemplo de tipo semun.  Esta union parti-
cular se declara en linux/sem.h como se indica a continuacion:
___________________________________________________________________________________


          /*  argumento  para  llamadas  a  semctl  */
          union  semun  {
                     int  val;                     /*  valor  para  SETVAL  */
                     struct  semid_ds  *buf;    /*  buffer  para  IPC_STAT  e  IPC_SET  */
                     ushort  *array;             /*  array  para  GETALL  y  SETALL  */
                     struct  seminfo  *__buf;   /*  buffer  para  IPC_INFO  */
                     void  *__pad;
          };

___________________________________________________________________________________

val

      Se usa con el comando SETVAL, para indicar el valor a poner en el
      semaforo.


buf

      Se usa con los comandos IPC_STAT/IPC_SET. Es como una copia de la
      estructura de datos interna que tiene el nucleo para los semaforos.


array

      Puntero que se usa en los comandos GETALL/SETALL. Debe apuntar a
      una matriz de numeros enteros donde se ponen o recuperan valores de los
      semaforos.


    Los demas argumentos, __buf  y __pad, se usan internamente en el nucleo y
no son de excesiva utilidad para el programador. Ademas son especificos para
el sistema operativo Linux y no se encuentran en otras versiones de UNIX.

    Ya que esta llamada al sistema es de las mas complicadas que hemos visto,
pondremos diversos ejemplos para su uso.

    La siguiente funcion devuelve el valor del semaforo indicado.  Elultimo
argumento de la llamada (la union), es ignorada con el comando GETVAL por
lo que no la incluimos:
___________________________________________________________________________________


int  obtener_sem_val(  int  sid,  int  semnum  )
{
          return(  semctl(sid,  semnum,  GETVAL,  0));
}

6.4.  IPC EN SISTEMA V                                                    61



___________________________________________________________________________________
    Volviendo al ejemplo de las impresoras, asi obtendremos el estado de las
cinco maquinas:
___________________________________________________________________________________


          #define  MAX_IMPR  5


          uso_impresoras()
          {
                     int  x;


                     for(x=0;  x<MAX_IMPR;  x++)
                               printf("Impresora  %d:  %d\n\r",  x,  obtener_sem_val(  sid,  x  ));
          }
___________________________________________________________________________________
    Considerese la siguiente funcion, que se debe usar para iniciar un nuevo
semaforo:__________________________________________________________________________


void  iniciar_semaforo(  int  sid,  int  semnum,  int  initval)
{
          union  semun  semopts;


          semopts.val  =  initval;
          semctl(  sid,  semnum,  SETVAL,  semopts);
}
___________________________________________________________________________________
    Observe que el argumento final de semctl  es una copia de la union, mas
que un puntero a el.  Mientras nosotros estamos en el tema de la union como
argumento, me permito demostrar una equivocacion mas bien comun cuando
usa este llamado de sistema.
    Recordamos del proyecto msgtool que los comandos IPC_STAT e IPC_SET
se usaron para alterar los permisos sobre la cola.  Mientras estos comandos se
soportan, en la implementacion de un semaforo implementacion, su uso es un
poco diferente, como las estructuras de datos internas e recuperan y copian
desde un miembro de la union, mas bien que una entidad simple.  > Puede
encontrar el error en este codigo?
___________________________________________________________________________________


/*  Los  permisos  se  pasan  como  texto  (ejemplo:  "660")  */


void  changemode(int  sid,  char  *mode)
{
          int  rc;
          struct  semid_ds  mysemds;


          /*  Obtener  valores  actuales  */
          if((rc  =  semctl(sid,  0,  IPC_STAT,  semopts))  ==  -1)
          {

62       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



                    perror("semctl");
                    exit(1);
          }


          printf("Antiguos  permisos:  %o\n",  semopts.buf->sem_perm.mode);


          /*  Cambiar  los  permisos  del  semaforo  */
          sscanf(mode,  "%o",  &semopts.buf->sem_perm.mode);


          /*  Actualizar  estructura  de  datos  interna  */
          semctl(sid,  0,  IPC_SET,  semopts);


          printf("Actualizado...\n");
}
___________________________________________________________________________________
    El codigo intenta de hacer una copia local de las estructuras de datos inter-
nas estructura para el conjunto, modifica los permisos, e IPC_SET los devuelve
al nucleo. Sin embargo, la primera llamada a semctl devuelve EFAULT, o direc-
cion erronea para elultimo argumento (<la union!). Ademas, si no hubieramos
verificado los errores de la llamada, nosotros habriamos conseguido un fallo de
memoria. >Por que?
    Recuerde que los comandos IPC_SET/IPC_STAT usan el miembro buf  de
la union, que es un puntero al tipo semid_ds.  <Los punteros, son punteros,
son punteros y son punteros!  El miembro buf  debe indicar alguna ubicacion
valida de almacenamiento para que nuestra funcion trabaje adecuadamente.
Considere_esta_version:____________________________________________________________



void  cambiamodo(int  sid,  char  *mode)
{
          int  rc;
          struct  semid_ds  mysemds;


          /*  Obtener  valores  actuales  de  estructura  interna  */


          /*  !Antes  de  nada  apuntar  a  nuestra  copia  local!  */
          semopts.buf  =  &mysemds;


          /*  !Intentemos  esto  de  nuevo!  */
          if((rc  =  semctl(sid,  0,  IPC_STAT,  semopts))  ==  -1)
          {
                     perror("semctl");
                     exit(1);
          }


          printf("Antiguos  permisos:  %o\n",  semopts.buf->sem_perm.mode);


          /*  Cambiar  permisos  */

6.4.  IPC EN SISTEMA V                                                    63



           sscanf(mode,  "%o",  &semopts.buf->sem_perm.mode);


           /*  Actualizar  estructura  interna  */
           semctl(sid,  0,  IPC_SET,  semopts);


           printf("Actualizado...\n");
}

___________________________________________________________________________________


semtool: Manipulador interactivo de semaforos


Vistazo Rapido   El programa semtool usa la linea de comandos para deter-
minar su comportamiento: es especialmenteutil en los guiones de shell. Incluye
todas las operaciones posibles para un conjunto de semaforos y puede usarse
para controlar recursos compartidos desde los guiones de shell.



Sintaxis de la utilidad



    Creacion de un conjunto de semaforos


semtool  c  (numero  de  semaforos  en  el  conjunto)



    Bloqueo de un semaforo


semtool  b  (numero  de  semaforo  a  bloquear)



    Desbloqueo de un semaforo


semtool  d  (numero  de  semaforo  a  liberar)



    Cambio de los permisos (modo)


semtool  m  (modo)



    Borrado de un conjunto de semaforos


semtool  b

___________________________________________________________________________________
Ejemplos


semtool   c  5
semtool   b
semtool   d
semtool   m  660
semtool   b
___________________________________________________________________________________

64       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



___________________________________________________________________________________
Codigo fuente



/*****************************************************************************
  Parte  de  la  "Guia  Linux  de  Programacion  -  Capitulo  6"
  (C)opyright  1994-1995,  Scott  Burkett
  *****************************************************************************
  MODULO:  semtool.c
  *****************************************************************************
  Utilidad  de  manejo  de  semaforos  del  sistema  IPC  SYSV


  *****************************************************************************/


#include  <stdio.h>
#include  <ctype.h>
#include  <stdlib.h>
#include  <sys/types.h>
#include  <sys/ipc.h>
#include  <sys/sem.h>


#define  SEM_RESOURCE_MAX          1         /*  Valor  inicial  de  todo  semaforo  */


void  opensem(int  *sid,  key_t  key);
void  createsem(int  *sid,  key_t  key,  int  members);
void  locksem(int  sid,  int  member);
void  unlocksem(int  sid,  int  member);
void  removesem(int  sid);
unsigned  short  get_member_count(int  sid);
int  getval(int  sid,  int  member);
void  dispval(int  sid,  int  member);
void  changemode(int  sid,  char  *mode);
void  usage(void);


int  main(int  argc,  char  *argv[])
{
          key_t  key;
          int    semset_id;


          if(argc  ==  1)
                     usage();


          /*  Crear  clave  IPC  unica  */
          key  =  ftok(".",  's');


          switch(tolower(argv[1][0]))
          {
                     case  'c':  if(argc  !=  3)

6.4.  IPC EN SISTEMA V                                                    65



                                         usage();
                                 createsem(&semset_id,  key,   atoi(argv[2]));
                                 break;
                     case  'b':  if(argc  !=  3)
                                         usage();
                                 opensem(&semset_id,  key);
                                 locksem(semset_id,  atoi(argv[2]));
                                 break;
                     case  'd':  if(argc  !=  3)
                                         usage();
                                 opensem(&semset_id,  key);
                                 unlocksem(semset_id,  atoi(argv[2]));
                                 break;
                     case  'b':  opensem(&semset_id,  key);
                                 removesem(semset_id);
                                 break;
                     case  'm':  opensem(&semset_id,  key);
                                 changemode(semset_id,  argv[2]);
                                 break;
                      default:  usage();


           }


           return(0);
}


void  opensem(int  *sid,  key_t  key)
{
           /*  Abrir  (no  crear!)  el  conjunto  de  semaforos  */


           if((*sid  =  semget(key,  0,  0666))  ==  -1)
           {
                     printf("No  existe  el  conjunto  de  semaforos!\n");
                     exit(1);
           }


}


void  createsem(int  *sid,  key_t  key,  int  members)
{
           int  cntr;
           union  semun  semopts;


           if(members  >  SEMMSL)  {
                     printf("Lo  siento:  el  numero  maximo  de  semaforos  es  de:  %d\n",
                               SEMMSL);
                     exit(1);

66       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



          }


          printf("Intentando  crear  un  conjunto  de  %d  miembros\n",
                                        members);


          if((*sid  =  semget(key,  members,  IPC_CREAT|IPC_EXCL|0666))
                              ==  -1)
          {
                    fprintf(stderr,  "El  conjunto  ya  existe!\n");
                    exit(1);
          }


          semopts.val  =  SEM_RESOURCE_MAX;


          /*  Iniciar  todos  los  miembros  (puede  hacerse  con  SETALL)  */
          for(cntr=0;  cntr<members;  cntr++)
                    semctl(*sid,  cntr,  SETVAL,  semopts);
}


void  locksem(int  sid,  int  member)
{
          struct  sembuf  sem_lock={  0,  -1,  IPC_NOWAIT};


          if(  member<0  ||  member>(get_member_count(sid)-1))
          {
                    fprintf(stderr,  "miembro  %d  fuera  de  rango\n",  member);
                    return;
          }


          /*  Intentamos  bloquear  el  conjunto  */
          if(!getval(sid,  member))
          {
                    fprintf(stderr,  "Recursos  del  semaforo  agotados  (no  bloqueo)!\n");
                    exit(1);
          }


          sem_lock.sem_num  =  member;


          if((semop(sid,  &sem_lock,  1))  ==  -1)
          {
                    fprintf(stderr,  "Fallo  en  bloqueo\n");
                    exit(1);
          }
          else
                    printf("Recursos  decrementados  en  1  (bloqueo)\n");


          dispval(sid,  member);

6.4.  IPC EN SISTEMA V                                                    67



}


void  unlocksem(int  sid,  int  member)
{
           struct  sembuf  sem_unlock={  member,  1,  IPC_NOWAIT};
           int  semval;


           if(  member<0  ||  member>(get_member_count(sid)-1))
           {
                     fprintf(stderr,  "miembro  %d  fuera  de  rango\n",  member);
                     return;
           }


           /*  Esta  bloqueado?  */
           semval  =  getval(sid,  member);
           if(semval  ==  SEM_RESOURCE_MAX)  {
                     fprintf(stderr,  "Semaforo  no  bloqueado!\n");
                     exit(1);
           }


           sem_unlock.sem_num  =  member;


           /*  Intentamos  desbloquear  */
           if((semop(sid,  &sem_unlock,  1))  ==  -1)
           {
                     fprintf(stderr,  "Fallo  en  desbloqueo\n");
                     exit(1);
           }
           else
                     printf("Recursos  incrementados  en  1  (desbloqueo)\n");


           dispval(sid,  member);
}


void  removesem(int  sid)
{
           semctl(sid,  0,  IPC_RMID,  0);
           printf("Semaforo  borrado\n");
}


unsigned  short  get_member_count(int  sid)
{
           union  semun  semopts;
           struct  semid_ds  mysemds;


           semopts.buf  =  &mysemds;

68       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



          /*  Devolver  numero  de  miembros  */
          return(semopts.buf->sem_nsems);
}


int  getval(int  sid,  int  member)
{
          int  semval;


          semval  =  semctl(sid,  member,  GETVAL,  0);
          return(semval);
}


void  changemode(int  sid,  char  *mode)
{
          int  rc;
          union  semun  semopts;
          struct  semid_ds  mysemds;


          /*  Obtener  valores  de  la  estructura  interna  */
          semopts.buf  =  &mysemds;


          rc  =  semctl(sid,  0,  IPC_STAT,  semopts);


          if  (rc  ==  -1)  {
                    perror("semctl");
                    exit(1);
          }


          printf("Permisos  antiguos:  %o\n",  semopts.buf->sem_perm.mode);


          /*  Cambiar  los  permisos  */
          sscanf(mode,  "%ho",  &semopts.buf->sem_perm.mode);


          /*  Actualizar  estructura  interna  */
          semctl(sid,  0,  IPC_SET,  semopts);


          printf("Actualizado...\n");


}


void  dispval(int  sid,  int  member)
{
          int  semval;


          semval  =  semctl(sid,  member,  GETVAL,  0);
          printf("El  semval  del  miembro  %d  es  %d\n",  member,  semval);
}

6.4.  IPC EN SISTEMA V                                                    69

void  usage(void)
{
           fprintf(stderr,  "semtool  -  Utilidad  de  manejo  de  semaforos\n");
           fprintf(stderr,  "\nUSAGE:   semtool  (c)rear  <cuantos>\n");
           fprintf(stderr,  "                      (b)loquear  <sem  #>\n");
           fprintf(stderr,  "                      (d)esbloquear  <sem  #>\n");
           fprintf(stderr,  "                      (b)orrar\n");
           fprintf(stderr,  "                      (m)odo  <modo>\n");
           exit(1);
}
___________________________________________________________________________________


semstat: utilidad para semtool


Como regalo final, incluimos el codigo fuente de una utilidad adicional llamada
semstat.  Este programa muestra los valores de cada semaforo del conjunto
creado_con_semtool.________________________________________________________________



/*****************************************************************************
  Parte  de  la  "Guia  Linux  de  Programacion  -  Capitulo  6"
  (C)opyright  1994-1995,  Scott  Burkett
  *****************************************************************************
  MODULO:  semstat.c
  *****************************************************************************
  semstat  muestra  el  estado  de  los  semaforos  manejados  con  semtool
  *****************************************************************************/


#include  <stdio.h>
#include  <stdlib.h>
#include  <sys/types.h>
#include  <sys/ipc.h>
#include  <sys/sem.h>


int  get_sem_count(int  sid);
void  show_sem_usage(int  sid);
int  get_sem_count(int  sid);
void  dispval(int  sid);


int  main(int  argc,  char  *argv[])
{
          key_t  key;
          int    semset_id;


          /*  Obtener  clave  IPC  unica  */
          key  =  ftok(".",  's');


          /*  Abrir  (no  crear!)  el  conjunto  de  semaforos  */

70       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



          if((semset_id  =  semget(key,  1,  0666))  ==  -1)
          {
                    printf("El  conjunto  no  existe\n");
                    exit(1);
          }


          show_sem_usage(semset_id);
          return(0);
}


void  show_sem_usage(int  sid)
{
          int  cntr=0,  maxsems,  semval;


          maxsems  =  get_sem_count(sid);


          while(cntr  <  maxsems)  {
                    semval  =  semctl(sid,  cntr,  GETVAL,  0);
                    printf("Semaforo  #%d:   -->  %d\n",  cntr,  semval);
                    cntr++;
          }
}


int  get_sem_count(int  sid)
{
          int  rc;
          struct  semid_ds  mysemds;
          union  semun  semopts;


          /*  Obtener  valores  de  la  estructura  interna  */
          semopts.buf  =  &mysemds;


          if((rc  =  semctl(sid,  0,  IPC_STAT,  semopts))  ==  -1)  {
                    perror("semctl");
                    exit(1);
          }


          /*  devolver  numero  de  semaforos  del  conjunto  */
          return(semopts.buf->sem_nsems);
}


void  dispval(int  sid)
{
          int  semval;


          semval  =  semctl(sid,  0,  GETVAL,  0);
          printf("semval  vale  %d\n",  semval);

6.4.  IPC EN SISTEMA V                                                    71



}
___________________________________________________________________________________


6.4.4    Memoria Compartida


Conceptos Basicos


La memoria compartida se puede describir mejor como el plano (mapping) de
un area (segmento) de memoria que se combinara y compartira por mas de
un de proceso.  Esta es por mucho la forma mas rapida de IPC, porque no
hay intermediacion (es decir, un tubo, una cola de mensaje, etc). En su lugar,
la informacion se combina directamente en un segmento de memoria, y en el
espacio de direcciones del proceso llamante.  Un segmento puede ser creado
por un proceso, y consecutivamente escrito a y leido por cualquier numero de
procesos.



Estructuras de Datos Internas y de Usuario


Echemos un vistazo a las estructuras de datos que mantiene el nucleo para cada
segmento de memoria compartida.



Estructura shmid_ds del Nucleo   Como con la cola de mensaje y los con-
juntos de semaforos,  el nucleo mantiene unas estructuras de datos internas
especiales para cada segmento compartido de memoria que existe dentro de
su espacio de direcciones.  Esta estructura es de tipo shmid_ds, y se define en
linux/shm.h_como_se_indica_a_continuacion:_________________________________________


          /*  Por  cada  segmento  de  memoria  compartida,  el  nucleo  mantiene
              una  estructura  como  esta  */
          struct  shmid_ds  {
                     struct  ipc_perm  shm_perm;          /*  permisos  operacion  */
                     int       shm_segsz;                   /*  tamanyo  segmento  (bytes)  */
                     time_t   shm_atime;                   /*  instante  ultimo  enlace  */
                     time_t   shm_dtime;                   /*  instante  ult.  desenlace  */
                     time_t   shm_ctime;                   /*  instante  ultimo  cambio  */
                     unsigned  short   shm_cpid;          /*  pid  del  creador  */
                     unsigned  short   shm_lpid;          /*  pid  del  ultimo  operador  */
                     short    shm_nattch;                  /*  num.  de  enlaces  act.  */


                                                              /*  lo  que  sigue  es  privado  */


                     unsigned  short    shm_npages;       /*  tam.  segmento  (paginas)  */
                     unsigned  long    *shm_pages;        /*  array  de  ptr.  a  marcos  ->  SHMMAX  */
                     struct  vm_area_struct  *attaches;  /*  descriptor  de  enlaces  */
          };
___________________________________________________________________________________
    Las operaciones sobre esta estructura son realizadas por una llamada espe-
cial al sistema, y no deberian ser realizadas directamente. Aqui se describen de
los campos mas importantes:

72       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



shm_perm

      Este  es  un  ejemplo  de  la  estructura  ipc_perm,  que  se  define  en
      linux/ipc.h.  Esto tiene la informacion de permisos para el segmen-
      to, incluyendo los permisos de acceso, e informacion sobre el creador del
      segmento (uid, etc).


shm_segsz

      Tama"no del segmento (en bytes).


shm_atime

      Instante delultimo enlace al segmento por parte de algun proceso.


shm_dtime

      Instante delultimo desenlace del segmento por parte de algun proceso.


shm_ctime

      Instante delultimo cambio de esta estructura (cambio de modo, etc).


shm_cpid

      PID del proceso creador.


shm_lpid

      PID delultimo proceso que actuo sobre el segmento.


shm_nattch

      Numero de procesos actualmente enlazados con el segmento.



LLAMADA AL SISTEMA: shmget()


Para crear un nuevo segmento de memoria compartida, o acceder a una exis-
tente, tenemos la llamada al sistema shmget().
___________________________________________________________________________________

   LLAMADA  AL  SISTEMA:  shmget();


   PROTOTIPO:  int  shmget  (  key_t  key,  int  size,  int  shmflg  );
     RETORNA:  si exito,  ident.  de  segmento  de  memoria  compartida
                 -1  si  error:  errno  =  EINVAL  (Tam.  de  segmento  invalido)
                                           EEXIST  (El  segmento  existe,  no  puede  crearse)
                                           EIDRM  (Segmento  borrado  o  marcado  para  borrarse)
                                           ENOENT  (No  existe  el  segmento)
                                           EACCES  (Permiso  denegado)
                                           ENOMEM  (No  hay  memoria  suficiente)
   NOTAS:
___________________________________________________________________________________
    Esta llamada particular deberia parecer casi como vieja conocida a estas
alturas. Es parecido a las correspondientes para las colas de mensaje y conjuntos
de semaforos.

6.4.  IPC EN SISTEMA V                                                    73



    El argumento primero de shmget() es el valor clave (en nuestro caso vuelto
por una llamada a ftok()).  Este valor clave se compara entonces a valores
claves existentes que existen dentro de el nucleo para los otros segmentos com-
partidos de memoria. En esta situacion, las operaciones de apertura o de acceso
dependen de los contenidos del argumento shmflg.


IPC_CREAT

      Crea un segmento si no existe ya en el nucleo.


IPC_EXCL

      Al usarlo con IPC_CREAT, falla si el segmento ya existe.


    Si se usa IPC_CREAT sin nada mas, shmget() retornara, bien el identificador
del segmento recien creado, o bien el de un segmento que existia ya con la
misma clave IPC. Si se a"nade el comando IPC_EXCL, en caso de existir ya el
segmento fallara, y si no se creara.

    De nuevo, puede a"nadirse un modo de acceso en octal, mediante la operacion
OR.

    Preparemos una funcion recubridora para crear o localizar segmentos de
memoria compartida:
___________________________________________________________________________________

int  abrir_segmento(  key_t  clave,  int  tamanyo  )
{
          int       shmid;


          if((shmid  =  shmget(  clave,  tamanyo,  IPC_CREAT  |  0660  ))  ==  -1)
          {
                     return(-1);
          }


          return(shmid);
}

___________________________________________________________________________________

    Observe el uso de los permisos explicitos 0660.  Esta sencilla funcion re-
tornara un entero con el identificador del segmento, o -1 si hay error.  Los
argumentos son, el valor de la clave IPC y el tama"no deseado para el segmento
(en bytes).

    Una vez que un proceso obtiene un identificador de segmento valido, el
siguiente paso es mapear (attach) el segmento en su propio espacio de direc-
ciones.



LLAMADA AL SISTEMA: shmat()
___________________________________________________________________________________
   LLAMADA  AL  SISTEMA:  shmat();


   PROTOTIPO:  int  shmat  (  int  shmid,  char  *shmaddr,  int  shmflg);
     RETORNA:  direccion  de  acceso  al  segmento,  o

74       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



                -1  si  error:  errno  =  EINVAL  (Identificador  o  direccion  invalidos)
                                           ENOMEM  (No  hay  memoria  suficiente  para  ligarse)
                                           EACCES  (Permiso  denegado)
  NOTAS:

___________________________________________________________________________________

    Si el argumento addr es nulo (0), el nucleo intenta encontrar una zona no
mapeada. Es la forma recomendada de hacerlo. Se puede incluir una direccion,
pero es algo que solo suele usarse para facilitar el uso con hardware propietario
o resolver conflictos con otras aplicaciones.  El flag SHM_RND puede pasarse
con un OR logico en el argumento shmflg para forzar una direccion pasada
para ser pagina (se redondea al tama"no mas cercano de pagina).

    Ademas, si se hace OR con el flag SHM_RDONLY y con el argumento de
banderas, entonces el segmento compartido de memoria se mapea, pero marcado
como solo lectura.

    Esta llamada es quizas la mas simple de usar.  Considere esta funcion de
envoltura, que se pasa un identificador IPC valido para un segmento, y devuelve
la direccion a la que el segmento esta enlazado:
___________________________________________________________________________________



char  *ligar_segmento(  int  shmid  )
{
          return(shmat(shmid,  0,  0));
}

___________________________________________________________________________________

    Una vez un segmento ha sido adecuadamente adjuntado, y un proceso tiene
un puntero al comienzo del segmento, la lectura y la escritura en el segmento
llegar a ser tan facil como simplemente referenciar el puntero. <Tenga cuidado
de no perder el valor del puntero original!  Si esto sucede, no habra ninguna
manera de acceder a la base (comienzo) del segmento.



LLAMADA AL SISTEMA: shmctl()
___________________________________________________________________________________
   LLAMADA  AL  SISTEMA:  shmctl();
   PROTOTYPE:  int  shmctl  (  int  shmqid,  int  cmd,  struct  shmid_ds  *buf  );
     RETURNS:  0  si exito
                 -1  si  error:  errno  =  EACCES  (No  hay  permiso  de  lectura  y  cmd  es  IPC_STAT)
                                           EFAULT  (Se  ha  suministrado  una  direccion  invalida
                                           para  los  comandos  IPC_SET  e  IPC_STAT)
                                           EIDRM   (El  segmento  fue  borrado  durante  esta  operacion)
                                           EINVAL  (shmqid  invalido)
                                           EPERM   (Se  ha  intentado,  sin  permiso  de  escritura,
                                                     el  comando  IPC_SET  o  IPC_RMID)
        NOTAS:

___________________________________________________________________________________

    Esta llamada particular se usa tras la llamada msgctl solicitando colas de
mensaje. En vista de este hecho, no se discutira en detalle demasiado. Los que
valores validos de comando son:

6.4.  IPC EN SISTEMA V                                                    75



IPC_STAT

      Obtiene la estructura shmid_ds de un segmento y la almacena en la direc-
      cion del argumento buf.


IPC_SET

      Ajusta el valor del miembro ipc_perm de la estructura, tomando el valor
      del argumento buf.


IPC_RMID

      Marca un segmento para borrarse.



    El comado IPC_RMID no quita realmente un segmento del nucleo.  Mas
bien, marca el segmento para eliminacion. La eliminacion real del mismo ocurre
cuando elultimo proceso actualmente adjunto al segmento termina su relacion
con el. Por supuesto, si ningun proceso esta actualmente asociado al segmento,
la eliminacion es inmediata.

    Para separar adecuadamente un segmento compartido de memoria, un pro-
ceso invoca la llamada al sistema shmdt.



LLAMADA AL SISTEMA: shmdt()
___________________________________________________________________________________
   LLAMADA  AL  SISTEMA:  shmdt();


   PROTOTIPO:  int  shmdt  (  char  *shmaddr  );
     RETURNS:  -1  si  error:  errno  =  EINVAL  (Dir.  de  enlace  invalida)

___________________________________________________________________________________

    Cuando un segmento compartido de memoria no se necesita mas por un pro-
ceso, se debe separar con una llamado al sistema.  Como mencionamos antes,
esto no es lo mismo que eliminar un segmento desde el nucleo! Despues de sepa-
rar con exito, el miembro shm_nattch de la estructura shmid_ds se decrementa
en uno. Cuando este valor alcanza el cero (0), el nucleo quitara fisicamente el
segmento.



shmtool: Manipulador de segmentos de memoria compartida


Vistazo rapido   Nuestro ejemplo final de objetos Sistema V IPC seran las
shmtool, que es una herraminenta de linea de comando para crear, leer, escri-
bir, y borrar segmentos compartidos de memoria.  Una vez mas, como en los
ejemplos previos, el segmento se crea durante cualquier operacion, si no existia
anterioramente.



Sintaxis del comando



    Escribir cadenas en el segmento


shmtool  e  "text"

76       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



   Leer cadenas del segmento


shmtool  l



   Cambiar permisos (modo)


shmtool  m  (mode)



   Borrado del segmento


shmtool  b

___________________________________________________________________________________
Ejemplos


shmtool   e    prueba
shmtool   e    "Esto  es  una  prueba"
shmtool   l
shmtool   b
shmtool   m    660
___________________________________________________________________________________

___________________________________________________________________________________
Codigo Fuente


#include  <stdio.h>
#include  <sys/types.h>
#include  <sys/ipc.h>
#include  <sys/shm.h>


#define  SEGSIZE  100


main(int  argc,  char  *argv[])
{
          key_t  key;
          int    shmid,  cntr;
          char   *segptr;


          if(argc  ==  1)
                     usage();


          /*  Obtener  clave  IPC  */
          key  =  ftok(".",  'S');


          /*  Abrir  (crear  si  es  necesario)  el  segmento  de  memoria  compartida  */
          if((shmid  =  shmget(key,  SEGSIZE,  IPC_CREAT|IPC_EXCL|0666))  ==  -1)
          {
                     printf("El  segmento  existe  -  abriendo  como  cliente\n");


                     /*  El  segmento  existe  -  abrir  como  cliente  */
                     if((shmid  =  shmget(key,  SEGSIZE,  0))  ==  -1)

6.4.  IPC EN SISTEMA V                                                    77



                     {
                               perror("shmget");
                               exit(1);
                     }
           }
           else
           {
                     printf("Creando  nuevo  segmento\n");
           }


           /*  Ligar  el  proceso  actual  al  segmento  */
           if((segptr  =  shmat(shmid,  0,  0))  ==  -1)
           {
                     perror("shmat");
                     exit(1);
           }


           switch(tolower(argv[1][0]))
           {
                     case  'e':  writeshm(shmid,  segptr,  argv[2]);
                                 break;
                     case  'l':  readshm(shmid,  segptr);
                                 break;
                     case  'b':  removeshm(shmid);
                                 break;
                     case  'm':  changemode(shmid,  argv[2]);
                                 break;
                      default:  usage();


           }
}


writeshm(int  shmid,  char  *segptr,  char  *text)
{
           strcpy(segptr,  text);
           printf("Hecho...\n");
}


readshm(int  shmid,  char  *segptr)
{
           printf("valor  de  segptr:  %s\n",  segptr);
}


removeshm(int  shmid)
{
           shmctl(shmid,  IPC_RMID,  0);
           printf("Segmento  marcado  para  borrado\n");

78       CAPITULO 6.  COMUNICACION ENTRE PROCESOS EN LINUX



}


changemode(int  shmid,  char  *mode)
{
          struct  shmid_ds  myshmds;


          /*  Obtener  valor  actual  de  la  estructura  de  datos  interna  */
          shmctl(shmid,  IPC_STAT,  &myshmds);


          /*  Mostrar  antiguos  permisos  */
          printf("Antiguos  permisos:  %o\n",  myshmds.shm_perm.mode);


          /*  Convertir  y  cargar  el  modo  */
          sscanf(mode,  "%o",  &myshmds.shm_perm.mode);


          /*  Actualizar  el  modo  */
          shmctl(shmid,  IPC_SET,  &myshmds);


          printf("Nuevos  permisos  :  %o\n",  myshmds.shm_perm.mode);
}


usage()
{
          fprintf(stderr,  "shmtool  -  Utilidad  para  usar  memoria  compartida\n");
          fprintf(stderr,  "\nUSAGE:   shmtool  (e)scribir  <texto>\n");
          fprintf(stderr,  "                     (l)eer\n");
          fprintf(stderr,  "                     (b)orrar\n");
          fprintf(stderr,  "                     (m)odo  <modo  en  octal>\n");
          exit(1);
}
___________________________________________________________________________________
    Sven Goldt Guia Linux de Programacion

Capitulo  7


Programacion  del  Sonido


Un PC tiene por lo menos un dispositivo de sonido:  el altavoz interno.  Pero,
usted puede comprar tambien una tarjeta de sonido para insertar en su PC para
disponer de un dispositivo de sonido mas sofisticado. Mire Linux Sound User's
Guide o Sound-HOWTO - HOWTO para comprobar las tarjetas soportadas.
7.1      Programacion del altavoz interno


Lo crea o no, el altavoz de su PC es parte de la consola de Linux y por tanto un
dispositivo de caracter. Por tanto, para manipularlo usaremos llamadas ioctl().
Para el altavoz interno tenemos dos comandos:



   1. KDMKTONE

      Genera un tono durante un tiempo especificado.

      Ejemplo: ioctl  (fd,  KDMKTONE,(long)  argumento).


   2. KIOCSOUND

      Genera un tono sin fin, o para otro que suena actualmente.

      Ejemplo: ioctl(fd,KIOCSOUND,(int)  tono).



    El argumento consiste en un valor de tono en su parte baja y la duracion
en la parte alta.  El valor de tono no es la frecuencia.  El temporizador del
PC,el 8254, se cronometra a 1.19 MHz y por tanto es 1190000/frecuencia.  La
duracion se mide en ticks de cronometro.  Las dos llamadas a ioctl vuelven
inmediatamente y por consiguiente puede producir pitidos largos sin bloquear
el programa mientras.
El comando KDMKTONE debe usarse para producir se"nales de aviso ya que
no tiene que preocuparse de parar el tono.
El comando KIOCSOUND puede usarse para tocar canciones tal como se de-
muestra en el programa de ejemplo splay (por favor, envieme mas ficheros
.sng). Para parar el tono hay que usar el valor 0 en el tono.


                                         79

80                          CAPITULO 7.  PROGRAMACION DEL SONIDO



7.2     Programacion de una Tarjeta de sonido


Como programador, le resultara importante saber si el sistema sobre el que
actua tiene una tarjeta de sonido conectada.  Para ello puede intentar abrir
/dev/sndstat.  Si falla y sale ENODEV en el valor de errno, es porque no
hay ningun manejador de sonido activo.  El mismo resultado puede obtenerse
chequeando /dev/dsp, siempre que no sea un enlace al manejador pcsnd en
cuyo caso la llamada open() no fallaria.
   Si quiere intentarlo a nivel de hardware, debera conocer alguna combinacion
de llamadas outb() e inb() para detectar la tarjeta que esta buscando.
   Utilizando el manejador de sonido en los programas, tiene la ventaja de
que funcionara igual en otros sistemas 386, ya que la gente inteligente decidira
usar el mismo controlador en Linux, isc, FreeBSD y otras versiones de Unix
de 386. Esto ayudara a transportar programas con sonido entre Linux y otras
arquitecturas.  Una tarjeta de sonido no es parte de la consola Linux, sino un
dispositivo especial. En general ofrecera las siguientes prestaciones:


   o  Muestreo digital, en entrada y salida


   o  Modulacion de frecuencia, en salida


   o  Interfaz MIDI


   Cada una de estas caracteristicas tienen su propia interfaz con el controla-
dor. Para el muestreo digital se usa /dev/dsp. Para la modulacion de frecuencia
se usa /dev/sequencer, y para la interfaz MIDI se usa /dev/midi. Los ajustes
de sonido (tal como volumen o bajos), pueden controlarse mediante la interfaz
/dev/mixer. Por compatibilidad se incluye tambien un dispositivo /dev/audio,
capaz que reproducir datos de sonido SUN  -law, que los mapea al dispositivo
de muestreo digital.
   Si  supuso  la  utilizacion  de  ioctl()  para  manipular  dispositivos  de  so-
nido,  esta  en  lo  cierto.    Las  peticiones  de  esta  clase  se  definen  en  <
linux=soundcard.h > y comienzan con SNDCTL_.


   Puesto que no tengo una tarjeta de sonido, alguien con mas cono-
cimientos deberia continuar este capitulo
   Sven van der Meer v0.3.3, 19 Jan 1995

Capitulo  8


Graficos  en  modo  caracter


Este capitulo se dedica a las entradas y salidas de pantalla que no se basan
en pixels, sino en caracteres.  Cuando decimos caracter, queremos decir una
composicion de pixels que pueden cambiarse dependiendo de un conjunto de
caracteres.  Su tarjeta grafica ya dispone de uno o mas charsets y opera en
modo texto por defecto, porque el texto puede procesarse mucho mas rapido
que un grafico de pixel.  Se pueden hacer mas cosas con los terminales que
simplemente mostrar texto.  Describire como usar los aspectos especiales que
su terminal linux , especialmente los que ofrece la consola el linux.



    o printf, sprintf, fprintf, scanf, sscanf, fscanf
      Con estas funciones de libc podra realizar salida con formato sobre st-
      dout (la salida estandar), stderr (la salida de errores estandar) y otros
      streams definidos como FILE  *stream (ficheros, por ejemplo). La funcion
      scanf(...) proporciona un mecanismo similar para entradas con formato,
      desde stdin (la entrada estandar).


    o termcap
      La base de datos termcap (CAPacidades de TERMinal) es un conjunto de
      entradas de descripcion de terminales en el archivo ASCII /etc/termcap.
      Aqui puede encontrar la informacion sobre como mostrar caracteres espe-
      ciales, como realizar operaciones (borrar, insertar caracteres o lineas, etc)
      y como inicializar un terminal. La base de datos se usa, por ejemplo, por
      el editor vi. Hay funciones de biblioteca de vista para leer y usar las capa-
      cidades terminales (termcap(3x)). Con esta base de datos, los programas
      pueden trabajar con una variedad de terminales con el mismo codigo. El
      uso de la biblioteca de funciones termcap y la base de datos proporciona
      solo acceso a bajo nivel al terminal. El cambio de los atributos o colores
      o atributos, y la optimizacion debe ser hecha por el mismo programador.


    o base de datos terminfo
      La base de datos terminfo (INFOrmacion de TERMinales) se basa en la
      base de datos termcap y tambien describe las capacidades de las termi-
      nales, pero sobre un nivel mas alto que termcap.  Usando terminfo, el
      programa puede cambiar facilmente los atributos, usar teclas especiales


                                         81

82                      CAPITULO 8.  GRAFICOS EN MODO CARACTER



      tales como teclas de funcion y mas.  La base de datos puede encontrarse
      en /usr/lib/terminfo/[A-z,0-9]*. Cada archivo describe un de terminal.


   o  curses
      Terminfo es una base buena para usar en el manejo de un terminal en
      un programa.  La biblioteca (BSD -)CURSES da acceso a alto nivel al
      terminal y se base en la base de datos terminfo. Curses le permite abrir y
      manipular ventanas sobre la pantalla, proporciona un conjunto completo
      de funciones de entrada y salida, y puede alterar atributos de video de
      una manera independiente del terminal sobre mas de 150 terminales. La
      biblioteca de curses puede encontrarse en /usr/lib/libcurses.a. Esta es el
      la version BSD curses.


   o  ncurses
      Ncurses es la siguiente mejora.  La version 1.8.6debe ser compatible con
      curses de AT&T como se define en SYSVR4 y tiene algunas extensiones
      tales como manipulacion de color, optimizacion especial para el salida,
      optimizaciones especificas de terminales, y mas.  Se ha probado sobre
      muchos sistemas tales como SUN-OS, HP y Linux.  Yo recomiendo usar
      ncurses en vez de las otras. Sobre Unix SYSV de sistemas (tal como Sun
      Solaris) deber existir una biblioteca de curses con la misma funcionalidad
      que ncurses (realmente las curses de solaris tienen algunas funciones mas
      y soporte de raton).


   En las secciones siguientes describire como usar los diferentes paquetes di-
ferentes para acceder a un terminal.  Con Linux tenemos la version GNU de
termcap y nosotros podemos usar ncurses en vez de curses.



8.1     Funciones E/S en la libc


8.1.1    Salida con Formato


Las funciones del grupo printf(...)  proporciona una salida formateada y per-
mite la conversion de los argumentos.


   o  int  fprintf(FILE  *stream,  const  char  *formato,  ...),
      transformara la salida (argumentos para rellenar en ...)  y lo escribira
      en un stream.  El formato definido en formato se escribe tambien.  La
      funcion devuelve el numero de caracteres escritos o un valor negativo en
      caso de error.

      El formato contiene dos tipos de objetos:


        1. caracteres normales para la salida

        2. informacion de como transformar o dar formato a los argumentos


      La informacion del formato debe comenzar con el simbolo %, seguido de
      valores apropiados para el formato y de un caracter para la traduccion
      (para imprimir el propio signo % usaremos el comando %%). Los posibles
      valores para el formato son:

8.1.  FUNCIONES E/S EN LA LIBC                                         83



         -  Flags

              * -
                El argumento formateado se imprimira en el margen izquierdo
                (por defecto va en el margen derecho del campo para el argu-
                mento).

              * +
                Cada numero sera impreso con su signo,  por ejemplo +12 o
                -2.32.

         -  Blanco
            Cuando el primer caracter no es un signo, se insertara un blanco.

         -  0
            Para transformaciones numericas la anchura del campo se rellenara
            con ceros a la izquierda.

         -  #
            Salida alternativa dependiendo de las transformaciones para los ar-
            gumentos

              * Para o el primer numero es un 0.

              * Para x o X se imprimira 0x o 0X delante del argumento.

              * Para e, E, f o F la salida tiene punto decimal.

              * Para g o G se imprimen ceros al final del argumento.

         -  Un numero para la amplitud minima del campo.
            El argumento transformado se imprime en un campo que, al menos,
            es tan grande como el mismo argumento. Para numeros, puede hacer
            la anchura del campo mas grande.  Si el argumento formateado es
            mas peque"no, entonces la anchura del campo se rellenara con ceros
            o blancos.

         -  Un punto separa la anchura del campo de la precision.

         -  Un numero para la precision.


      Valores posibles para la transformacion estan en la tabla 8.1 en la pagina
      84.


    o int  printf(const  char  *formato,  ...)
      Similar a fprintf(stdout, ...).


    o int  sprintf(char  *s,  const  char  *formato,  ...)
      Similar a printf(...), con la salvedad de que la salida es escrita en la
      cadena apuntada por el puntero s (terminada en \0).

      (Nota: Debe haberse reservado memoria suficiente para s.)


    o vprintf(const  char  *formato,  va_list  arg)
      vfprintf(FILE  *stream,  const  char  *formato,  va_list  arg)
      vsprintf(char  *s,  const  char  *formato,  va_list  arg)
      Funciones similares a las anteriores, aunque ahora la lista de argumentos
      se introduce en arg.

84                      CAPITULO 8.  GRAFICOS EN MODO CARACTER
                  Tabla 8.1: Libc - transformaciones en printf


     _Caracter__|__Formateado_a_______________________________________________

           d,i     |entero con signo, decimal
           o      | entero sin signo, octal, sin ceros a la izquierda
          x,X     | entero sin signo, hexadecimal sin cabecera 0x
           u      | entero sin signo, decimal
           c      | entero (sin signo), como caracter
           s      | char * hasta el \0
           f      | coma flotante (double), como [-]mmm.ddd
          e,E     | coma flotante (double) como [-]m.dddddde xx
          g,G     | coma flotante (double) usando %e o %f si es necesario
           p      | void *
           n      | int *
           %      | %


8.1.2    Entrada con Formato


Igual que usamos printf(...) para salidas con formato, tambien podemos usar
scanf(...) para entradas con formato.



   o  int  fscanf(FILE  *stream,  const  char  *formato,  ...)



      fscanf(...)  lee de un stream y transformara la entrada con las reglas
      definidas en el formato.   El resultado se situa en el argumento dado
      por ....  (Observe que los argumentos deben ser punteros).  La lectu-
      ra termina cuando no hay mas reglas de transformacion en el formato.
      fscanf(...)  devolvera EOF cuando la primera transformacion alcance el
      final del archivo u ocurra algun error. En otro caso devolvera el numero
      de argumentos transformados.

      El formato puede incluir reglas para dar formato a los caracteres de en-
      trada (vea la tabla 8.2 en la pagina 85).  Tambien puede incluir:  can
      include rules on how to format the input arguments


        -  Espacios o tabuladores, que son ignorados.

        -  Cualquier caracter normal (salvo %). Los caracteres deben estar en
           la entrada, en la misma posicion.

        -  Reglas de transformacion, precedidas de un %, con el caracter op-
           cional * (esto permitira a fscanf(...) asignarlo a un argumento), un
           numero opcional, un caracter opcional h, l o L (para la longitud del
           objetivo) y el caracter de transformacion.


   o  int  scanf(const  char  *formato,  ...)
      Equivalente a fscanf(stdin,...).

8.2.  LA LIBRERIA TERMCAP                                              85
                   Tabla 8.2: Libc - transformaciones en scanf


   _Caracter__|__Entrada_-_Tipo_del_argumento____________________________________

         d      | entero decimal - int *
          i      |entero - int * (la entrada puede ser octal o hexadecimal)
         o      | entero octal - int * (con 0 a la izquierda opcional)
         u      | decimal, sin signo - unsigned int *
         x      | entero hexadecimal - int * (con 0x a la izquierda opcional)
         c      | uno o mas caracteres - char * (sin el \0)
         s      | caracteres (sin separadores) - char * (con el \0)
       e,f,gf    |coma flotante - float * (ej: [-]m.dddddde xx)
         p      | puntero - void *
         n      | numero de argumentos transformados - int *
        [...]     |caracteres de la entrada - char *
        [^...]    e|xcluir esos caracteres - char *
   ______%______|_%______________________________________________________________

     h puede ir antes de d,i,n,o,u y x, cuando el puntero es tipo short
     l puede ir antes de d,i,n,o,u y x, cuando el puntero es long
     l puede ir antes de e,f y g cuando el puntero es double
     L puede ir antes de e,f y g cuando el puntero es long double

    o int  sscanf(char  *str,  const  char  *format,  ...)
      Similar a scanf(...), aunque ahora la entrada se lee de la cadena str.

8.2      La Libreria Termcap


8.2.1     Introduccion


La libreria Termcap es una API (Interfaz de Programacion de Aplicacion) con
la base de datos termcap que se encuentra en /etc/termcap/. Las funciones de
esta libreria permiten realizar las siguientes acciones:


    o Obtener descripcion del terminal actual: tgetent(...).


    o Buscar  la  descripcion  para  informacion:  tgetnum(...),  tgetflag(...),
      tgetstr(...).


    o Codificar parametros numericos en la forma de un terminal especifico:
      tparam(...), tgoto(...).


    o Calcular y realizar rellenos tputs(...).


    Los progamas que usan la biblioteca termcap deben incluir termcap.h y
deben ser enlazados con libtermcap de esta forma:


           gcc [opciones] ficheros -ltermcap

86                      CAPITULO 8.  GRAFICOS EN MODO CARACTER



   Las funciones termcap son rutinas independientes del terminal, pero solo
dan al programador acceso a bajo nivel. Para un manejo de alto nivel tenemos
que usar curses o ncurses.



8.2.2    Encontrar la descripcion del terminal


   o  int  tgetent(void  *buffer,  const  char  *tipoterm)
      En el sistema operativo Linux, el nombre de la clase de terminal actual
      se encuentra en la variable de entorno TERM. Por tanto, el argumento
      tipoterm lo obtendremos mediante la funcion getenv(3).

      Cuando usamos la version termcap de GNU (lo habitual bajo Linux), no
      es necesario reservar memoria para el buffer. En otras implementaciones
      habra que reservar 2048 bytes (realmente son 1024, pero el tama"no fue
      doblado).

      tgetent(...)  devuelve 1 cuando hay exito, y 0 si no se encuentra infor-
      macion para ese terminal en la base de datos.  Otros errores devolveran
      diferentes valores.

      El  siguiente  ejemplo  nos  ayudara  a  ver  como  se  usa  la  funcion  tge-
      tent(...):


                #define buffer 0
                char *tipoterm=getenv("TERM");
                int ok;


                ok=tgetent(buffer,tipoterm);
                if(ok==1)
                  /* todo va bien, se ha encontrado la informacion */
                else if(ok==0)
                 /* algo va mal con TERM
                  * comprobar tipoterm y luego la propia base de datos
                  */
                else
                 /* este caso corresponde a un error fatal */


      Por defecto, la base de datos se encuentra en /etc/termcap/.  Si exis-
      te la variable de entorno TERMCAP, por ejemplo con el valor $HO-
      ME/mytermcap, las funciones de la libreria buscaran la base de datos
      en ese nuevo directorio. Sin barras iniciales en TERMCAP, el valor defi-
      nido se usara como nombre y descripcion del terminal.



8.2.3    Lectura de una descripcion de terminal


Cada parte de la informacion se llama capacidad, cada capacidad, es un codigo
de dos letras, y cada codigo de dos letras viene seguido de por el valor de la
capacidad. Los tipos posibles son:


   o  Numerico: Por ejemplo, co - numero de columnas


   o  Booleano o Flag: Por ejemplo, hc - terminal hardcopy

8.2.  LA LIBRERIA TERMCAP                                              87



    o Cadena: Por ejemplo, st - set tab stop


    Cada  capacidad  esta  asociada  con  un  valor  individual.   (co  es  siempre
numerico, hc es siempre un flag y st es siempre un string).Hay tres tipos diferen-
tes de valores, y por tanto hay tres funciones para interrogarlos. char  *nombre
es el codigo de dos letras para la capacidad.


    o int  tgetnum(char  *nombre)
      Obtiene el valor de una capacidad que es numerica, tal como co.  tget-
      num(...)  devuelve el valor numerico si la capacidad esta disponible, en
      otro caso 1. (Observe que el valor devuelto no es negativo).


    o int  tgetflag(char  *nombre)
      Obtiene el valor de una capacidad que es boolean (o flag). Devuelve 1 si
      la badera (flag) esta presente, 0 en otro caso.


    o char  *tgetstr(char  *nombre,  char  **area)
      Obtiene el valor de una capacidad que es un string. Devuelve un puntero
      al string o NULL si no esta presente. En la version GNU, si area es NULL,
      termcap ubicara memoria para el. Termcap no hara mas referencia a ese
      puntero, de forma que no olvide liberar el nombre antes de terminar el
      programa.  Este metodo es preferido, porque no tiene que saber cuanto
      espacio se necesita para el puntero, asi que deje a termcap hacerlo por vd.


           char *clstr, *cmstr;
           int    lines,   cols;


           void term_caps()
           {
           char *tmp;


            clstr=tgetstr("cl",0); /* limpiar pantalla */
            cmstr=tgetstr("cm",0); /* mover y,x      */


            lines=tgetnum("li"); /* lineas del terminal    */
            cols=tgetnum("co");   /* columnas del terminal */


            tmp=tgetstr("pc",0); /* caracter separador */


            PC=tmp ? *tmp : 0;
            BC=tgetstr("le",0); /* cursor un caracter a la izquierda */
            UP=tgetstr("up",0); /* cursor arriba una linea    */
           }



8.2.4     Capacidades de Termcap


Capacidades Booleanas

5i  La impresora no hara eco en la pantalla
am  Margenes automaticos
bs  Control-H (8 dec.) realiza un backspace

88                      CAPITULO 8.  GRAFICOS EN MODO CARACTER



bw  Backspace al margen izquierdo vuelve la margen derecho e la linea superior
da  Display retenido sobre la pantalla
db  Display retenido bajo la pantalla
eo  Un espacio borra todos los caracteres de la posicion del cursor
es  Secuencias de Escape y caracteres especiales funcionan en la linea de estado
gn  Dispositivo Generico
hc  Esto es un terminal de copia fisica (hardcopy terminal)
HC  El cursor es dificil de ver cuando no esta en la linea de abajo
hs  Tiene linea de estado
hz  "Hazel tine bug", el terminal no puede imprimir caracteres con tilde
in  Terminal inserta nulos, no espacios, para rellenar blancos
km  Terminal tiene tecla "meta" (alt)
mi  Los movimientos del cursor funcionan en modo insercion
ms  Los movimientos del cursor funcionan en modo standout/subrayado
NP  Sin caracter de "padding"
NR  "ti" no invierte "te"
nx  No hay "padding", debe usarse XON/XOFF
os  Terminal can overstrike
ul  Terminal underlines although it can not overstrike
xb  f1 envi ESCAPE, f2 envi ^C
xn  Newline/wraparound glitch
xo  El terminal usa protocolo xon/xoff
xs  Text typed over standout text will be displayed in standout
xt  Teleray glitch, destructive tabs and odd standout mode
Capacidades Numericas


co  Numero de columnas                    lh  Alto de los 'soft labels'
dB  Retardo (ms) para el retroceso        lm  Lineas de memoria
    en terminales de copia fisica         lw  Ancho de los 'soft labels'
dC  Retardo (ms) para el retorno de       li  Numero de lineas
    carro en terminales de copia fisica   Nl  Numero de 'soft labels'
dF  Retardo (ms) para alim. pagina        pb  Minimo ratio en baudios que
    en terminales de copia fisica             necesita 'padding'
dN  Retardo (ms) para fin de linea        sg  Standout glitch
    en terminales de copia fisica         ug  Underline glitch
dT  Retardo (ms) para parada de tabulacionvt  Numero de terminal virtual
    en terminales de copia fisica         ws  Ancho de linea de estado si
dV  Retardo (ms) para parada de tabulacion    difiere del ancho de pantalla
    vertical en terminales de copia fisica
it  Diferencia entre posiciones de tabulacion


Capacidades con Cadenas


!1  tecla de salvar con shift             %0  tecla de rehacer
!2  tecla de suspension con shift         %1  tecla de ayuda
!3  tecla de deshacer con shift           %2  tecla de seleccion
#1  tecla de ayuda con shift              %3  tecla de mensaje
#2  tecla de inicio con shift             %4  tecla de mover
#3  tecla de entrada con shift            %5  tecla de siguiente objeto
#4  tecla con shift de cursor izquierda   %6  tecla de abrir

8.2.  LA LIBRERIA TERMCAP                                              89



%7  tecla de opciones                      cb  Limpiar desde comienzo de linea
%8  tecla de objeto anterior                   hasta el cursor
%9  tecla de imprimir                      cc  Carcter comodin de comando
%a  tecla de mensajes con shift            cd  Limpiar hasta final de pantalla
%b  tecla de mover con shift               ce  Limpiar hasta final de linea
%c  tecla de siguiente, con shift          ch  Mover cursor horizontalmente hasta la
%d  tecla de opciones con shift            columna %1
%e  tecla de anterior, con shift           cl  Limpiar pantalla y devolver cursor al principio
%f  tecla de imprimir, con shift           cm  Mover cursor a la fila %1 y
%g  tecla de rehacer, con shift            columna %2 (de la pantalla)
%h  tecla de reemplazar, con shift         CM  Mover cursor a la fila %1 y
%i  tecla de cursor dcha. con shift        la columna %2 (de la memoria)
%j  tecla continuar con shift              cr  Retorno de carro
&0  tecla cancelar con shift               cs  Mover region de linea %1 a la %2
&1  tecla de referencia                    ct  Limpiar tabuladores
&2  tecla de refrescar                     cv  Mover cursor a la
&3  tecla de reemplazar                        linea %1
&4  tecla de reinicio                      dc  Borrar un caracter
&5  tecla de continuar                     DC  Borrar %1 caracteres
&6  tecla de salvar                        dl  Borrar una linea
&7  tecla de suspension                    DL  Borrar %1 lineas
&8  tecla deshacer                         dm  Inicio del modo borrado
&9  tecla de inicio con shift              do  Bajar cursor una linea
*0  tecla de buscar con shift              DO  Bajar cursor #1 lineas
*1  tecla de comando con shift             ds  Desactivar linea de estado
*2  tecla de copiar con shift              eA  Activar juego de caracteres alternativo
*3  tecla de crear con shift               ec  Borrar %1 caracteres desde el cursor
*4  caracter de borrado con shift          ed  Fin del modo borrado
*5  borrar linea con shift                 ei  Fin del modo insercion
*6  tecla de seleccion                     ff  Caracter de salto de pgina en
*7  tecla de fin con shift                     terminales de copia fisica
*8  limpiar linea con shift                fs  Devolver caracter a posicion antes
*9  tecla de salida con shift                  de ir a la linea de estado
0   tecla de buscar                        F1  Cadena enviada por tecla f11
1   tecla de inicio                        F2  Cadena enviada por tecla f12
2   tecla de cancelar                      F3  Cadena enviada por tecla f13
3   tecla de cerrar                            ... ...
4   tecla de comando                       F9  Cadena enviada por tecla f19
5   tecla de copiar                        FA  Cadena enviada por tecla f20
6   tecla de crear                         FB  Cadena enviada por tecla f21
7   tecla de fin                               ... ...
8   tecla de entrar/enviar                 FZ  Cadena enviada por tecla f45
9   tecla de salir                         Fa  Cadena enviada por tecla f46
al  Insertar una linea                     Fb  Cadena enviada por tecla f47
AL  Insertar %1 lineas                         ... ...
ac  Pairs of block graphic characters to   Fr  Cadena enviada por tecla f63
     map alternate character set           hd  Bajar el cursor una linea
ae  Fin de juego de caracteres alternativo ho  Vuelta del cursor al principio
as  Iniciar juego de caracteres alternativohu  Mover cursor media linea arriba
     para caracteres grficos de bloques    i1  Cadena de inicio 1 al entrar (login)
bc  Carcter de retroceso, si no es ^H      i3  Cadena de inicio 2 al entrar (login)
bl  Pitido acustico                        is  Cadena de inicio 3 al entrar (login)
bt  Moverse a la parada de tabulacion previaic  Inserar un caracter

90                      CAPITULO 8.  GRAFICOS EN MODO CARACTER



IC  Insertar %1 caracteres                    if not f2
if  Fichero de inicio                         ... ...
im  Entrar en modo insercion              la  Label of tenth function key,
ip  Insert pad time and needed special        if not f10
    characters after insert               le  Cursor left one character
iP  Programa de inicio                    ll  Move cursor to lower left corner
K1  tecla superior izquierda en teclado deLnumerosE  Cursor left %1 characters
K2  tecla central en teclado de numeros   LF  Turn soft labels off
K3  tecla superior derecha en teclado de numerosLO  Turn soft labels on
K4  tecla inferior izquierda en teclado demnumerosb  Start blinking
K5  tecla inferior derecha en teclado de numerosMC  Clear soft margins
k0  Tecla de funcion 0                    md  Start bold mode
k1  Tecla de funcion 1                    me  End all mode like so, us, mb,
k2  Tecla de funcion 2                    md and mr
k3  Tecla de funcion 3                    mh  Start half bright mode
k4  Tecla de funcion 4                    mk  Dark mode (Characters invisible)
k5  Tecla de funcion 5                    ML  Set left soft margin
k6  Tecla de funcion 6                    mm  Put terminal in meta mode
k7  Tecla de funcion 7                    mo  Put terminal out of meta mode
k8  Tecla de funcion 8                    mp  Turn on protected attribute
k9  Tecla de funcion 9                    mr  Start reverse mode
k;  Tecla de funcion 10                   MR  Set right soft margin
ka  Limpiar todas las tabulaciones        nd  Cursor right one character
kA  Tecla de insertar linea               nw  Carriage return command
kb  Tecla de retroceso                    pc  Padding character
kB  Fin de tab. retroceso                 pf  Turn printer off
kC  Tecla de limpiar pantalla             pk  Program key %1 to send string %2
kd  Tecla de bajar cursor                     as if typed by user
kD  Tecla de borrar caracter              pl  Program key %1 to execute string
    en el cursor                              %2 in local mode
ke  desactivar teclado de numeros         pn  Program soft label %1 to to show
kE  Tecla de borrar hasta fin de linea        string %2
kF  Tecla de scroll adelante/abajo        po  Turn the printer on
kh  Tecla de regreso del cursor al inicio pO  Turn the printer on for %1
kH  Cursor home down key                      (<256) bytes
kI  Tecla de insertar caracter/modo de insercionps  Print screen contents on printer
kl  Tecla de cursor izquierda             px  Program key %1 to send string
kL  Tecla de borrar linea                     %2 to computer
kM  Tecla de salir modo insercion         r1  Reset string 1, set sane modes
kN  Tecla de siguiente pagina             r2  Reset string 2, set sane modes
kP  Tecla de pagina anterior              r3  Reset string 3, set sane modes
kr  Tecla de cursor derecha               RA  disable automatic margins
kR  Tecla de scroll atrs/arriba           rc  Restore saved cursor position
ks  Activar teclado de numeros            rf  Reset string file name
kS  Tecla de borrar hasta fin de pantalla RF  Request for input from terminal
kt  Tecla de limpiar esta tabulacion      RI  Cursor right %1 characters
kT  Tecla para poner tab. aqui            rp  Repeat character %1 for %2 times
ku  Tecla de cursor arriba                rP  Padding after character sent in
l0  Label of zeroth function key,             replace mode
    if not f0                             rs  Reset string
l1  Label of first function key,          RX  Turn off XON/XOFF flow control
    if not f1                             sa  Set %1 %2 %3 %4 %5 %6 %7 %8
l2  Label of first function key,              %9 attributes

8.2.  LA LIBRERIA TERMCAP                                              91



SA  enable automatic margins                   cursor motion
sc  Save cursor position                   ts  Move cursor to column %1 of
se  End standout mode                          status line
sf  Normal scroll one line                 uc  Underline character under cursor
SF  Normal scroll %1 lines                     and move cursor right
so  Start standout mode                    ue  End underlining
sr  Reverse scroll                         up  Cursor up one line
SR  scroll back %1 lines                   UP  Cursor up %1 lines
st  Set tabulator stop in all rows at      us  Start underlining
     current column                        vb  Visible bell
SX  Turn on XON/XOFF flow control          ve  Normal cursor visible
ta  move to next hardware tab              vi  Cursor invisible
tc  Read in terminal description from      vs  Standout cursor
     another entry                         wi  Set window from line %1 to %2
te  End program that uses                      and column %3 to %4
     cursor motion                         XF  XOFF character if not ^S
ti  Begin program that uses

92                      CAPITULO 8.  GRAFICOS EN MODO CARACTER



8.3     Ncurses - Introduccion


Se usara la siguiente terminologia a lo largo de este capitulo:


   o  ventana (window) - es una representacion interna que contiene una imagen
      de una parte de la pantalla. WINDOW se define en ncurses.h.


   o  pantalla (screen) - es una ventana con el tama"no de toda la pantalla (desde
      el superior izquierdo al inferior derecho). Stdscr y curscr son pantallas.


   o  terminal - es una pantalla especial con informacion sobre lo que aparece
      en la pantalla actual.


   o  variables - Las siguientes son variables y constantes definidas en ncurses.h


        -  WINDOW *curscr - pantalla actual

        -  WINDOW *stdscr - pantalla estandar

        -  int LINES - lineas del terminal

        -  int COLS - columnas del terminal

        -  bool TRUE - flag verdadero, 1

        -  bool FALSE - flag falso, 0

        -  int ERR - flag de error, -1

        -  int OK - flag de correccion, 0


   o  funciones - los argumentos que llevan son de los siguientes tipos:


        -  win - WINDOW*

        -  bf - bool

        -  ch - chtype

        -  str - char*

        -  chstr - chtype*

        -  fmt - char*

        -  en otro caso, int (entero)


   Normalmente un programa que usa la biblioteca ncurses se parece a esto:


          #include <ncurses.h>
          ...
          main()
          {
             ...
             initscr();
             /* Llamadas a funciones de ncurses */
             endwin();
             ...
          }

8.3.  NCURSES - INTRODUCCION                                         93



    La inclusion de ncurses.h definira variables y tipos para ncurses, tales co-
mo WINDOW y prototipos de funciones.  Incluye automaticamente stdio.h,
stdarg.h, termios.h y unctrl.h.
    La funcion initscr() se usa para inicializar las estructuras de datos ncurses
y para leer el archivo terminfo apropiado.  La memoria se reserva.  Si ocurre
un error, initscr devolvera ERR, en otro caso devuelve un puntero. Adicional-
mente la pantalla se borra e inicializa.
    La funcion endwin() libera todos los recursos para ncurses y restaura los
modos de terminal al estado que tenian antes de llamar a initscr().  Se debe
llamar antes de cualquier otra funcion de la biblioteca ncurses y endwin() debe
ser llamado antes de que su programa termine. Cuando quiere salidas por mas
de un terminal, puede usar newterm(...) en lugar de initscr().
    Compilese el programa con:


           gcc [opciones] ficheros -lncurses


    En las opciones se incluira cualquiera que precise (ver gcc(1)).  Ya que el
camino a ncurses.h ha cambiado, debe poner al menos la siguiente opcion:


           -I/usr/include/ncurses


    En otro caso, no se encontraran ni ncurses.h, nterm.h, termcap.h o unctrl.h.
Otras posibles opciones en Linux son:


           -O2 -ansi -Wall -m486


    O2 pide a gcc cierta optimizacion, -ansi es para que compile codigo compa-
tible con ANSI-C, -Wall imprimira toda clase de avisos y -m486 generara codigo
optimizado para Intel 486 (aunque el codigo podra correr tambien en un 386).
    La libreria ncurses esta en /usr/lib/. Hay tres versiones de esta:


    o libncurses.a es la libreria normal.


    o libdcurses.a es la libreria que permite depuracion.


    o libpcurses.a para analisis de perfil (desde la version 1.8.6libpcurses.a ya
      no existe ?).


    o libcurses.a es la curses BSD original, presente en paquetes BSD de dis-
      tribuciones como la Slackware 2.1.


    Las estructuras de datos para la pantalla se llaman ventanas (windows)
como se define en ncurses.h.  Una ventana es como un string de caracteres
en memoria que el programador puede manipular sin salida al terminal.  La
ventana por defecto tiene el tama"no del terminal.  Puede crear otras ventanas
con newwin(...).
    Para actualizar el terminal fisico de forma optima, ncurses tiene otra ven-
tana declarada, curscr.  Esto es una imagen de a que se parece actualmente el
terminal, y stdscr es una imagen de como deberia parecer el terminal.  La sa-
lida se efectuara cuando llame a refresh(). Ncurses entonces actualizara curscr

94                      CAPITULO 8.  GRAFICOS EN MODO CARACTER



y el terminal fisico con la informacion disponible en stdscr.  Las funciones de
biblioteca usaran optimizaciones internas para actualizar el proceso de forma
que pueda cambiar diferentes ventanas y entonces actualizar la pantalla de una
vez de una forma optima.
   Con las funciones ncurses puede manipular las estructuras de datos de las
ventanas.  La funciones que comienzan por w le permiten especificar una ven-
tana, mientras que otras afectaran a la ventana. Las funciones que comienzan
con mv moveran el cursor a la posicion y,x primero.
   Un caracter tiene el tipo chtype que es de tipo entero largo sin signo, para
almacenar informacion adicional sobre el (atributos, etc.).
   Ncurses usa la base de datos terminfo.  Normalmente la base de dtos esta
situada en /usr/lib/terminfo/ y ncurses buscara alli para las definiciones del
terminal local.  Si quiere comprobar alguna otra definicion para una terminal
sin cambiar el terminfo original, ponga el valor en la variable de entorno TER-
MINFO. Ncurses comprobara esta variable y usara las definiciones almacenadas
alli en lugar de /usr/lib/terminfo/.
   La version actual de ncurses es la 1.8.6().
   Al final del capitulo encontrara una tabla con una revision de las Curses
de BSD, NCurses y las Curses de SunOS 5.4.  Refierase a ella cuando quiera
buscar una funcion especifica y donde se encuentra implementada.
8.4     Inicializacion


   o  WINDOW  *initscr()
      Esta es la primerra funcion que se normalmente se llama de un programa
      que usa ncurses.  En algunos casos esutil para llamar a slk_init(int),
      filter(), ripoffline(...)  o use_env(bf ) antes de initscr().  Cuando use
      terminales multiples (o quizas capacidades de comprobacion), puede usar
      newterm(...) en lugar de initscr().

      initscr() leera el archivo terminfo apropiado e inicializara la estructura
      de datos ncurses , reservara memoria para curscr y pondra los valores
      LINES y COLS que tiene el terminal.  Devolvera un puntero a stdscr o
      ERR cuando ocurra un error. No necesita inicializar el puntero con:


                stdscr=initscr();


      initscr() hara esto por usted. Si el valor retornado es ERR, su programa
      debe salir debido a que ninguna funcion ncurses funcionara:


                if(!(initscr())){
                  fprintf(stderr,"tipo: initscr() ha fallado\n\n");
                  exit (1);
                }


   o  SCREEN  *newterm(char  *tipo,  FILE  *outfd,  FILE  *infd)
      Para salida por multiples terminales debe llamarse a newterm(...)  por
      cada uno de aquellos que pretenda controlar con ncurses, en lugar de
      llamar a initscr(). El argumento tipo es el nombre del terminal, tal como

8.5.  VENTANAS                                                             95



      aparece en la variable de entorno $TERM (ansi, xterm, vt100, etcetera).
      El argumento outfd es el puntero de salida, y el infd es el de entrada.
      Debe llamarse a endwin() por cada terminal abierto con newterm(...).


    o SCREEN  *set_term(SCREEN  *nueva)
      Com set_term(SCREEN) podemos cambiar de terminal.  Todas las
      funciones posteriores actuaran sobre el terminal seleccionado.


    o int  endwin()
      endwin() realiza labores de limpieza, restaura el modo del terminal al
      estado que tenia antes de llamar a initscr() y lleva el cursor a la esquina
      inferior izquierda.  No olvide cerrar todas las ventanas antes de llamar a
      esta funcion para finalizar su aplicacion.

      Una llamada adicional a refresh() despues de endwin() restaurara el
      terminal al estado que tuviera antes de llamar a initscr() (modo visual);
      en otro caso sera limpiado (modo no visual).


    o int  isendwin()
      Devuelve TRUE si endwin() y refresh() han sido ejecutadas ya.  En
      otro caso devolvera FALSE.


    o void  delscreen(SCREEN*  pantalla)
      Tras llamar a endwin(), llamese a delscreen(SCREEN) para liberar
      los recursos, cuando la pantalla del argumento ya no se necesite. (Nota:
      no implementado aun.)



8.5      Ventanas


Las ventanas se pueden crear, borrar, mover, copiar, duplicar y mas.


    o WINDOW  *newwin(nlineas,  ncols,  iniy,  inix)
      iniy e inix son las coordenadas de la esquina superior izquierda de la
      ventana. nlineas es un entero con el numero de lineas, y ncols es otro
      entero con el numero de columnas.


                WINDOW *miventana;
                miventana=newwin(10,60,10,10);


      La esquina superior izquierda de nuestra ventana queda en la linea y
      columna 10; y tiene 10 lineas y 60 columnas.  Si nlineas fuera cero, la
      ventana tendria LIN EAS - iniy filas.  De la misma manera, tendremos
      COLS - inix columnas si ncols vale cero.

      Cuando llame a newwin(...) con todos los argumentos a cero:

                WINDOW *miventana;
                miventana=newwin(0,0,0,0);


      la ventana asi creada tendra el tama"no de la pantalla.

      Con la ayuda de LINES y COLS podemos abrir ventanas en medio de la
      pantalla, con el codigo siguiente:

96                      CAPITULO 8.  GRAFICOS EN MODO CARACTER
                   Figura 8.1: Ncurses - esquema de newwin


              0          ini_x
              |           |
              |           |
              ||          |
        _0_ __|___________|_______________________________________________-|COLS
              |           |                                   |
              |           |                                   |
              |           |                                   |
              |           |                                   |
              |           |                                   |
              |           |oe... . . .. .n.c.o..l.s. .. . . -.|
         ini_y|           |                                   |
        _ __ __|_ __ __ ______________________________________||||
              |        6. |                                    |
              |        .  |                                    |
              |        .  |                                    |
              |        .  |                                    |
              |        .  |                                    |
              |        .  |                                    |
              |   nline.as| newwin(nlineas, ncols, ini_y, ini_x|)
              |        .  |                                    |
              |           |                                    |
              |        .  |                                    |
              |        .  |                                    |
              |        .  |                                    |
              |        .  |                                    |
              _|__ _ __?__|___________________________________ |
              |
              |
              |
              |
        LINEAS|
              |
              |?

                #define MILINEA (int) ((LINES-22)/2)
                #define MICOL ((COLS-70)/2)
                #define MISLINEAS 22
                #define MISCOLS   70
                ...
                WINDOW *vent;
                ...
                if(!(initscr())){
                  fprintf(stderr,"tipo: initscr() ha fallado\n\n");
                  exit(1);
                }
                ...
                if ((LINES<22)||(COLS<70)){
                  fprintf(stderr,"pantalla demasiado peque~na\n\n");
                  endwin(); exit (1);
                }
                win=newwin(MISLINEAS,MISCOLS,MILINEA,MICOL);
                ...


      Esto abrira una ventana de 22 lineas y 70 columnas en medio de la pan-
      talla.  Comprueba antes que quepa.  En la consola de Linux tenemos 25
      o mas lineas, y 80 o mas columnas, pero en los xterms este no es el caso,
      pues son libremente dimensionables.

      Alternativamente podemos usar LINES y COLS para adaptar las ventanas
      al tama"no de la pantalla:

8.5.  VENTANAS                                                             97



                #define MISFILAS    (int) (LINES/2+LINES/4)
                #define MISCOLS    (int) (COLS/2+COLS/4)
                #define FILAIZ   (int) ((LINES-MISFILAS)/2)
                #define COLIZ   (int) (((COLS-2)-MISCOLS)/2)
                #define FILADR (int) (FILAIZ)
                #define COLDR (int) (FILAIZ+2+MISCOLS)
                #define VCOLS     (int) (MISCOLS/2)
                ...
                WINDOW *ventizq, *ventder;
                ...
                ventizq=newwin(MISFILAS, VCOLS, FILAIZ, COLIZ);
                ventder=newwin(MISFILAS, VCOLS, FILADR, COLDR);
                ...


      Vease screen.c en el directorio de ejemplos para mas detalle.


    o int  delwin(ventana)
      Borra la ventana ventana.  Cuando hay subventanas dentro, las borra
      antes.  Ademas libera todos los recursos que ocupe la ventana.  Y Borra
      todas las ventanas abiertas antes de llamar a endwin().


    o int  mvwin(ventana,  by,  bx)
      Esta funcion mueve la ventana a las coordenadas (by,bx). Si esto implica
      mover la ventana mas alla de los extremos de la pantalla, no se hace nada
      y se devuelve ERR.


    o WINDOW  *subwin(venorig,  nlineas,  ncols,  iniy,  inix)
      Devuelve una subventana interior a venorig.  Cuando cambie una de
      las dos ventanas (la subventana o la otra), este cambio sera reflejado en
      ambas. Llame a touchwin(venorig) antes del siguiente refresh().

      inix e iniy son relativos a la pantalla, no a la ventana venorig.


    o WINDOW  *derwin(venorig,  nlineas,  ncols,  iniy,  inix)
      Es parecida a la anterior funcion, solo que ahora los parametros iniy e
      inix son relativos a la ventana venorig y no a la pantalla.


    o int  mvderwin(ventana,  y,  x)
      Mueve la ventana deltro de la ventana madre. (Nota: no implementado
      aun.)


    o WINDOW  *dupwin(ventana)
      Duplica la ventana.


    o int  syncok(ventana,  bf)
      void  wsyncup(ventana)
      void  wcursyncup(ventana)
      void  wsyncdown(ventana)
      (Nota: no implementado aun.)


    o int  overlay(vent1,  vent2)
      int  overwrite(vent1,  vent2)

98                      CAPITULO 8.  GRAFICOS EN MODO CARACTER



      overlay(...) copia todo el texto de vent1 a vent2 sin copiar los blancos.
      La funcion overwrite(...) hace lo mismo pero ademas copia los blancos.


   o  int  copywin(vent1,  vent2,  sminfil,  smincol,  dminfil,
      dmincol,
      dmaxfil,  dmaxcol,  overlay)
      Es similar a overlay(...)  y overwrite(...),  pero proporciona control
      sobre la region de la ventana a copiar.
8.6     Salida


   o  int  addch(ch)
      int  waddch(ven,  ch)
      int  mvaddch(y,  x,  ch)
      int  mvwaddch(ven,  y,  x,  ch)
      Estas funciones se usan para enviar caracteres a la ventana.  Para verlos
      efectivamente habra que llamar a refresh(). Con las funciones addch()
      y waddch() se envia el caracter a la ventana actual o a la especificada,
      respectivamente.  Las funciones mvaddch() y mvwaddch() hacen lo
      mismo pero previamente mueven el cursor a la posicion indicada.


   o  int  addstr(str)
      int  addnstr(str,  n)
      int  waddstr(ven,  str)
      int  waddnstr(ven,  str,  n)
      int  mvaddstr(y,  x,  str)
      int  mvaddnstr(y,  x,  str,  n)
      int  mvwaddstr(ven,  y,  x,  str)
      int  mvwaddnstr(ven,  y,  x,  str,  n)



      Estas funciones escriben un string en la ventana y son equivalentes a se-
      ries de llamadas a addch(), etc.  str debe ser terminado en el caracter
      nulo (\0). Las funciones con parametro ven especifican la ventana donde
      escribir.  Si no aparece se envia a la ventana estandar (stdscr).  Las fun-
      ciones con parametro n indican cuantos caracteres escribir; y si n vale -1,
      se escribiran todos los caracteres del string.


   o  int  addchstr(chstr)
      int  addchnstr(chstr,  n)
      int  waddchstr(ven,  chstr)
      int  waddchnstr(ven,  chstr,  n)
      int  mvaddchstr(y,  x,  chstr)
      int  mvaddchnstr(y,  x,  chstr,  n)
      int  mvwaddchstr(ven,  y,  x,  chstr)
      int  mvwaddchnstr(ven,  y,  x,  chstr,  n)
      Estas funciones copian chstr a la ventana.  La posicion inicial es la del
      cursor.  Las funciones con parametro n escriben esos n caracteres del

8.6.  SALIDA                                                                  99



      string;  y si vale -1 se escribiran todos.  El cursor no es movido ni se
      comprueban caracteres de control.  Estas funciones son mas rapidas que
      las addstr(...).  El parametro chstr es un puntero a un array de tipo
      chtype.


    o int  echochar(ch)
      int  wechochar(vent,  ch)
      Es lo mismo que llamar a addch o waddch seguido de una llamada al
      refresh().



8.6.1     Salida con Formato


    o int  printw(fmt,  ...)
      int  wprintw(win,  fmt,  ...)
      int  mvprintw(y,  x,  fmt,  ...)
      int  mvwprintw(win,  y,  x,  fmt,  ...)
      int  vwprintw(win,  fmt,  va_list)
      Estas funciones se corresponden con printf(...) y su formas asociadas.

      El paquete printf(...)  se usa para formatear salidas.  Puede definir una
      cadena de salida e incluir variables de diferentes tipos en ella.  Vea la
      seccion 8.1.1 en la pagina 82 para mas informacion.

      Para usar la funcion vwprintw(...) tiene que incluirse en el programa la
      cabecera varargs.h.



8.6.2     Insercion de Caracteres/Lineas


    o int  insch(c)
      int  winsch(win,  c)
      int  mvinsch(y,x,c)
      int  mvwinsch(win,y,x,c)
      El caracter ch se inserta a la izquierda del cursor y los demas son movidos
      una posicion a la derecha.  El caracter del extremo derecho de la linea
      puede perderse.


    o int  insertln()
      int  winsertln(win)
      Inserta una linea en blanco sobre la actual (la linea mas inferior se per-
      dera).


    o int  insdelln(n)
      int  winsdelln(win,  n)
      Para valores positivos de n estas funciones insertaran n lineas sobre el
      cursor en la ventana seleccionada, con lo que las n lineas inferiores se
      perderan. Cuando n es negativo, se borraran n lineas bajo el cursor y las
      inferiores seran movidas hacia arriba.


    o int  insstr(str)
      int  insnstr(str,  n)

100                     CAPITULO 8.  GRAFICOS EN MODO CARACTER



      int  winsstr(win,  str)
      int  winsnstr(win,  str,  n)
      int  mvinsstr(y,  x,  str)
      int  mvinsnstr(y,  x,  str,  n)
      int  mvwinsstr(win,  y,  x,  str)
      int  mvwinsnstr(win,  y,  x,  str,  n)
      Estas funciones insertaran la cadena str en la linea actual a la izquierda
      del cursor. Los caracteres de la derecha de este son movidas a la derecha
      y se perderan si superan el final de la linea.  La posicion del cursor no
      cambia.

      y y x son las coordenadas a las que el cursor sera movido antes de insertar
      la cadena, y n es el numero de caracteres a insertar (cuando valga 0, se
      insertara la cadena completa).



8.6.3    Borrado de Caracteres/Lineas


   o  int  delch()
      int  wdelch(win)
      int  mvdelch(y,  x)
      int  mvwdelch(win,  y,  x)
      Estas funciones borran el caracter del cursor y mueven los restantes ca-
      racteres que esten a la derecha, una posicion a la izquierda.

      y y x son las coordenadas en las que se pondra el cursor previamente al
      borrado.


   o  int  deleteln()
      int  wdeleteln(win)
      Estas funciones borran la linea del cursor y mueven las restantes lineas
      inferiores una posicion mas arriba.



8.6.4    Cajas y Lineas


   o  int  border(ls,  rs,  ts,  bs,  tl,  tr,  bl,  br)
      int  wborder(win,  ls,  rs,  ts,  bs,  tl,  tr,  bl,  br)
      int  box(win,  vert,  hor)
      Sirven para dibujar un borde en los lados de una ventana (bien sea la
      stdscr o el parametro win). En la siguiente tabla se aprecian los caracteres
      y sus valores por defecto cuando se llama a box(...). En la figura puede
      verse la posicion de los caracteres en una caja.


   o  int  vline(ch,  n)
      int  wvline(win,  ch,  n)
      int  hline(ch,  n)
      int  whline(win,  ch,  n)
      Estas funciones dibujan una linea vertical u horizontal a partir de la po-
      sicion del cursor. El caracter ch esl el que se utiliza, y n es el numero de
      caracteres deseados. La posicion del cursor no cambia.

8.6.  SALIDA                                                                 101



                    Tabla 8.3: Ncurses - caracteres del borde


               __Caracter__|__Posicion______|__Defecto_______________

                     tl      |superior izq.   |ACS_ULCORNER
                     ts      |lado superior   |ACS_HLINE
                     tr      |superior der.   |ACS_URCORNER
                     ls      |lado izquierdo  |ACS_VLINE
                     rs      |lado derecho   | ACS_VLINE
                     bl      |inferior izq.    |ACS_LLCORNER
                     bs      |lado inferior    |ACS_HLINE
                     br      |inferior der.    |ACS_LRCORNER
                     rt      |centro der.     |ACS_RTEE
                     lt      |centro izq.     |ACS_LTEE
                     tt      |centro sup.     |ACS_TTEE
                     bt      |centro inf.      |ACS_BTEE


                     Figura 8.2: Ncurses - caracteres de caja



                        tl_________________________________|||||tstttstr
                          |               |                |
                          |               |                |
                          |               |                |
                          |               |                |
                        ls|               |                |rs
                          |               |                |
                          |               |                |
                          |               |                |
                          |___ __ __ __ __|_ __ __ __ __ _||
                        lt|               |                |rt
                          |               |                |
                          |               ||               |
                          |               |                |
                          |               |                |
                        ls|               |                |rs
                          |               |                |
                          |               |                |
                          |               |                |
                          |________________________________|||
                        bl        bs      bt     bs        br

102                     CAPITULO 8.  GRAFICOS EN MODO CARACTER



8.6.5    Caracter de Fondo


   o  void  bkgdset(ch)
      void  wbkgdset(win,  ch)
      Fija el caracter y atributo para la pantalla o una ventana.  El atributo
      en ch sera ORed con cada caracter no blanco en la ventana. El fondo es
      entonces parte de la venana y no cambia con desplazamientos ni con las
      salidas.


   o  int  bkgd(ch)
      int  wbkgd(win,  ch)
      Cambia el caracter de fondo y el atributo a ch.
8.7     Entrada


   o  int  getch()
      int  wgetch(win)
      int  mvgetch(y,  x)
      int  mvwgetch(win,  y,  x)
      getch() leera la entrada del terminal de una forma que dependera si el
      modo de retardo (delay) esta activo no.  Si delay esta activo, getch()
      esperara hasta que se pulse una tecla, en otro caso devolvera la tecla
      del buffer de entrada o ERR si el buffer esta vacio.  mvgetch(...)  y
      mvwgetch(...) movera primero el cursor a la posicion y,x. Las funciones
      w leen la entrada del terminal a la ventana win, getch() y mvgetch(...)
      del terminal asociado.

      Con keypad(...)  activado, getch() devolvera un codigo definido en .h
      como ,macros KEY_* cuando se pulsa una tecla de funcion.  Cuando se
      pulsa ESCAPE (que puede ser el inicio de una tecla de funcion) ncurses
      iniciara un temporizador de un segundo. Si el resto de las pulsaciones no
      se completa en este segundo, se devuelve la tecla. En otro caso se devuelve
      el valor de la tecla de funcion.  (Si es necesario, use notimeout() para
      desactivar el temporizador de un segundo).


   o  int  ungetch(ch)
      Will put the character ch back to the input buffer.


   o  int  getstr(str)
      int  wgetstr(win,  str)
      int  mvgetstr(y,  x,  str)
      int  mvwgetstr(win,  y,  x,  str)
      int  wgetnstr(win,  str,  n)
      Estas funciones realizaran series de llamadas a getch() hasta que se reciba
      un caracter de fin de linea. Los caracteres se guardan en str (por lo que
      no olvide reservar memoria antes de llamar a estas funciones).  Si el eco
      esta activo, la cadena es reflejada en pantalla, y las teclas kill y delete
      seran interpretadas (utilice la funcion noecho para desactivar el eco).

8.7.  ENTRADA                                                             103



    o chtype  inch()
      chtype  winch(win)
      chtype  mvinch(y,  x)
      chtype  mvwinch(win,  y,  x)
      Estas funciones devuelven un caracter de una pantalla o ventana. Como
      el tipo del valor devuelto es chtype se incluye informacion de atributo.
      Esta informacion se puede extraer del caracter usando las constantes A_*
      constants (ver tabla 8.4 en la pagina 112).

    o int  instr(str)
      int  innstr(str,  n)
      int  winstr(win,  str)
      int  winnstr(win,  str,  n)
      int  mvinstr(y,  x,  str)
      int  mvinnstr(y,  x,  str,  n)
      int  mvwinstr(win,  y,  x,  str)
      int  mvwinnstr(win,  y,  x,  str,  n)
      Return a character string from the screen or a window.  (Nota:  no im-
      plementado aun.)

    o int  inchstr(chstr)
      int  inchnstr(chstr,  n)
      int  winchstr(win,  chstr)
      int  winchnstr(win,  chstr,  n)
      int  mvinchstr(y,  x,  chstr)
      int  mvinchnstr(y,  x,  chstr,  n)
      int  mvwinchstr(win,  y,  x,  chstr)
      int  mvwinchnstr(win,  y,  x,  chstr,  n)
      Estas funciones devuelven una cadena de carateres de la pantalla o venta-
      na. En la cadena se incluye una informacion de atributo por cada caracter.
      (Nota: no implementado aun, lib_inchstr no incluida en la libreria ncur-
      ses.)



8.7.1     Entrada con Formato



    o int  scanw(fmt,  ...)
      int  wscanw(win,  fmt,  ...)
      int  mvscanw(y,  x,  fmt,  ...)
      int  mvwscanw(win,  y,  x,  fmt,  ...)
      int  vwscanw(win,  fmt,  va_list)
      Estas son similares a scanf(...)  (vea la seccion 8.1.2 en la pagina 84).
      wgetstr(...) se llama y el resultado se usa como una entrada para scan.

104                     CAPITULO 8.  GRAFICOS EN MODO CARACTER



8.8     Opciones


Opciones de Salida


   o  int  idlok(win,  bf)
      void  idcok(win,  bf)
      Activan o desactivan las caracteristicas de insercion/borrado del terminal
      a la ventana; para lineas con idlok(...) y para caracteres con idcok(...).
      (Nota: idcok(...) no implementado aun.)


   o  void  immedok(win,  bf)
      Si es TRUE, cada cambio en la ventana win supondra un refresco de la
      pantalla fisica.  Esto puede decrementar el rendimiento de un programa,
      por lo que el valor por defecto es FALSE. (Nota: no implementado aun.)


   o  int  clearok(win,  bf)
      Si bf es TRUE, la siguiente llamada a wrefresh(win) limpiara la pantalla
      y la redibujara totalmente (como cuando pulsamos CTRL-L en el editor
      vi).


   o  int  leaveok(win,  bf)
      El comportamiento normal de ncurses deja el cursor fisico en el mismo
      lugar antes delultimo refresco de la pantalla. Los programas que no usan
      el cursor pueden ejecutar esta funcion con el valor TRUE, y evitar el
      tiempo que requiere mover el cursor.  Ademas, ncurses intentara hacer
      que el cursor no sea visible.


   o  int  nl()
      int  nonl()
      Controla la traduccion del fin de linea.  Cuando se activa con la funcion
      nl(), traducira el fin de linea a un retorno de carro seguido de una alimen-
      tacion de linea.  Si lo ponemos a OFF con la funcion nonl(), se evitara
      esta traduccion lo que tambien implica un movimiento del cursor mas
      rapido.



8.8.1    Opciones en la entrada


   o  int  keypad(win,  bf)
      Si vale TRUE, habilita el teclado numerico de la terminal del usuario
      cuando esta esperando entrada de datos.  Ncurses retornara el codigo de
      tecla que se define en ncurses.h como KEY_* para cada tecla de funcion
      y para las teclas con las flechas. Esto es muyutil para un teclado de PC
      porque se puede de esta manera disponer entonces del bloqueo numerico
      y de las flechas.


   o  int  meta(win,  bf)
      Si esta en TRUE, los codigos de teclas que retorna getch() seran de 8
      bits (esto es, no se le pondra a cero su bit mas alto).

8.8.  OPCIONES                                                             105



                ____________________||||  __________________________|||||||||

                | ???  KEY_| KEY_|  |     NUM|| ||/   || *   ||-     ||
                ||     HOME||PPAGE||||    ||    |     |      |       ||
                |______|_____|______|     ______|_____|______|______
                |CTRL  KEY_| KEY_|  |     KEY_||KEY_|||KEY_| ||      ||
                |+D    END|  NPAGE| |     HOME| UP|   |PPAGE |       |
                |___________________|||   |_____|_____|______|_+     |
                                          KEY_||| ??? |KEY_| ||      ||
                       _______            LEFT|_||____|RIGHT_|______ |
                       |     |            |     |     |      |       |
                       KEY_| |            KEY_| KEY_| |KEY_  |       |
                _______UP||_________||_   END||_DOWN|||NPAGE||CTRL|_ ||
                |      |     |     |      |           |      |       |
                KEY_|  KEY_| KEY_| |      |   ???     |KEY_  |+M     |
                LEFT___DOWN__RIGHT__||||||||___________DC___________||||||||

    o int  cbreak()
      int  nocbreak()
      int  crmode()
      int  nocrmode()
      cbreak()  y  nocbreak()  enciende  y  apaga,  respectivamente  el  modo
      CBREAK de la terminal.  Cuando CBREAK esta encendido, cualquier
      caracter leido a la entrada estara disponible inmediatamente para el pro-
      grama; mientras que si esta apagado se almacenara en un bufer hasta
      que aparezca un caracter cambio de linea (newline). (Nota: crmode()
      y nocrmode() existen solo por razones de compatibilidad con versiones
      anteriores, por favor no los utilice..)


    o int  raw()
      int  noraw()
      Enciende y apaga, respectivamente, el modo RAW. Este modo es igual al
      CBREAK, excepto por el hecho que en modo RAW no se procesa a los
      caracteres especiales.


    o int  echo()
      int  noecho()
      Use echo() para obtener eco en pantalla de los caracteres tecleados por
      el usuario a la entrada, y noecho() para que no se vea dicho eco.


    o int  halfdelay(t)
      Como el caso de cbreak() pero con una espera de t segundos.


    o int  nodelay(win,  bf)
      Con TRUE como argumento, configura la terminal en modo inmediato
      (non-blocking). getch() retornara ERR si no hay caracteres ya disponi-
      bles a la entrada. Si se le da FALSE como argumento, getch() esperara
      hasta que se oprima una tecla.


    o int  timeout(t)
      int  wtimeout(win,  t)

106                     CAPITULO 8.  GRAFICOS EN MODO CARACTER



      Se recomienda utilizar estas funciones en lugar de halfdelay(t) y node-
      lay(win,bf ).  El resultado de getch() depende del valor de t.  Si t es
      positivo, la lectura se detiene durante t milisegundos, si t es cero, no se
      realiza ninguna detencion, y cuando t es negativo el programa se detiene
      hasta que haya caracteres disponibles a la entrada.


   o  int  notimeout(win,  bf)
      Si bf es TRUE, getch() utilizara un contador regresivo especial (con
      un lapso de un segundo) para interpretar y aceptar las secuencias que
      comienzan con teclas como ESCAPE, etc.


   o  int  typeahead(fd)
      Si fd es -1 no se realizara control para saber si hay caracteres en es-
      pera (typeahead); sino, cuando ncurses realice dicho control utilizara el
      descriptor de fichero fd en lugar de stdin.


   o  int  intrflush(win,  bf)
      Cuando se habilita con bf en TRUE, entonces cualquiera de las teclas de
      interrupcion que se oprima (quit, break, . . . )  ocasionara que se exhiban
      todos los caracteres pendientes de salida en la cola del manejador de la
      tty.


   o  void  noqiflush()
      void  qiflush()
      (Nota: no implementado aun.)



8.8.2    Atributos de la terminal


   o  int  baudrate()
      Retorna la velocidad de la terminal en bps (bits per second).


   o  char  erasechar()
      Retorna el actual caracter que sirve para borrar (erase).


   o  char  killchar()
      Retorna el caracter actual para   matar   la linea actual (kill).


   o  int  has_ic()
      int  has_il()
      has_ic()  retorna  TRUE  si  la  terminal  tiene  la  capacidad  de  inser-
      tar/borrar de a un caracter has_il() retorna TRUE cuando la terminal
      tiene la capacidad de insertar/borrar de a lineas.  Si no fuera asi, las
      funciones retornan ERR. (Nota: no implementado aun.)


   o  char  *longname()
      Retorna un apuntador que nos permite acceder a la descripcion de la
      terminal actual.


   o  chtype  termattrs()
      (Nota: no implementado aun.)

8.8.  OPCIONES                                                             107



    o char  *termname()
      Retorna el contenido de la variable del entorno de usuario TERM. (Nota:
      no implementado aun.)



8.8.3     >Como se usa?


Hasta ahora hemos visto las opciones de las ventanas y los modos de las termi-
nales, ya es hora de describir como se utiliza todo esto.
    En Linux lo primero es habilitar el teclado numerico.  Esto permitira la
utilizacion de las teclas de las flechas y el teclado numerico.


           keypad(stdscr,TRUE);


    Ahora bien, existen dos maneras fundamentales de esperar entradas desde
el teclado.


   1. El progrma quiere que el usuario oprima una tecla y luego en funcion de
      la tecla seleccionada se elegira el procedimiento apropiado. (Por ejemplo,
      "Oprima  't'  para  terminar" y luego el programa aguarda una t)


   2. El programa quiere que el usuario escriba una cadena de caracteres dentro
      de una mascara exhibida en la pantalla.  Por ejemplo,  un nombre de
      directorio, o una direccion postal en una base de datos.


    Para el primer caso, utilizaremos las siguientes opciones y modos:


           char c;


           noecho();
           timeout(-1);
           nonl();
           cbreak();
           keypad(stdscr,TRUE);
           while(c=getch()){
             switch(c){
                case 't': funcion_de_terminaci\'on();
                default:   break;
             }
          }


    El programa se detiene hasta que se oprime una tecla.  Si la tecla fue s
llamamos a nuestra funcion de terminacion, sino, esperamos por otra tecla.
    La construccion switch puede expandirse hasta llenar nuestras necesidades
de procesamiento de entradas.  Utilice las macros KEY_* para leer las teclas
especiales, por ejemplo:


           KEY_UP       KEY_RIGHT     KEY_A1     KEY_B2     KEY_C1
           KEY_DOWN     KEY_LEFT      KEY_A3                KEY_C3


le serviran para leer las teclas de movimiento de cursor.
    Si desea ver el contenido de un fichero, debera utilizar un codigo como el
que sigue:

108                     CAPITULO 8.  GRAFICOS EN MODO CARACTER



          int sigo=TRUE;
          char c;
          enum{ARRIBA,ABAJO,DERECHA,IZQUIERDA};


          noecho();
          timeout(-1);
          nonl();
          cbreak();
          keypad(stdscr,TRUE);
          while(sigo==TRUE){
             c=getch();
             switch(c){
               case KEY_UP:
               case 'a':
               case 'A': scroll_s(ARRIBA);
                          break;
               case KEY_DOWN:
               case 'b':
               case 'B': scroll_s(ABAJO);
                          break;
               case KEY_LEFT:
               case 'i':
               case 'I': scroll_s(IZQUIERDA);
                          break;
               case KEY_RIGHT
               case 'd':
               case 'D': scroll_s(DERECHA);
                          break;
               case 't':
               case 'T': sigo=FALSE;
               default:   break;
             }
         }


   Para  el  segundo  caso,  solo  necesitamos  ejecutar  echo()  y  entonces  los
caracteres tecleados por el usuario se escribiran en la pantalla.  Para poner
los caracteres en alguna posicion deseada en particular, utilice move(. . . )  o
wmove(. . . ).

   O sino, podemos abrir una ventana con una mascara en ella (por ejemplo
podemos elegir colores distintos para resaltar la mascara) y solicitar al usuario
que ingrese una cadena:


          WINDOW *maskwin;
          WINDOW *mainwin;
          char *ptr=(char *)malloc(255);
          ...
             mainwin=newwin(3,37,9,21);
             maskwin=newwin(1,21,10,35);
             ...
             werase(mainwin);
             werase(maskwin);
             ...

8.9.  >COMO BORRAR VENTANAS Y LINEAS?                          109



             box(mainwin,0,0);
             mvwaddstr(mainwin,1,2,"Cadena a ingresar: ");
             ...
             wnoutrefresh(mainwin);
             wnoutrefresh(maskwin);
             doupdate();
             ...
             mvwgetstr(maskwin,0,0,ptr);
             ...
             delwin(maskwin);
             delwin(mainwin);
             endwin();
             free(ptr);



    Mire por favor input.c en el directorio de ejemplos, para una mejor explica-
cion.
8.9      >Como borrar ventanas y lineas?


    o int  erase()
      int  werase(win)
      werase(. . . ) y erase() taparan con espacios en blanco cada posicion de
      la ventana win o de stdscr, respectivamente. Por ejemplo, si Ud. configura
      los atributos de una ventana con ciertos colores y luego llama a werase(),
      la ventana se coloreara.  He tenido algunos problemas con COLOR_PAIRS
      cuando defino atributos distintos a negro sobre blanco, asi que termine
      escrbiendo mi propia funcion para borrar (que accede a bajo nivel a la
      estructura WINDOW:


                void NewClear(WINDOW *win)
                {
                int   y,x;


                  for ( y = 0 ; y <= win -> _maxy ; y++ )
                    for ( x = 0 ; x <= win -> _maxx ; x++ )
                      (chtype *) win-> _line[y][x] = ' '|win-> _attrs;
                  win -> _curx = win -> _cury = 0;
                  touchwin(win);
                }
      El problema es que ncurses a veces no utiliza los atributos de la venta-
      na cuando limpia la pantalla.  Por ejemplo, en lib_clrtoeol.c, se define a
      BLANK como:


                #define BLANK ' '|A_NORMAL
      asi que los otros atributos se pierden al borrar hasta el final de la linea.

110                     CAPITULO 8.  GRAFICOS EN MODO CARACTER



   o  int  clear()
      int  wclear(win)
      Igual que erase(), pero ejecuta ademas clearok() ( la pantalla se limpiara
      al realizarse el siguiente refresco).


   o  int  clrtobot()
      int  wclrtobot(win)
      Borra la linea donde se encuentra el cursor, comenzando desde el caracter
      justo a la derecha del cursor, y la linea debajo del cursor.


   o  int  clrtoeol()
      int  wclrtoeol(win)
      Borra la linea actual desde la posicion del cursor hasta el final.
8.10      Actualizacion de la imagen an la terminal


Como ya se menciono en la introduccion, las ventanas de ncurses son imagenes
en memoria. Esto significa que cualquier cambio que se realice en una ventana
no se refleja en la pantalla fisica de la terminal hasta que se efectue un   re-
fresco  .  De esta manera se optimiza la tarea de enviar la salida a la terminal
porque se puede realizar un monton de cambios y luego, de una sola vez, llamar
a refresh() para que escriba la pantalla final. Si no se manejara de este mo-
do, cada peque"no cambio en la pantalla deberia enviarse a la terminal, y por lo
tanto perjudicaria la performance del programa del usuario.


   o  int  refresh()
      int  wrefresh(win)
      refresh() copia stdscr a la terminal y wrefresh(win) copia la imagen
      de la ventana a stdscr y luego hace que curscr se vea como stdscr.


   o  int  wnoutrefresh(win)
      int  doupdate()
      wnoutrefresh(win) copia solo la ventana win a stdscr. Esto significa no
      se ha realizado ninguna actualizacion de la terminal, aunque la pantalla
      virtual stdscr tiene la disposicion actualizada. doupdate() se ocupara de
      enviar la salida a la terminal. De esta manera, un programa puede cambiar
      varias ventanas, llamar a wnoutrefresh(win) para cada ventana y luego
      llamar a doupdate() para actualizar la pantalla fisica solo una vez.

      Por ejemplo, tenemos el siguiente programa con dos ventanas.  Procede-
      mos a alterar ambas ventanas cambiando algunas lineas de texto. Pode-
      mos escribir changewin(win) con wrefresh(win).


                main()                       changewin(WINDOW *win)
                {                             {
                WINDOW *win1,*win2;             ... /* aqu\'{\i} alteramos */
                    ...                           ... /* las l\'{\i}neas */
                    changewin(win1);            wrefresh(win);
                    changewin(win2);       return;

8.11.  ATRIBUTOS DE VIDEO Y COLORES                              111



                     ...                      }
                }
      De esta manera, ncurses debera actualizar dos veces la terminal, y por
      lo tanto disminuira la velocidad de ejecucion de nuestro programa.  Con
      doupdate() modificamos changewin(win) y la funcion main() obtenien-
      do una mejor performance.


                main()                       changewin(WINDOW *win)
                {                             {
                WINDOW *win1,*win2;             ... /* aqu\'{\i} alteramos */
                     ...                           ... /* las l\'{\i}neas */
                     changewin(win1);            wnoutrefresh(win);
                     changewin(win2);       return;
                     doupdate();             }
                     ...
                }


    o int  redrawwin(win)
      int  wredrawln(win,  bline,  nlines)
      Utilice estas funciones cuando algunas lineas o toda la pantalla deba des-
      cartarse antes de escribir algo nuevo (puede ser por ejemplo cuando las
      lineas en la pantalla se han mezclado con basura, o algo asi).


    o int  touchwin(win)
      int  touchline(win,  start,  count)
      int  wtouchln(win,  y,  n,  changed)
      int  untouchwin(win)
      Le indica a ncurses que toda la ventana win o las lineas que van desde la
      start hasta la start+count se han tocado.  Por ejemplo, cuando tiene
      algunas ventanas que se solapan (como en el ejemplo de type.c) y se
      produce un cambio en una ventana, no se afecta a la imagen de la otra.

      wtouchln(. . . ) marcara como tocadas las n lineas que comienzan en y.
      Si change se pone en TRUE, entonces se marcan como tocadas dichas
      lineas, sino se marcan como que no han sido tocadas (cambiadas o no
      cambiadas).

      untouchwin(win) marcara la ventana win como que no ha sido modifi-
      cada desde laultima llamada a refresh().


    o int  is_linetouched(win,  line)
      int  is_wintouched(win)
      Con estas funciones, Ud. puede controlar si la linea line o la ventana win
      ha sido tocada desde laultima llamada a refresh().



8.11      Atributos de video y colores


Los atributos son capacidades especiales de la terminal que se utilizan al escribir
los caracteres en la pantalla. Los caracteres pueden escribirse en negrilla (bold),

112                     CAPITULO 8.  GRAFICOS EN MODO CARACTER
                         Tabla 8.4: Ncurses - atributos


_Definicion__________|___Atributo__________________________________________________

  A_ATTRIBUTES    |      mascara para los atributos (chtype)
  A_NORMAL         |     normal, quita todos los otros
  A_STANDOUT      |      el mejor modo para resaltar
  A_UNDERLINE     |      subrayado
  A_REVERSE        |     video en reverso
  A_BLINK            |   parpadeante
  A_DIM               |  brillo disminuido o medio brillo
  A_BOLD             |   negrilla o brillo extra
  A_ALTCHARSET   |       usar conjunto de caracteres alternativos
  A_INVIS             |  invisible
  A_PROTECT        |     ???
  A_CHARTEXT      |      mascara para el caracter actual (chtype)
  A_COLOR           |    mascara para el color
  COLOR_PAIR(n)    |     que el par de colores sea el almacenado en n
  PAIR_NUMBER(a)  |      obtener el par de colores almacenado en el atributo a

subrayado, parpadeantes, etc..  Con ncurses, Ud. disfruta de la posibilidad de
encender y apagar estos atributos y de esa manera puede mejorar la apariencia
de la salida. Los posibles atributos se enumeran en la siguiente tabla:
   Ncurses define ocho colores que Ud. puede utilizar en una terminal que
puede mostrar colores.  Primero, inicialice las estructuras de datos para color
con start_color(), luego controle la existencia de las capacidades de color con
has_colors().  start_color() inicializara COLORS, la maxima cantidad de
colores que puede manejar la terminal, y COLOR_PAIR, la maxima cantidad
de pares de colores que podra definir.
   Los atributos se pueden combinar con el operador OR   '|  , asi que puede
obtener negrilla y parpadeante mediante:


          A_BOLD|A_BLINK
                          Tabla 8.5: Ncurses - colores


                       _Definicion____________|__Color______

                         COLOR_BLACK      |       negro
                         COLOR_RED         |      rojo
                         COLOR_GREEN     |        verde
                         COLOR_YELLOW   |         amarillo
                         COLOR_BLUE        |      azul
                         COLOR_MAGENTA  |         magenta
                         COLOR_CYAN       |       cyan
                         COLOR_WHITE      |       blanco

8.11.  ATRIBUTOS DE VIDEO Y COLORES                              113



    Cuando se asignan ciertos atributos attr a una ventana, todos los caracteres
que escriba en dicha vantana se mostraran con esas propiedades, hasta haga un
cambio en los atributos de la ventana. No se perderan cuando enrolle (scroll)
la ventana, ni cuando la mueva, o accione sobre ella de cualquier otra manera.

    Si Ud. escribe programas que pueden utilizar ncurses y BSD curses, recuerde
que la BSD curses no permite el uso de colores.  (Tampoco hay colores en las
versiones antiguas de ncurses tipo SYS V.) Asi que si desea utilizar ambas
bibliotecas, debera utilizar estructuras de compilacion condicional con #ifdef.


    o int  attroff(attr)
      int  wattroff(win,  attr)
      int  attron(attr)
      int  wattron(win,  attr)
      Encienden (on) o apagan (off) el atributo especificado mediante attr sin
      tocar los otros atributos en la ventana (que sera stdscr o win).


    o int  attrset(attr)
      int  wattrset(win,  attr)
      Hace que los atributos de stdscr o win se configuren en attr.


    o int  standout()
      int  standend()
      int  wstandout(win)
      int  wstandend(win)
      Enciende y apaga el modo standout sobre la ventana (stdscr o win), que
      se utiliza para resaltar texto.


    o chtype  getattrs(win)
      Retorna los atributos que tiene win al momento de la llamada a esta
      funcion.


    o bool  has_colors()
      Retorna TRUE si la terminal tiene colores. Antes de utilizar colores, Ud.
      debe controlar con has_colors() que la terminal los pueda manejar, y a
      continuacion debe inicializar los colores con start_color()).


    o bool  can_change_color()
      TRUE si la terminal puede redefinir colores.


    o int  start_color()
      Inicializacion de colores.  Debe llamar a esta funcion antes de utilizar el
      manejo de colores!


    o int  init_pair(pair,  fg,  bg)
      Cuando en los argumentos a funciones de ncurses, donde se espera un
      atributo queremos poner colores, debemos utilizar los pares de colores.
      La definicion de un par de colores se realiza con init_pair(. . . ).  fg es
      el color del primer plano (caracteres) y bg es el color del fondo que se
      asocian en el par de colores pair. El par de colores pair no es mas que

114                     CAPITULO 8.  GRAFICOS EN MODO CARACTER



      un numero en el rango de 1 a COLOR P AIRS - 1 (Si, leyo bien, desde
      el 1; pues el 0 esta reservado para el par negro sobre blanco. Una vez que
      ha sido definido, el pair se comporta como un atributo. Por ejemplo, si
      desea poner caracteres rojos sobre fondo azul, haga:


                init_pair(1,COLOR_RED,COLOR_BLUE);


      Ahora puede llamar a wattr(. . . )  para que win tenga como colores los
      de nustro nuevo par:


                wattr(win,COLOR_PAIR(1));


      O puede combinar pares de colores con otros atributos, como se muestra
      a continuacion:


                wattr(win ,A_BOLD|COLOR_PAIR(1));
                wattr(win1,A_STANDOUT|COLOR_PAIR(1));


      El primero pone los colores que habiamos seleccionado y ademas enciende
      el atributo BOLD; el segundo ejemplo pone los colores y ademas levanta el
      brillo (STANDOUT), asi que obtenemos rojo brillante sobre azul.


   o  int  pair_content(pair,  f,  b)
      Obtiene los colores de primer plano (f) y fondo (b) correspondientes al
      par de colores pair.


   o  int  init_color(color,  r,  g,  b)
      Cambia los componentes del color color. Los componentes son r (rojo), g
      (verde) and b (azul), y pueden tomar valores en el rango 1 a COLORS-1.


   o  int  color_content(color,  r,  g,  b)
      Devuelve los componentes r (rojo), g (verde) y b (azul) que forman un
      dado color.


   Bueno, la pregunta ahora sera: como combinar atributos y colores?. Algunas
terminales, como la consola de Linux, tienen colores; otras, como xterm, vt100,
no los tienen.  El codigo que se muestra a continuacion deberia resolver este
problema:


      void CheckColor(WINDOW *win1, WINDOW *win2)
      {
       start_color();
       if (has_colors()){
         /* muy bien, tenemos colores, as\'{\i} que definimos los pares de
          * colores para car\'acter y para fondo.
          */
         init_pair(1,COLOR_BLUE,COLOR_WHITE);
         init_pair(2,COLOR_WHITE,COLOR_RED);
         /* ahora usamos los pares de colores reci\'en definidos para
          * configurar las ventanas
          */

8.12.  COORDENADAS DEL CURSOR Y DE LAS VENTANAS          115



          wattrset(win1,COLOR_PAIR(2));
          wattrset(win2,COLOR_PAIR(1));
        }
        else{
          /* Arf!, no hay colores (a lo mejor estamos en una vt100 o xterm).
           * Bien, entonces utilizaremos negro sobre blanco
           */
          wattrset(win1,A_REVERSE);
          wattrset(win2,A_BOLD);
        }
      return;
      }



    Primero, la funcion CheckColor inicializa los colores con start_color(), lue-
go la funcion has_colors() retornara TRUE si la terminal puede mostrar co-
lores. Si nos encontramos que acepta colores, llamamos a init_pair(. . . ) para
combinar los colores de frente con fondo en un par de colores, y luego llamamos
a

    wattrset(. . . ) para configurar las ventanas con los colores correspondien-
tes.   En el caso en que no tuvieramos la posibilidad de colores en nuestra
terminal, nos alcanza con utilizar wattrset(. . . ) para poner los atributos que
tolera nuestra terminal monocroma.

    Para obtener colores en xterm, la mejor manera que he encontrado consiste
en utilizar la ansi_xterm con las entradas emparchadas correspondientes al ter-
minfo del Midnight Commander. Si Ud. quiere usar la misma solucion, consiga
los fuentes de ansi_xterm y Midnight Commander (mc-x.x.tar.gz); compile la
ansi_xterm; use tic con xterm.ti y vt100.ti que obtiene del archivo mc-x.x.tar.gz;
ejecute ansi_xterm y compruebe su funcionamiento.
8.12      Coordenadas del cursor y de las ventanas


    o int  move(y,  x)
      int  wmove(win,  y,  x)
      move() mueve el cursor dentro de stdscr, wmove(win) mueve el cursor
      dentro de la ventana win. Para las funciones de entrada/salida, se definen
      macros adicionales que mueven el cursor antes de llamar a la funcion
      especificada.


    o int  curs_set(bf)
      Muestra u oculta el cursor, si la terminal es capaz de esta operacion.


    o void  getyx(win,  y,  x)
      getyx(. . . )  devuelve la posicion del cursor al momento de la llamada.
      (Nota: Es una macro.)


    o void  getparyx(win,  y,  x)
      Cuando win es una subventana, getparyx(. . . )  nos entregara las coor-
      denadas de la ventana en relacion a su ventana paterna, almacenandolas

116                     CAPITULO 8.  GRAFICOS EN MODO CARACTER



      en y y x.  En cualquier otro caso y y x se pondran a -1.  (Nota:  no
      implementado aun.)


   o  void  getbegyx(win,  y,  x)
      void  getmaxyx(win,  y,  x)
      int  getmaxx(win)
      int  getmaxy(win)
      Guardan en y y x las coordenadas de posicion y tama"no de win.


   o  int  getsyx(int  y,  int  x)
      int  setsyx(int  y,  int  x)
      Almacena la posicion del cursor dentro de la pantalla virtual en y y x o lo
      posiciona alli, respectivamente. Si pone a -1 los valores de y y x y llama
      a getsyx(. . . ), se habilitara leaveok.
8.13      Moviendonos por alli


   o  int  scrollok(win,  bf)
      Si se pone a TRUE, entonces el texto en la ventana win se movera una
      linea hacia arriba cuando se escriba un caracter (o un cambio de linea) y el
      cursor estaba posicionado sobre el caracter de la esquina inferior derecha.
      Si se pone a FALSE, el cursor quedara en la misma posicion.

      Cuando se habilita (con TRUE), se puede mover el contenido de las ven-
      tanas mediante la utilizacion de las siguientes funciones.  (Nota:  Las
      lineas del contenido de la ventana tambien se moveran si escribe un cam-
      bio de linea en laultima linea de la ventana. Asi que tenga cuidado con
      scrollok(. . . ) o le soprenderan los resultados..)


   o  int  scroll(win)
      Mueve las lineas de la ventana (y en la estructura de datos) una linea
      hacia ariba.


   o  int  scrl(n)
      int  wscrl(win,  n)
      Estas funciones mueven la pantalla stdscr o la ventana win hacia arriba
      o hacia abajo, de acuerdo al valor del entero n. Si n es positivo, las lineas
      de la ventana se mueven n lineas hacia arriba, si n es negativo se movera
      hacia abajo n lineas.


   o  int  setscrreg(t,  b)
      int  wsetscrreg(win,  t,  b)
      Configura una region de movimientos por software.


   El codigo que se muestra a continuacion le mostrara como puede obtener
el efecto de movimiento de las lineas de texto en la pantalla.  Vea ademas en
type.c en el directorio de los ejemplos.
   Tenemos una ventana con 18 lineas y 66 columnas, en la cual queremos
mover el texto. s[] es un vector de caracteres con el texto. max_s es el numero

 8.14.  PADS                                                                  117



 de laultima linea en s[]. clear_line escribe caracteres blancos desde la posicion
 actual del cursor hasta el fin de la linea, y utiliza los atributos actuales en la
 ventana (y no con el atributo A_NORMAL como lo hace clrtoeol(). beg es la
ultima linea de
     s[] que se muestra en la pantalla en cualquier momento dado.  scroll es un
 tipo enumerado para indicar a la funcion que es lo que hay que hacer: si mostrar
 la linea SIGuiente o la ANTerior.

       enum{ANT,SIG)};


       void scroll_s(WINDOW *win, int scroll)
       {
         /* verificar si necesitamos mover las l\'{\i}neas,
          * y si hay l\'{\i}neas para mover
          */
         if((scroll==SIG)&&(beg<=(max_s-18))){
         /* una l\'{\i}nea para abajo */
             beg++;
         /* habilitar el movimiento */
             scrollok(win, TRUE);
         /* mover */
             wscrl(win, +1);
         /* deshabilitar el movimiento */
             scrollok(win, FALSE);
         /* colocar la cadena de car\'acteres de la \'ultima l\'{\i}nea */
             mvwaddnstr(win,17,0,s[beg+17],66);
         /* limpiar la \'ultima l\'{\i}nea despu\'es del \'ultimo car\'acter ocupado
          * y hasta el fin de l\'{\i}nea.
          * Si no se hace, los atributos se ver\'an mal
          */
             clear_line(66,win);
         }
         else if((scroll==ANT)&&(beg>0)){
             beg--;
             scrollok(win, TRUE);
             wscrl(win, -1);
             scrollok(win, FALSE);
             mvwaddnstr(win,0,0,s[beg],66);
             clear_line(66,win);
         }
         wrefresh(win);
       return;
       }

 8.14      Pads


     o WINDOW  *newpad(nlines,  ncols)



     o WINDOW  *subpad(orig,  nlines,  ncols,  begy,  begx)

118                     CAPITULO 8.  GRAFICOS EN MODO CARACTER



   o  int  prefresh(pad,  pminrow,  pmincol,  sminrow,  smincol,
      smaxrow,  smaxcol)
   o  int  pnoutrefresh(pad,  pminrow,  pmincol,  sminrow,  smincol,
      smaxrow,  smaxcol)
   o  int  pechochar(pad,  ch)


8.15      Soft-labels


   o  int  slk_init(int  fmt)
   o  int  slk_set(int  labnum,  char  *label,  int  fmt)
   o  int  slk_refresh()
   o  int  slk_noutrefresh()
   o  char  *slk_label(int  labnum)
   o  int  slk_clear()
   o  int  slk_restore()
   o  int  slk_touch()
   o  int  slk_attron(chtype  attr)
      int  slk_attrset(chtype  attr)
      int  slk_attroff(chtype  attr)
      Estas  funciones  corresponden  a  attron(attr),  attrset(attr)  y  at-
      troff(attr). No se han construido aun.
8.16      Miscelanea


   o  int  beep()
   o  int  flash()

8.17.  ACCESO DE BAJO NIVEL                                          119



    o char  *unctrl(chtype  c)
    o char  *keyname(int  c)
    o int  filter()
      (Nota: no implementado aun.)


    o void  use_env(bf)
    o int  putwin(WINDOW  *win,  FILE  *filep)
      (Nota: no implementado aun.)


    o WINDOW  *getwin(FILE  *filep)
      (Nota: no implementado aun.)


    o int  delay_output(int  ms)
    o int  flushinp()


8.17      Acceso de Bajo Nivel


    o int  def_prog_mode()
    o int  def_shell_mode()
    o int  reset_prog_mode()
    o int  reset_shell_mode()
    o int  resetty()
    o int  savetty()
    o int  ripoffline(int  line,  int  (*init)(WINDOW  *,  int))
    o int  napms(int  ms)

120                     CAPITULO 8.  GRAFICOS EN MODO CARACTER



8.18      Volcado de Pantalla


   o  int  scr_dump(char  *filename)
      (Nota: no implementado aun.)


   o  int  scr_restore(char  *filename)
      (Nota: no implementado aun.)


   o  int  scr_init(char  *filename)
      (Nota: no implementado aun.)


   o  int  scr_set(char  *filename)
      (Nota: no implementado aun.)
8.19      Emulacion Termcap


   o  int  tgetent(char  *bp,  char  *name)
   o  int  tgetflag(char  id[2])
   o  int  tgetnum(char  id[2])
   o  char  *tgetstr(char  id[2],  char  **area)
   o  char  *tgoto(char  *cap,  int  col,  int  row)
   o  int  tputs(char  *str,  int  affcnt,  int  (*putc)())


8.20      Funciones Terminfo


   o  int  setupterm(char  *term,  int  fildes,  int  *errret)
   o  int  setterm(char  *term)
   o  int  set_curterm(TERMINAL  *nterm)
   o  int  del_curterm(TERMINAL  *oterm)
   o  int  restartterm(char  *term,  int  fildes,  int  *errret)
      (Nota: no implementado aun.)

8.21.  FUNCIONES DE DEPURADO                                       121



    o char  *tparm(char  *str,  p1,  p2,  p3,  p4,  p5,  p6,  p7,  p8,  p9)
      p1 - p9 long int.


    o int  tputs(char  *str,  int  affcnt,  int  (*putc)(char))
    o int  putp(char  *str)
    o int  vidputs(chtype  attr,  int  (*putc)(char))
    o int  vidattr(chtype  attr)
    o int  mvcur(int  oldrow,  int  oldcol,  int  newrow,  int  newcol)
    o int  tigetflag(char  *capname)
    o int  tigetnum(char  *capname)
    o int  tigetstr(char  *capname)


8.21      Funciones de Depurado


    o void  _init_trace()
    o void  _tracef(char  *,  ...)
    o char  *_traceattr(mode)
    o void  traceon()
    o void  traceoff()


8.22      Atributos Terminfo


8.22.1     Atributos Logicos

Variable               NombreCod. Descripcion
                         Car.   Int.
auto_left_margin         bw      bw   cub1 ajusta de la columna 0 a la ultima

122                     CAPITULO 8.  GRAFICOS EN MODO CARACTER



auto_right_margin       am      am   La terminal tiene margenes automaticos
back_color_erase         bce    ut   Borrado de pantalla con el color del segundo plano
can_change              ccc    cc   La terminal puede redefinir los colores existentes
ceol_standout_glitch     xhp    xs   Los caracteres destacados no se borran al sobresecibirse (hp)
col_addr_glitch          xhpa   YA   Solo se permite movimientos positivos para hpa/mhpa
cpi_changes_res          cpix   YF   El cambio de la frecuencia de puntos de un caracter cambia la resolucion
cr_cancels_micro_mode   crxm   YB   El uso del retorno de carro (cr) sale del modo micro
eat_newline_glitch       xenl   xn   El caracter de nueva linea (nl) se ignora pasadas las 80 cols (Concepto)
erase_overstrike          eo      eo   Se pueden borrar los sobreimpresionados con un blanco
generic_type             gn      gn   Tipo de linea genererico (p.ej., llamada, conmutador).
hard_copy               hc      hc   La terminal que produce copia sobre papel
hard_cursor             chts   HC   El cursor es dificil de ver
has_meta_key            km      km   Tiene la tecla meta (shift activa el bit de paridad)
has_print_wheel          daisy  YC   La impresora necesita un operador para cambiar de conjunto de caracteres
has_status_line          hs      hs   Tiene "linea de estado" extra
hue_lightness_saturation hls    hl   La terminal utiliza unicamente la notacion HLS para color
                                     (Tektronix)
insert_null_glitch         in      in   El modo de insercion distingue los caracteres nulos
lpi_changes_res          lpix   YG   El cambio del la frecuencia de puntos de la linea cambia la resolucion
memory_above          da      da   Puede almacenar informacion encima de la pantalla
memory_below          db      db   Puede almacenar informacion debajo de la pantalla
move_insert_mode       mir    mi   No es arriesgado moverse durante la insercion
move_standout_mode    msgr   ms   No es arriesgado moverse en los modo destacados
needs_xon_xoff           nxon   nx   No vale rellenar, se requiere xon/xoff
no_esc_ctl_c              xsb    xb   Colmena (f1=escape, f2=ctrl C)
non_rev_rmcup          nrrmc  NR   smcup no deshace rmcup
no_pad_char             npc    NP   No existe caracter de relleno
non_dest_scroll_region    ndscr  ND   La region de paginado no destruye la informacion
over_strike              os      os   La terminal sobreescribe
prtr_silent               mc5i   5i   La impresora no envia eco a la pantalla
row_addr_glitch          xvpa   YD   Solo se permite movimientos positivos para vhp/mvpa
semi_auto_right_margin  sam    YE   La escritura en la ultima columna resulta en un returno de carro
status_line_esc_ok        eslok  es   Se puede usar el caracter de escape en la linea de estado
dest_tabs_magic_smso    xt      xt   Los tabuladores arrunian, magic so char (Teleray 1061)
tilde_glitch              hz      hz   Hazel-tine; no se puede imprimir el simbolo de tilde
transparent_underline    ul      ul   El caracter de subrayado se sobreescribe
xon_xoff                 xon    xo   La terminal usa el protocolo xon/xoff

8.22.2    Numeros

Variable             NombreCod. Descripcion
                      Car.    Int.
bit_image_entwining   bitwin  Yo   No esta documentado en el SYSV
buffer_capacity        bufsz   Ya   Numero de bytes almacenados antes de imprimir
columns              cols    co   Numero de columnas por linea
dot_vert_spacing       spinv   Yb   Espaciado horizonal de los puntos en puntos por pulgada
dot_horz_spacing      spinh   Yc   Espaciado vertical de pines en pines por pulgada
init_tabs              it       it   Tabuladores iniclamente cada # de espacios
label_height           lh       lh   Filas por etiqueta
label_width           lw       lw   Columnas por etiqueta
lines                  lines   li   Numero de lineas por pantalla o pagina
lines_of_memory       lm       lm   Numero de lineas en la memoria. 0 indica que varia.

8.22.  ATRIBUTOS TERMINFO                                            123



magic_cookie_glitch    xmc      sg   Numero de espacios que producen smso o rmso
max_colors            colors  Co   Maximo numero de colores en la pantalla
max_micro_address    maddr   Yd   Valor maximo de las micro_... direcciones
max_micro_jump      mjump   Ye   Valor maximo de parm_..._micro
max_pairs             pairs   pa   Numero maximo de parejas de color en la pantalla
micro_col_size         mcs      Yf   Tama"no del paso de caracter en modo micro
micro_line_size        mls      Yg   Tama"no del paso de linea en modo micro
no_color_video         ncv      NC   Atributos de video que no se pueden usar en colores
number_of_pins        npins   Yh   Numero de pines en la cabeza de impresion
num_labels            nlab    Nl   Numero de etiquetas en la pantalla
output_res_char       orc      Yi   Resolucion horizontal en unidades por linea
output_res_line        orl      Yj   Resolucion vertical en unidades por linea
output_res_horz_inch   orhi    Yk   Resolucion horizontal en unidades por pulgada
output_res_vert_inch   orvi    Yl   Resolucion vertical en unidades por pulgada
padding_baud_rate    pb       pb   Minimo numero de baudios que necesita relleno de cr/nl
virtual_terminal       vt       vt   Numero de terminal virtual (Sistema UNIX)
width_status_line      wsl      ws   Numero de columnas en la linea de estado
(Los siguientes atributos numericos estan presentes en la estructura term del SYSV,
pero no se han documentado aun en la pagina de manual. Los comentarios provienen
del fichero de cabecera que contiene la definicion de la estructura.)


bit_image_type        bitype  Yp   Tipo de dispositivo de imagenes por bit
buttons               btns    BT   Numero de botones por raton
max_attributes        ma       ma   Numero maximo de atributos que la terminal puede manejar
maximum_windows    wnum    MW   Numero maximo de vetanas definibles
print_rate             cps      Ym   Velocidad de impresion en caracteres por segundo
wide_char_size         widcs   Yn   Tama"no del paso de un caracter en modo doble ancho
8.22.3     Cadenas

Variable                Nombre Cod. Descripcion
                          Car.     Int.
acs_chars                 acsc      ac   Parejas de conjuntos de caracteres graficos - por defecto vt100
alt_scancode_esc          scesa    S8   Escape alternativo para emulacion del codigo escaneado
                                         (por defecto setoma vt100)
back_tab                 cbt       bt   Tabulador inverso (P)
bell                      bel       bl   Se"nal audible (timbre) (P)
bit_image_repeat          birep    Xy   Repetir la celula de imagen por bits #1, #2 veces
                                         (usar tparm)
bit_image_newline        binel    Zz   Desplazarse hasta la siguiente fila de la imagen por bits
                                         (usar tparm)
bit_image_carriage_return bicr      Yv   Desplazarse hasta el comienzo de esta fila
                                         (usar tparm)
carriage_return           cr        cr   Retorno de carro (P*)
change_char_pitch         cpi       ZA   Cambia # de caracteres por pulgada
change_line_pitch         lpi       ZB   Cambia # de lineas por pulgada
change_res_horz           chr       ZC   Cambia la resolucion horizontal
change_res_vert           cvr       ZD   Cambia la resolucion vertical

124                     CAPITULO 8.  GRAFICOS EN MODO CARACTER



change_scroll_region       csr       cs   Cambia de las lineas #1 a la #2
                                        (vt100) (PG)
char_padding             rmp       rP   Como ip pero cuando se esta en modo insercion
char_set_names           csnm      Zy   Lista de los nombres de conjuntos de caracteres
clear_all_tabs             tbc       ct   Borra todos las paradas del tabulador (P)
clear_margins             mgc       MC   Borra todos los margenes (superior, inferior y laterales)
clear_screen              clear    cl   Borra la pantalla y desplaza el cursor al comienzo (P*)
clr_bol                   el1       cb   Borra hasta el comienzo de la linea
clr_eol                    el        ce   Borra hasta el final de la linea (P)
clr_eos                   ed        cd   Borra hasta el final de la pantalla (P*)
code_set_init              csin      ci   Secuencia de inicio para conjuntos de codigos multiples
color_names              colornm  Yw   Da un nombre al color #1
column_address           hpa       ch   Fija la columna del cursor (PG)
command_character       cmdch    CC   Caracter de cmd se puede fijar por la terminal en el prototipo
cursor_address            cup       cm   Desplazamiento relativo del cursor fila #1 columna #2
                                        (PG)
cursor_down              cud1      do   Baja una linea
cursor_home              home      ho   Desplaza el cursor al inicio (sin cup)
cursor_invisible           civis    vi   Hace el cursor invisible
cursor_left                cub1      le   Mueve el cursor un caracter hacia la izquierda
cursor_mem_address      mrcup    CM   Direccionamiento relativo del cursor a traves de memoria
cursor_normal            cnorm    ve   Vuelve el cursor a modo normal (deshace vs/vi)
cursor_right              cuf1      nd   Espacio no destructivo (cursor a la derecha)
cursor_to_ll               ll        ll   Ultima linea, primera columna (sin cup)
cursor_up                cuu1      up   Subir linea (cursor hacia arriba)
cursor_visible             cvvis    vs   Hacer el cursor muy visible
define_bit_image_region   defbi    Yx   Definir region de imagen de bits rectangular
                                        (usar tparm)
define_char               defc      ZE   Definir caracter en conjunto de caracteres
delete_character          dch1      dc   Borrar caracter (P*)
delete_line                dl1       dl   Borrar linea (P*)
device_type               devt      dv   Indica soporte de idioma/conjuto de codigo
dis_status_line            dsl       ds   Desactiva linea de estado
display_pc_char           dispc    S1   Imprime el caracter pc
down_half_line            hd        hd   Baja media linea (1/2 avance de linea hacia delante)
ena_acs                   enacs    eA   activa conjunto de car. altern.
end_bit_image_region      endbi    Yy   Fin de region de imagen por bits (usar tparm)
enter_alt_charset_mode    smacs    as   Comienza un conjunto de caracteres alternativo (P)
enter_am_mode           smam      SA   Activa margenes automaticos
enter_blink_mode         blink    mb   Activa caracteres intermitentes
enter_bold_mode          bold      md   Activa el modo negrita(de brillo extra)
enter_ca_mode            smcup    ti   Cadena al principio de los programas que usen cup
enter_delete_mode        smdc      dm   Modo de borrado (avtivado)
enter_dim_mode          dim       mh   Activa el modo de menor brillo
enter_doublewide_mode   swidm    ZF   Activa el modo de doble ancho
enter_draft_quality        sdrfq    ZG   Activa el modo de calidad de borrador
enter_insert_mode         smir      im   Activa el modo de insercion (activado);
enter_italics_mode        sitm      ZH   Activa el modo en cursiva
enter_leftward_mode      slm       ZI   Activa el movimiento del carro hacia la izquierda
enter_micro_mode         smicm    ZJ   Activa los atributos de micro-movimiento
enter_near_letter_quality  snlq      ZK   Activa impresion NLQ
enter_normal_quality      snrmq    ZL   Activa modo de impresion de calidad normal
enter_pc_charset_mode    smpch    S2   Activa el modo de impresion del conjunto de caracteres PC

8.22.  ATRIBUTOS TERMINFO                                            125



enter_protected_mode     prot      mp   Activa el modo protegido
enter_reverse_mode       rev       mr   Activa el modo de video inverso
enter_scancode_mode     smsc      S4   Activa el modo de codigos de escaneado de PC
enter_secure_mode        invis    mk   Activa el modo vacio (caracteres invisibles)
enter_shadow_mode       sshm      ZM   Activa la impresion en modo de sombra
enter_standout_mode     smso      so   Activa el modo destacado
enter_subscript_mode     ssubm    ZN   Activa el modo de subindice
enter_superscript_mode   ssupm    ZO   Activa el modo de superindice
enter_underline_mode     smul      us   Comienza el modo de subrayado
enter_upward_mode       sum       ZP   Permite el movimiento hacia arriba del carro
enter_xon_mode          smxon    SX   Activa el protocolo xon/xoff
erase_chars               ech       ec   Borra #1 caracteres (PG)
exit_alt_charset_mode     rmacs    ae   Fin de conjunto de caracteres alternativo (P)
exit_am_mode            rmam      RA   Desactiva los margenes automaticos
exit_attribute_mode       sgr0      me   Desactiva todos los atributos
exit_ca_mode             rmcup    te   Cadena para terminar los programas que usan cup
exit_delete_mode          rmdc      ed   Fin del modo de borrado
exit_doublewide_mode    rwidm    ZQ   Desactiva la impresion en doble ancho
exit_insert_mode          rmir      ei   Fin del modo de insercion
exit_italics_mode          ritm      ZR   Desactiva la impresion de cursiva
exit_leftward_mode       rlm       ZS   Activa el movimiento del carro (normal) hacia
                                         la derecha
exit_micro_mode          rmicm    ZT   Desactiva la capacidad de micro movimiento
exit_pc_charset_mode     rmpch    S3   Desactiva la impresion de caracteres PC
exit_scancode_mode       rmsc      S5   Desactiva el modo de escaneado de codigos PC
exit_shadow_mode        rshm      ZU   Deactiva la impresion en modo sombra
exit_standout_mode       rmso      se   Fin del modo destacado
exit_subscript_mode      rsubm    ZV   Desatciva la impresion de subindices
exit_superscript_mode     rsupm    ZW   Desatciva la impresion de superindices
exit_underline_mode      rmul      ue   Fin del modo de subrayado
exit_upward_mode        rum       ZX   Permite el movimiento del carro (normal) hacia
                                         abajo
exit_xon_mode            rmxon    RX   Desactiva el protocolo xon/xoff
flash_screen              flash    vb   Timbre visible (puede que no mueva el cursor)
form_feed                ff        ff   Expulsion de pagina en terminal de impresion (P*)
from_status_line          fsl       fs   Retorno desde la linea de estado
init_1string               is1       i1   Cadena de inicializacion de la terminal
init_2string               is2       i2   Cadena de inicializacion de la terminal
init_3string               is3       i3   Cadena de inicializacion de la terminal
init_file                   if        if   Nombre del fichero que contiene es
init_prog                 iprog    iP   Ruta del programa de inicio
initialize_color            initc    Ic   Inicia la definicion de color
initialize_pair             initp    Ip   Inicializa una pareja de colores
insert_character           ich1      ic   A"nadir caracter (P)
insert_line                il1       al   A"nadir una linea vacia (P*)
insert_padding            ip        ip   A"nadir relleno despues de caracter nuevo (p*)
key_a1                   ka1       K1   Superior izquierda en el teclado numerico
key_a3                   ka3       K3   Superior derecha en el teclado numerico
key_b2                   kb2       K2   Centro del teclado numerico
key_backspace            kbs       kb   Enviado por el retroceso
key_beg                  kbeg      1     Tecla de comienzo
key_btab                 kcbt      kB   Tabulador inverso
key_c1                   kc1       K4   Inferior izquierda en el teclado numerico

126                     CAPITULO 8.  GRAFICOS EN MODO CARACTER



key_c3                   kc3       K5   Inferior derecha en el teclado numerico
key_cancel                kcan      2     Tecla de cancelacion
key_catab                ktbc      ka   Enviado por la tecla de borrado de tabuladores
key_clear                 kclr      kC   Enviado por el borrado de pantalla o la tecla de borrado
key_close                 kclo      3     Tecla de cerrado
key_command            kcmd      4     Tecla de orden
key_copy                 kcpy      5     Tecla de copiado
key_create                kcrt      6     Tecla de creacion
key_ctab                 kctab    kt   Enviado por borrado de tabulador
key_dc                   kdch1    kD   Enviado por la tecla de borrado de caracter
key_dl                    kdl1      kL   Enviado por la tecla de borrado de linea
key_down                kcud1    kd   Enviado por la flecha hacia abajo
key_eic                   krmir    kM   Enviado por rmir o smir en modo de insercion
key_end                  kend      7     Fin
key_enter                 kent      8     enter/envio
key_eol                   kel       kE   Enviado por borrado hasta final de linea
key_eos                   ked       kS   Enviado por borrado hasta fin de pantalla
key_exit                  kext      9     Tecla de salida


key_f0     kf0  k0   Tecla de funcion F00 key_f32    kf32 FM   Tecla de funcion F32
key_f1     kf1  k1   Tecla de funcion F01 key_f33    kf33 FN   Tecla de funcion F33
key_f2     kf2  k2   Tecla de funcion F02 key_f34    kf34 FO   Tecla de funcion F34
key_f3     kf3  k3   Tecla de funcion F03 key_f35    kf35 FP   Tecla de funcion F35
key_f4     kf4  k4   Tecla de funcion F04 key_f36    kf36 FQ   Tecla de funcion F36
key_f5     kf5  k5   Tecla de funcion F05 key_f37    kf37 FR   Tecla de funcion F37
key_f6     kf6  k6   Tecla de funcion F06 key_f38    kf38 FS   Tecla de funcion F38
key_f7     kf7  k7   Tecla de funcion F07 key_f39    kf39 FT   Tecla de funcion F39
key_f8     kf8  k8   Tecla de funcion F08 key_f40    kf40 FU   Tecla de funcion F40
key_f9     kf9  k9   Tecla de funcion F09 key_f41    kf41 FV   Tecla de funcion F41
key_f10    kf10 k;   Tecla de funcion F10 key_f42    kf42 FW   Tecla de funcion F42
key_f11    kf11 F1   Tecla de funcion F11 key_f43    kf43 FX   Tecla de funcion F43
key_f12    kf12 F2   Tecla de funcion F12 key_f44    kf44 FY   Tecla de funcion F44
key_f13    kf13 F3   Tecla de funcion F13 key_f45    kf45 FZ   Tecla de funcion F45
key_f14    kf14 F4   Tecla de funcion F14 key_f46    kf46 Fa   Tecla de funcion F46
key_f15    kf15 F5   Tecla de funcion F15 key_f47    kf47 Fb   Tecla de funcion F47
key_f16    kf16 F6   Tecla de funcion F16 key_f48    kf48 Fc   Tecla de funcion F48
key_f17    kf17 F7   Tecla de funcion F17 key_f49    kf49 Fd   Tecla de funcion F49
key_f18    kf18 F8   Tecla de funcion F18 key_f50    kf50 Fe   Tecla de funcion F50
key_f19    kf19 F9   Tecla de funcion F19 key_f51    kf51 Ff   Tecla de funcion F51
key_f20    kf20 FA   Tecla de funcion F20 key_f52    kf52 Fg   Tecla de funcion F52
key_f21    kf21 FB   Tecla de funcion F21 key_f53    kf53 Fh   Tecla de funcion F53
key_f22    kf22 FC   Tecla de funcion F22 key_f54    kf54 Fi   Tecla de funcion F54
key_f23    kf23 FD   Tecla de funcion F23 key_f55    kf55 Fj   Tecla de funcion F55
key_f24    kf24 FE   Tecla de funcion F24 key_f56    kf56 Fk   Tecla de funcion F56
key_f25    kf25 FF   Tecla de funcion F25 key_f57    kf57 Fl   Tecla de funcion F57
key_f26    kf26 FG   Tecla de funcion F26 key_f58    kf58 Fm   Tecla de funcion F58
key_f27    kf27 FH   Tecla de funcion F27 key_f59    kf59 Fn   Tecla de funcion F59
key_f28    kf28 FI   Tecla de funcion F28 key_f60    kf60 Fo   Tecla de funcion F60
key_f29    kf29 FJ   Tecla de funcion F29 key_f61    kf61 Fp   Tecla de funcion F61
key_f30    kf30 FK   Tecla de funcion F30 key_f62    kf62 Fq   Tecla de funcion F62
key_f31    kf31 FL   Tecla de funcion F31 key_f63    kf63 Fr   Tecla de funcion F63


key_find                  kfnd      0     Tecla de busqueda

8.22.  ATRIBUTOS TERMINFO                                            127



key_help                  khlp      %1   Tecla de ayuda
key_home                khome    kh   Enviado por la tecla de Inicio
key_ic                    kich1    kI   Enviado por la tecla de Insercion
key_il                    kil1      kA   Enviado por insertar linea
key_left                  kcub1    kl   Enviado por la flecha izquierda
key_ll                    kll       kH   Enviado por la tecla home-down
key_mark                 kmrk      %2   Tecla de marcar
key_message              kmsg      %3   Tecla de mensaje
key_move                 kmov      %4   Tecla de movimiento
key_next                 knxt      %5   Tecla "siguiente"
key_npage                knp       kN   Enviado por la tecla de pagina siguiente
key_open                 kopn      %6   Tecla de apertura
key_options               kopt      %7   Tecla de opciones
key_ppage                kpp       kP   Enviado por la tecla de pagina previa
key_previous              kprv      %8   Tecla previa
key_print                 kprt      %9   Tecla de impresion
key_redo                 krdo      %0   Tecla de repeticion
key_reference             kref      &1   Tecla de referencia
key_refresh               krfr      &2   Tecla de refresco
key_replace               krpl      &3   Tecla de reemplazamiento
key_restart               krst      &4   Tecla de reinicio
key_resume               kres      &5   Tecla de continuacion
key_right                 kcuf1    kr   Enviado por la tecla de flecha derecha
key_save                  ksav      &6   Tecla de grabado
key_sbeg                 kBEG      &9   Mayus. + tecla de comienzo
key_scancel               kCAN      &0   Mayus. + cancelacion
key_scommand           kCMD      *1   Mayus. + tecla de orden
key_scopy                kCPY      *2   Mayus. + tecla de copiado
key_screate               kCRT      *3   Mayus. + tecla de creacion
key_sdc                  kDC       *4   Mayus. + suprimir
key_sdl                   kDL       *5   Mayus. + suprimir linea
key_select                kslt      *6   Tecla de seleccion
key_send                 kEND      *7   Mayus. + fin
key_seol                  kEOL      *8   Mayus. + final de linea
key_sexit                 kEXT      *9   Mayus. + salida
key_sf                    kind      kF   Enviado por la tecla de avance
key_sfind                 kFND      *0   Mayus. + tecla de busqueda
key_shelp                 kHLP      #1   Mayus. + tecla de ayuda
key_shome                kHOM      #2   Mayus. + inicio
key_sic                   kIC       #3   Mayus. + tecla de insercion
key_sleft                  kLFT      #4   Mayus. + izquierda
key_smessage             kMSG      %a   Mayus. + tecla de mensaje
key_smove                kMOV      %b   Mayus. + tecla de movimiento
key_snext                kNXT      %c   Mayus. + "siguiente"
key_soptions              kOPT      %d   Mayus. + tecla de opciones
key_sprevious             kPRV      %e   Mayus. + previo
key_sprint                kPRT      %f   Mayus. + tecla de impresion
key_sr                    kri       kR   Enviado por la tecla de desplazamiento hacia atras
key_sredo                 kRDO      %g   Mayus. + tecla de repeticion
key_sreplace              kRPL      %h   Mayus. + tecla de substitucion
key_sright                kRIT      %i   Mayus. + derecha
key_srsume               kRES      %j   Mayus. + tecla de continuacion
key_ssave                 kSAV      !1   Mayus. + tecla de grabado

128                     CAPITULO 8.  GRAFICOS EN MODO CARACTER



key_ssuspend             kSPD      !2   Mayus. + tecla de suspension
key_stab                 khts      kT   Enviado por la tecla de fijacion de tabulador
key_sundo                kUND      !3   Mayus. + deshacer
key_suspend              kspd      &7   Suspension
key_undo                 kund      &8   Deshacer
key_up                   kcuu1    ku   Enviado por la flecha hacia arriba
keypad_local              rmkx      ke   Salida del modo de transmision de teclas numericas
keypad_xmit              smkx      ks   Poner la terminal en modo de transmision de teclas numericas
lab_f0                    lf0       l0   Etiqueta de la funcion f0 si no es f0
lab_f1                    lf1       l1   Etiqueta de la funcion f1 si no es f1
lab_f2                    lf2       l2   Etiqueta de la funcion f2 si no es f2
lab_f3                    lf3       l3   Etiqueta de la funcion f3 si no es f3
lab_f4                    lf4       l4   Etiqueta de la funcion f4 si no es f4
lab_f5                    lf5       l5   Etiqueta de la funcion f5 si no es f5
lab_f6                    lf6       l6   Etiqueta de la funcion f6 si no es f6
lab_f7                    lf7       l7   Etiqueta de la funcion f7 si no es f7
lab_f8                    lf8       l8   Etiqueta de la funcion f8 si no es f8
lab_f9                    lf9       l9   Etiqueta de la funcion f9 si no es f9
lab_f10                   lf10      la   Etiqueta de la funcion f10 si no es f10
label_on                  smln      LO   Activa las etiquetas software
label_off                  rmln      LF   Desactiva las etiquetas software
meta_off                  rmm       mo   Desactiva el modo "meta"
meta_on                  smm       mm   Activa el modo "meta" (8 bit)
micro_column_address     mhpa      ZY   Igual que column_address for micro adjustment
micro_down              mcud1    ZZ   Igual que cursor_down for micro adjustment
micro_left                mcub1    Za   Igual que cursor_left for micro adjustment
micro_right               mcuf1    Zb   Igual que cursor_right for micro adjustment
micro_row_address        mvpa      Zc   Igual que row_address for micro adjustment
micro_up                 mcuu1    Zd   Igual que cursor_up for micro adjustment
newline                  nel       nw   Nueva linea (equivale a cr seguido de lf)
order_of_pins             porder   Ze   Matches software buts to print-head pins
orig_colors                oc        oc   Resetea todas las parejas de color
orig_pair                 op        op   Vuelve a establecer la pareja de color por defecto a su valor original
pad_char                 pad       pc   Caracter de relleno (en vez del nulo)
parm_dch                dch       DC   Borra #1 caracteres (PG*)
parm_delete_line          dl        DL   Borra #1 lineas (PG*)
parm_down_cursor        cud       DO   Desplaza el cursor hacia abajo #1 lineas (PG*)
parm_down_micro         mcud      Zf   Igual que cud para micro ajustes
parm_ich                 ich       IC   A"nadir #1 caracteres vacios (PG*)
parm_index               indn      SF   Avanza #1 lineas (PG)
parm_insert_line          il        AL   A"nadir #1 lineas vacias (PG*)
parm_left_cursor          cub       LE   Mueve el cursor hacia la izquierda #1 espacios (PG)
parm_left_micro          mcub      Zg   Igual que cul para micro ajustes
parm_right_cursor        cuf       RI   Mueve el cursor hacia la derecha #1 espacios (PG*)
parm_right_micro         mcuf      Zh   Igual que cuf para micro ajustes
parm_rindex              rin       SR   Retrocede #1 lineas (PG)
parm_up_cursor           cuu       UP   Mueve el cursor #1 lineas hacia arriba (PG*)
parm_up_micro           mcuu      Zi   Igual que cuu para micro ajustes
pkey_key                 pfkey    pk   Programa funcion #1 para imprimir la cadena #2
pkey_local                pfloc    pl   Programa funcion #1 para ejecutar la cadena #2
pkey_xmit                pfx       px   Programa funcion #1 para transmitir la cadena #2
pkey_plab                pfxl      xl   Programa la tecla #1 para transmitir #2 e imprimir #3
plab_norm                pln       pn   Programa la etiqueta #1 para imprimir la cadena #2

8.22.  ATRIBUTOS TERMINFO                                            129



print_screen              mc0       ps   Imprime el contenido de la pantalla
prtr_non                 mc5p      pO   Activa la impresora para #1 bytes
prtr_off                   mc4       pf   Desacitva la impresora
prtr_on                   mc5       po   Activa la impresora
repeat_char               rep       rp   Repite el caracter #1 #2 veces. (PG*)
req_for_input             rfi       RF   Peticion de entrada
reset_1string              rs1       r1   Pone la terminal el modos normales.
reset_2string              rs2       r2   Pone la terminal el modos normales.
reset_3string              rs3       r3   Pone la terminal el modos normales.
reset_file                 rf        rf   Nombre del fichero con la cadena de reset
restore_cursor            rc        rc   Devuelve el cursor a la posicion del ultimo sc
row_address              vpa       cv   Posicion vertical absoluta (fija la fila) (PG)
save_cursor               sc        sc   Salvado del cursor (P)
scancode_escape          scesc    S7   Escape para la emulacion de codigo de escaneado
scroll_forward            ind       sf   Avanza el texto hacia arriba (P)
scroll_reverse             ri        sr   Avanza el texto hacia abajo (P)
select_char_set            scs       Zj   Selecciona el codigo de caracteres
set0_des_seq              s0ds      s0   Utilizar el conjunto de codigos 0 (EUC conjunto 0, ASCII)
set1_des_seq              s1ds      s1   Utilizar el conjunto de codigos 1
set2_des_seq              s2ds      s2   Utilizar el conjunto de codigos 2
set3_des_seq              s3ds      s3   Utilizar el conjunto de codigos 3
set_a_background         setab    AB   Fijar el color del segundo plano usando una secuencia de escape ANSI
set_a_foreground          setaf    AF   Fijar el color del primer plano usando una secuencia de escape ANSI
set_attributes             sgr       sa   Definir los atributos de video (PG9)
set_background           setb      Sb   Fijar el color del segundo plano
set_bottom_margin        smgb      Zk   Fijar el margen inferior en esta linea
set_bottom_margin_parm  smgbp    Zl   Fijar el margen inferior en la linea #1 o a #2
                                         lineas del final
set_color_band            setcolor Yz   Cambia a la cinta de color #1
set_color_pair             scp       sp   Fijar la pareja de colores
set_foreground            setf      Sf   Fijar el color del primer plano
set_left_margin           smgl      ML   Fijar el margen izquierdo en esta columna
set_left_margin_parm      smglp    Zm   Fijar el margen izquierdo (derecho) en #1 (#2)
set_lr_margin             smglr    ML   Fijar los margenes izquierdo y derecho
set_page_length           slines   YZ   Fijar la longitud de la pagina en #1 lineas (usar tparm)
set_right_margin          smgr      MR   Fijar el margen derecho en esta columna
set_right_margin_parm    smgrp    Zn   Fijar el margen derecho en la columna #1
set_tab                   hts       st   Fijar una parada del tabulador en esta columna en todas las filas
set_tb_margin            smgtb    MT   Fijar los margenes superior e inferior
set_top_margin           smgt      Zo   Fijar el margen superior en esta linea
set_top_margin_parm      smgtp    Zp   Fijar el margen superior en la linea #1
set_window               wind      wi   Esta ventana esta entre las lineas #1-#2 y las columnas #3-#4
start_bit_image           sbim      Zq   Comenzar la impresion de imagen de bits
start_char_set_def         scsd      Zr   Comenazar la definicion de un conjunto de caracteres
stop_bit_image            rbim      Zs   Fin de impresion de imagen de bits
stop_char_set_def         rcsd      Zt   Fin de la definicion de un conjunto de caracteres
subscript_characters      subcs    Zu   Lista de caracteres que pueden ser subindices
superscript_characters    supcs    Zv   Lista de caracteres que pueden ser superindices
tab                      ht        ta   Desplazarse hasta la siguiente parada de tabulador (en espacios de a ocho)
these_cause_cr            docr      Zw   Estos caracteres causan un CR
to_status_line             tsl       ts   Desplazarse hasta la linea de estado, columna #1
underline_char            uc        uc   Subrayar un caracter y situarse despues de el
up_half_line               hu        hu   Desplazarse media linea hacia arriba (avance de 1/2 linea inverso)

130                     CAPITULO 8.  GRAFICOS EN MODO CARACTER



xoff_character            xoffc    XF   caracter XON
xon_character            xonc      XN   caracter XOFF


   (Los siguientes atributos de cadena estan presentes en la estructura term del SYSVr,
aunque no estan documentados en la pagina de manual. Los comentarios estan sacados
de fichero de cabecera que define la estructura term.)


label_format              fln       Lf   ??
set_clock                 sclk      SC   Fija el reloj
display_clock             dclk      DK   Imprime el reloj
remove_clock             rmclk    RC   Borra el reloj??
create_window            cwin      CW   Define que la ventana #1 va de #2,#3 a #4,#5
goto_window             wingo    WG   Ir a la ventana #1
hangup                  hup       HU   Colgar el telefono
dial_phone               dial      DI   Marcar el telefono #1
quick_dial                qdial    QD   Marcar el telefono #1, sin detectar
                                        como va la llamada
tone                     tone      TO   Elegir modo de marcado por tonos
pulse                     pulse    PU   Elegir modo de marcado por pulsos
flash_hook                hook      fh   Pulsar rapidamente el interruptor de colgado
fixed_pause               pause    PA   Pausa de 2-3 segundos
wait_tone                 wait      WA   Esperar el tono de marcado
user0                     u0        u0   Cadena de usuario # 0
user1                     u1        u1   Cadena de usuario # 1
user2                     u2        u2   Cadena de usuario # 2
user3                     u3        u3   Cadena de usuario # 3
user4                     u4        u4   Cadena de usuario # 4
user5                     u5        u5   Cadena de usuario # 5
user6                     u6        u6   Cadena de usuario # 6
user7                     u7        u7   Cadena de usuario # 7
user8                     u8        u8   Cadena de usuario # 8
user9                     u9        u9   Cadena de usuario # 9
get_mouse                getm      Gm   Curses deberia responder a los mensajes de botones
key_mouse                kmous    Km   ??
mouse_info               minfo    Mi   Informacion del estado del raton
pc_term_options          pctrm    S6   Opciones de terminal del PC
req_mouse_pos            reqmp    RQ   Peticion de la posicion del raton
zero_motion              zerom    Zx   No desplazarse al detectar el siguiente caracter



8.23      Esquema de las Funciones de [N]Curses


A continuacion se puede ver un resumen de los diferentes paquetes (n)curses. La
primera columna corresponde a la curses de bsd (que forma parte del slackware
2.1.0 y Sun-OS 4.x), en la segunda tenemos la curses del sysv (en Sun-OS 5.4
/Solaris 2) y la tercera es ncurses (version 1.8.6).
   En la cuarta columna se encuentra un referencia a la pagina en la que se
describe la funcion (si es que se describe en algun lado).


   x  el paquete tiene esta funcion.


   n  la funcion no ha sido implementada aun.

8.23.  ESQUEMA DE LAS FUNCIONES DE [N]CURSES                 131



Funcion            BSD  SYSV Nc. Pag.      getmaxx(win)              x      x   116
 _init_trace()                    x   121  getmaxy(win)              x      x   116
 _traceattr(mode)                 x   121  getmaxyx(...)             x      x   116
 _tracef(char *, ...)              x   121 getmouse()                x
addbytes(...)       x                      getnwstr(...)             x
addch(ch)           x      x      x   98   getparyx(...)             x      x   115
addchnstr(...)            x      x   98    getstr(str)         x      x      x   102
addchstr(chstr)           x      x   98    getsyx(...)               x      x   116
addnstr(...)              x      x   98    gettmode()          x      x
addnwstr(...)             x                getwch(...)               x
addstr(str)         x      x      x   98   getwin(...)               x
addwch(...)               x                getwin(FILE *)            x      x,n 119
addwchnstr(...)           x                getwstr(...)              x
addwchstr(...)            x                getyx(...)          x      x      x   115
addwstr(...)              x                halfdelay(t)              x      x   105
adjcurspos()              x                has_colors()               x      x   113
attroff(attr)             x      x   113   has_ic()                  x      x,n 106
attron(attr)              x      x   113   has_il()                  x      x,n 106
attrset(attr)             x      x   113   hline(...)                x      x   100
baudrate()          x      x      x   106  idcok(...)                x      x,n 104
beep()                    x      x   118   idlok(...)          x      x      x   104
bkgd(ch)                  x      x   102   immedok(...)              x      x   104
bkgdset(ch)               x      x   102   inch()             x      x      x   103
border(...)               x      x   100   inchnstr(...)             x      x,n 103
box(...)           x      x      x   100   inchstr(...)              x      x,n 103
can_change_color()         x      x   113  init_color(...)            x      x   114
cbreak()           x      x      x   105   init_pair(...)             x      x   113
clear()            x      x      x   110   initscr()           x      x      x   94
clearok(...)        x      x      x   104  innstr(...)               x      x,n 103
clrtobot()          x      x      x   110  innwstr(...)              x
clrtoeol()          x      x      x   110  insch(c)           x      x      x   99
color_content(...)         x      x   114  insdelln(n)               x      x   99
copywin(...)              x      x   98    insertln()          x      x      x   99
crmode()           x      x      x   105   insnstr(...)              x      x   99
curs_set(bf)               x      x   115  insstr(str)               x      x   99
curserr()                 x                instr(str)                x      x,n 103
def_prog_mode()            x      x   119  inswch(...)               x
def_shell_mode()           x      x   119  inswstr(...)              x
del_curterm(...)           x      x   120  intrflush(...)            x      x   106
delay_output(ms)           x      x   119  inwch(...)                x
delch()            x      x      x   100   inwchnstr(...)            x
deleteln()          x      x      x   100  inwchstr(...)             x
delscreen(...)            x      x,n 95    inwchstr(...)             x
delwin(win)         x      x      x   97   inwstr(...)               x
derwin(...)               x      x   97    is_linetouched(...)        x      x   111
doupdate()                x      x   110   is_wintouched(win)         x      x   111
drainio(int)              x                isendwin()                x      x   95
dupwin(win)               x      x   97    keyname(c)                x      x   119
echo()             x      x      x   105   keypad(...)               x      x   104
echochar(ch)              x      x   99    killchar()          x      x      x   106
echowchar(ch)             x                leaveok(...)        x      x      x   104
endwin()           x      x      x   95    longname()          x      x      x   106
erase()            x      x      x   109   map_button(long)           x
erasechar()         x      x      x   106  meta(...)                 x      x   104
filter()                  x      x   119   mouse_off(long)            x
flash()                   x      x   118   mouse_on(long)             x
flushinp()                x      x   119   mouse_set(long)            x
flushok(...)        x                      move(...)           x      x      x   115
garbagedlines(...)         x               movenextch()              x
garbagedwin(win)           x               moveprevch()              x
getattrs(win)             x      x   113   mvaddbytes(...)     x
getbegyx(...)             x      x   116   mvaddch(...)        x      x      x   98
getbkgd(win)              x                mvaddchnstr(...)           x      x   98
getbmap()                 x                mvaddchstr(...)           x      x   98
getcap(str)         x                      mvaddnstr(...)            x      x   98
getch()            x      x      x   102   mvaddnwstr(...)           x

132                     CAPITULO 8.  GRAFICOS EN MODO CARACTER



mvaddstr(...)       x      x      x   98  mvwinwstr(...)            x
mvaddwch(...)             x               mvwprintw(...)      x      x      x   99
mvaddwchnstr(...)          x              mvwscanw(...)       x      x      x   103
mvaddwchstr(...)           x              mvwvline(...)             x
mvaddwstr(...)            x               napms(ms)                 x      x   119
mvcur(...)          x      x      x   121 newkey(...)               x
mvdelch(...)        x      x      x   100 newpad(...)               x      x   117
mvderwin(...)             x      x,n 97   newscreen(...)            x
mvgetch(...)        x      x      x   102 newterm(...)              x      x   94
mvgetnwstr(...)           x               newwin(...)         x      x      x   95
mvgetstr(...)       x      x      x   102 nl()               x      x      x   104
mvgetwch(...)             x               nocbreak()          x      x      x   105
mvgetwstr(...)            x               nocrmode()          x      x      x   105
mvhline(...)              x               nodelay(...)              x      x   105
mvinch(...)         x      x      x   103 noecho()           x      x      x   105
mvinchnstr(...)           x      x,n 103  nonl()             x      x      x   104
mvinchstr(...)            x      x,n 103  noqiflush()               x      x,n 106
mvinnstr(...)             x      x,n 103  noraw()            x      x      x   105
mvinnwstr(...)            x               notimeout(...)            x      x   106
mvinsch(...)        x      x      x   99  overlay(...)        x      x      x   97
mvinsnstr(...)            x      x   100  overwrite(...)      x      x      x   97
mvinsnwstr(...)           x               pair_content(...)          x      x   114
mvinsstr(...)             x      x   100  pechochar(...)            x      x   118
mvinstr(...)              x      x,n 103  pechowchar(...)           x
mvinswch(...)             x               pnoutrefresh(...)          x      x   118
mvinswstr(...)            x               prefresh(...)             x      x   118
mvinwch(...)              x               printw(...)         x      x      x   99
mvinwchnstr(...)           x              putp(char *)              x      x   121
mvinwchstr(...)           x               putwin(...)               x      x,n 119
mvinwstr(...)             x               qiflush()                 x      x,n 106
mvprintw(...)       x      x      x   99  raw()              x      x      x   105
mvscanw(...)        x      x      x   103 redrawwin(win)            x      x   111
mvvline(...)              x               refresh()           x      x      x   110
mvwaddbytes(...)    x                     request_mouse_pos()        x
mvwaddch(...)       x      x      x   98  reset_prog_mode()          x      x   119
mvwaddchnstr(...)          x      x   98  reset_shell_mode()         x      x   119
mvwaddchstr(...)           x      x   98  resetty()           x      x      x   119
mvwaddnstr(...)           x      x   98   restartterm(...)           x      x,n 120
mvwaddnwstr(...)           x              ripoffline(...)           x      x   119
mvwaddstr(...)      x      x      x   98  savetty()           x      x      x   119
mvwaddwch(...)            x               scanw(...)          x      x      x   103
mvwaddwchnstr(...)         x              scr_dump(char *)           x      x,n 120
mvwaddwchstr(...)          x              scr_init(char *)           x      x,n 120
mvwaddwstr(...)           x               scr_restore(char *)        x      x,n 120
mvwdelch(...)       x      x      x   100 scr_set(char *)            x      x,n 120
mvwgetch(...)       x      x      x   102 scrl(n)                   x      x   116
mvwgetnwstr(...)           x              scroll(win)         x      x      x   116
mvwgetstr(...)      x      x      x   102 scrollok(...)       x      x      x   116
mvwgetwch(...)            x               set_curterm(...)           x      x   120
mvwgetwstr(...)           x               set_term(...)              x      x   95
mvwhline(...)             x               setcurscreen(SCREEN *)     x
mvwin(...)          x      x      x   97  setscrreg(...)            x      x   116
mvwinch(...)        x      x      x   103 setsyx(...)               x      x   116
mvwinchnstr(...)           x      x,n 103 setterm(char *)     x      x      x   120
mvwinchstr(...)           x      x,n 103  setupterm(...)            x      x   120
mvwinnstr(...)            x      x,n 103  slk_attroff(attr)          x      x,n 118
mvwinnwstr(...)           x               slk_attron(attr)           x      x,n 118
mvwinsch(...)       x      x      x   99  slk_attrset(attr)          x      x,n 118
mvwinsnstr(...)           x      x   100  slk_clear()               x      x   118
mvwinsstr(...)            x      x   100  slk_init(fmt)              x      x   118
mvwinstr(...)             x      x,n 103  slk_label(labnum)          x      x   118
mvwinswch(...)            x               slk_noutrefresh()          x      x   118
mvwinswstr(...)           x               slk_refresh()              x      x   118
mvwinwch(...)             x               slk_restore()              x      x   118
mvwinwchnstr(...)          x              slk_set(...)               x      x   118
mvwinwchstr(...)           x              slk_touch()               x      x   118

8.23.  ESQUEMA DE LAS FUNCIONES DE [N]CURSES                 133



standend()          x      x      x   113  wclrtoeol(win)      x      x      x   110
standout()          x      x      x   113  wcursyncup(win)           x      x,n 97
start_color()              x      x   113  wdelch(win)         x      x      x   100
subpad(...)               x      x   117   wdeleteln(win)      x      x      x   100
subwin(...)         x      x      x   97   wechochar(...)            x      x   99
syncok(...)               x      x,n 97    wechowchar(...)           x
termattrs()               x      x,n 106   werase(win)         x      x      x   109
termname()                x      x,n 107   wgetch(win)         x      x      x   102
tgetent(...)              x      x   120   wgetnstr(...)             x      x   102
tgetflag(char [2])         x      x   120  wgetnwstr(...)            x
tgetnum(char [2])          x      x   120  wgetstr(...)        x      x      x   102
tgetstr(...)              x      x   120   wgetwch(...)              x
tgoto(...)                x      x   120   wgetwstr(...)             x
tigetflag(...)            x      x   121   whline()                  x
tigetnum(...)             x      x   121   whline(...)               x
tigetstr(...)             x      x   121   whline(...)               x      x   100
timeout(t)                x      x   105   winch(win)          x      x      x   103
touchline(...)      x      x      x   111  winchnstr(...)            x      x,n 103
touchwin(win)       x      x      x   111  winchstr(...)             x      x,n 103
tparm(...)                x      x   121   winnstr(...)              x      x,n 103
tputs(...)                       x   120   winnwstr(...)             x
traceoff()                x      x   121   winsch(...)         x      x      x   99
traceon()                 x      x   121   winsdelln(...)      x      x      x   99
typeahead(fd)             x      x   106   winsertln(win)            x      x   99
unctrl(chtype c)           x      x   119  winsnstr(...)             x      x   100
ungetch(ch)               x      x   102   winsnwstr(...)            x
ungetwch(c)               x                winsstr(...)              x      x   100
untouchwin(win)           x      x   111   winstr(...)               x      x,n 103
use_env(bf)               x      x   119   winswch(...)              x
vidattr(...)              x      x   121   winswstr(...)             x
vidputs(...)              x      x   121   winwch(...)               x
vidupdate(...)            x                winwchnstr(...)           x
vline(...)                x      x   100   winwchstr(...)            x
vwprintw(...)             x      x   99    winwstr(...)              x
vwscanw(...)              x      x   103   wmouse_position(...)       x
waddbytes(...)      x                      wmove(...)          x      x      x   115
waddch(...)         x      x      x   98   wmovenextch(win)           x
waddchnstr(...)           x      x   98    wmoveprevch(win)           x
waddchstr(...)            x      x   98    wnoutrefresh(win)          x      x   110
waddnstr(...)             x      x   98    wprintw(...)        x      x      x   99
waddnwstr(...)            x                wredrawln(...)            x      x   111
waddstr(...)        x      x      x   98   wrefresh(win)       x      x      x   110
waddwch(...)              x                wscanw(...)         x      x      x   103
waddwchnstr(...)           x               wscrl(...)                x      x   116
waddwchstr(...)           x                wsetscrreg(...)           x      x   116
waddwstr(...)             x                wstandend(win)      x      x      x   113
wadjcurspos(win)           x               wstandout(win)      x      x      x   113
wattroff(...)             x      x   113   wsyncdown(win)            x      x,n 97
wattron(...)              x      x   113   wsyncup(win)              x      x,n 97
wattrset(...)             x      x   113   wtimeout(...)             x      x   105
wbkgd(...)                x      x   102   wtouchln(...)             x      x   111
wbkgdset(...)             x      x   102   wvline()                  x
wborder(...)              x      x   100   wvline(...)               x
wclear(win)         x      x      x   110  wvline(...)               x      x   100
wclrtobot(win)      x      x      x   110


    Continuara...
    Sven Goldt Guia del Programador de Linux

134                     CAPITULO 8.  GRAFICOS EN MODO CARACTER

Capitulo  9


Programacion  de  los  Puertos



de  E/S



Normalmente, un PC tiene al menos dos interfaces serie y una paralelo. Estas
interfaces son dispositivos especiales y se mapean como sigue:


    o =dev=ttyS0 - =dev=ttySn
      estos son los dispositivos serie RS232 0-n donde n depende de su hardware.


    o =dev=cua0 - =dev=cuan
      estos son los dispositivos RS232 0-n donde n depende de su hardware.


    o =dev=lp0 - =dev=lpn
      estos son los dispositivos paralelos 0-n donde n depende de su hardware.


    o =dev=js0 - =dev=jsn
      estos son los dispositivos de joystick 0-n donde 0 <= n <= 1.


    La diferencia entre los dispositivos =dev=ttyS* y =dev=cua* consiste en co-
mo se maneja la llamada a open(2). Se supone que los dispositivos =dev=cua*
se deben usar como dispositivos de llamada saliente y por lo tanto, al invocar a
open(2), reciben parametros por defecto diferentes a los que reciben los dispo-
sitivos =dev=ttyS*, que se inicializan para llamadas entrantes y salientes.  Por
defecto los dispositivos son dispositivos controladores para aquellos procesos
que los abrieron.  Normalmente estos dispositivos especiales deberian mane-
jarse con peticiones ioctl(), pero POSIX prefirio definir nuevas funciones para
manejar los terminales asincronos que dependen fuertemente de la estructura
termios. Ambos metodos requieren que se incluya < termios.h >.


   1. metodo ioctl:
      TCSBRK, TCSBRKP, TCGETA (obtener atributos), TCSETA (poner
      atributos)
      Peticiones de control de E/S de Terminal (TIOC):
      TIOCGSOFTCAR  (obtener  portadora  soft),  TIOCSSOFTCAR  (po-
      ner  portadora  soft),  TIOCSCTTY  (poner  tty  controlador),  TIOCM-
      GET  (obtener  lineas  de  modem),  TIOCMSET  (activar  lineas  de


                                        135

136         CAPITULO 9.  PROGRAMACION DE LOS PUERTOS DE E/S



      modem), TIOCGSERIAL, TIOCSSERIAL, TIOCSERCONFIG, TIOC-
      SERGWILD,  TIOCSERSWILD,  TIOCSERGSTRUCT,  TIOCMBIS,
      TIOCMBIC, ...
   2. metodo POSIX:
      tcgetattr(), tcsetattr(), tcsendbreak(), tcdrain(), tcflush(), tcflow(), tc-
      getpgrp(), tcsetpgrp()
      cfsetispeed(), cfgetispeed(), cfsetospeed(), cfgetospeed()
   3. otros metodos:
      outb,inb para la programacion a bajo nivel, como por ejemplo para usar
      el puerto de la impresora con algo que no sea una impresora.

9.1.  PROGRAMACION DEL RATON                                     137



9.1      Programacion del Raton


Los ratones se conectan o bien a un puerto serie o bien directamente al bus
AT. Diferentes tipos de ratones envian diferentes tipos de datos, lo que hace la
programacion del raton algo mas complicada aun. Pero Andrew Haylett fue tan
amable que puso un copyright generoso en su programa selection, lo que signi-
fica que puede usar estas rutinas de raton para sus propios programas.  Junto
con este manual puede encontrar la version ***release** previa de selection-
1.81 junto con la nota de COPYRIGHT. Por otra parte, X11 ofrece una API
comoda de usar, asi que las rutinas de Andrew se deberian usarunicamente
para aplicaciones que no sean X11. Solo son necesarios los modulos mouse.c y
mouse.h del paquete selection.  Para recibir los eventos del raton basicamente
hay que llamar a ms_init() y get_ms_event(). ms_init necesita los 10 parametos
siguientes:


   1. int acceleration
      es el factor de aceleracion.  Si mueve el raton mas de delta pixels, la
      velocidad de movimiento se incrementa dependiendo de este valor.


   2. int baud
      es la velocidad en bps que usa su raton (normalmente 1200).


   3. int delta
      este es el numero de pixels que debe mover el raton antes de que comience
      la aceleracion.


   4. char *device
      es el nombre de su dispositivo de raton (por ejemplo /dev/mouse)


   5. int toggle
      conmuta la linea de modem del raton DTR, RTS o ambas durante la
      inicializacion (normalmente 0).


   6. int sample
      la resolucion (en dpi) de su raton (normalmente 100).


   7. mouse_type mouse
      el identificador del raton conectado, como P_MSC (Mouse Systems Corp.)
      para mi raton ;).


   8. int slack
      cantidad de elasticidad para el "salto circular"2, lo que significa que si
      slack es -1, un intento de mover el raton mas alla del borde de la pantalla
      dejara el cursor en el borde.  Los valores >= 0 significan que el cursor
      del raton pasara al otro extremo de la pantalla tras mover el raton slack
      pixels contra el borde.
__________________________________1
    N. del T.: en el momento de traducir esto habia una version mas reciente disponible
   2N. del T.: traduccion libre de wraparound, que es sinonimo de word wrapping y se refiere

al salto automatico al otro extremo de la pantalla cuando algo no cabe en un lado de la misma

138         CAPITULO 9.  PROGRAMACION DE LOS PUERTOS DE E/S



   9. int maxx
      la resolucion de su terminal actual en la direccion x. Con el tipo de letra
      por defecto, un caracter tiene una anchura de 10 pixels y por lo tanto la
      resolucion total de la pantalla en x es 10*80-1.


 10.  int maxy
      la resolucion de su terminal actual en la direccion y. Con el tipo de letra
      por defecto, un caracter tiene una altura de 12 pixels y por lo tanto la
      resolucion total de la pantalla en y es 12*25-1.


get_ms_event() necesitaunicamente un puntero a una estructura ms_event.  Si
ocurre un error, get_ms_event() devuelve -1.  Cuando todo va bien, devuelve 0
y la estructura ms_event contiene el estado actual del raton.
9.2     Programacion del Modem


Vease el ejemplo miniterm.c
Usar termios para controlar el puerto RS232.
Usar los comandos Hayes para controlar el modem.
9.3     Programacion de la Impresora


Vease el ejemplo checklp.c
No usar termios para controlar el puerto de la impresora. Usar ioctl e inb/outb
si fuera necesario.
Usar comandos Epson, Postscript, PCL, etc. para controlar la impresora.
< linux=lp.h >
llamadas ioctl: LPCHAR, LPTIME, LPABORT, LPSETIRQ, LPGETIRQ, LP-
WAIT
inb/outb para estado y control del puerto.
9.4     Programacion del Joystick


Vease ejemplo js.c en el paquete del modulo cargable del nucleo para el joystick.
< linux=joystick.h >
llamadas
ioctl: JS_SET_CAL, JS_GET_CAL, JS_SET_TIMEOUT, JS_GET_TIMEOUT,
JS_SET_TIMELIMIT,  JS_GET_TIMELIMIT,  JS_GET_ALL,  JS_SET_ALL.
Una lectura en /dev/jsn devolvera la estructura JS_DATA_TYPE.

Capitulo  10


Conversion  de  Aplicaciones  a



Linux


Matt Welsh
mdw@cs.cornell.edu 26 de Enero de 1995
10.1      Introduccion


La conversion de aplicaciones UNIX al sistema operativo Linux es extremada-
mente facil.  Linux, y la biblioteca GNU C usada por el, han sido dise"nados
con la portabilidad de las aplicaciones en mente, lo que significa que muchas
aplicaciones compilaran con solo ejecutar make. Aquellas que no lo hagan, gene-
ralmente usaran alguna caracteristica oscura de una implementacion particular,
o dependeran fuertemente del comportamiento indocumentado o indefinido de,
por ejemplo, una llamada particular del sistema.

    Linux  obedece  casi  completamente  el  estandar  IEEE  1003.1-1988  (PO-
SIX.1), pero no ha sido certificado como tal.  De igual forma, Linux tambien
implementa muchas de las caracteristicas que se encuentran en las variantes
SVID y BSD de UNIX, pero de nuevo no se adhiere a ellas necesariamente
en todos los casos.  En general, Linux ha sido dise"nado para ser compatible
con otras implementaciones de UNIX, para hacer la conversion de aplicaciones
mas facil, y en ciertas ocasiones ha mejorado o corregido el comportamiento
encontrado en esas implementaciones.

    Como ejemplo, el argumento timeout que se le pasa a la llamada del sis-
tema select() es realmente decrementado por Linux durante la operacion de
sondeo. Otras implementaciones no modifican este valor para nada, y aquellas
aplicaciones que no esperen esto pueden dejar de funcionar cuando se compilen
bajo Linux.  Las paginas del manual de BSD y SunOS para select() avisan de
que en una "implementacion futura", la llamada del sistema puede modificar el
puntero timeout.  Desgraciadamente, muchas aplicaciones todavia presuponen
que el valor permanecera intacto.

    El objetivo de este articulo es proporcionar una vista general de los princi-
pales asuntos asociados a la conversion de aplicaciones a Linux, resaltando las
diferencias entre Linux, POSIX.1, SVID y BSD en las siguientes areas: gestion


                                        139

140         CAPITULO 10.  CONVERSION DE APLICACIONES A LINUX



de se"nales, E/S de terminales, control de procesos y obtencion de informacion
y compilacion portable condicional.
10.2      Gestion de Se"nales


A lo largo de los a"nos la definicion y semantica de las se"nales han sido modi-
ficadas de diferentes formas por diferentes implementaciones de UNIX. Hoy en
dia hay dos clases principales de simbolos: no fiables y fiables.  Las se"nales no
fiables son aquellas para las cuales el gestor de la se"nal no continua instalado
una vez llamado. Esta se"nales "mono-disparo" deben reinstalar el gestor de la
se"nal dentro del propio gestor de la se"nal, si el programa desea que la se"nal
siga instalada.  A causa de esto, existe una condicion de carrera en la cual la
se"nal puede llegar de nuevo antes de que el gestor este reinstalado, lo que puede
hacer que, o bien la se"nal se pierda, o bien que se dispare el comportamiento
original de la se"nal (tal como matar el proceso). Por lo tanto, estas se"nales son
"no fiables" puesto que la captura de la se"nal y la operacion de reinstalacion
del gestor no son atomicas.
   Con la semantica de las se"nales no fiables, las llamadas del sistema no son
reiniciadas automaticamente cuando son interrumpidas por una se"nal.   Por
lo tanto, para que un programa tenga en cuenta todas las posibilidades, es
necesario que el programa compruebe el valor de errno tras cada llamada del
sistema, y reejecute la llamada si su valor es EINTR.
   De forma similar, la semantica de las se"nales no fiables no proporciona una
forma facil de obtener una operacion de pausa atomica (poner un proceso a
dormir hasta que llegue una se"nal).  A causa de la naturaleza no fiable de la
reinstalacion de los gestores de se"nales, hay casos en los cuales la se"nal puede
llegar sin que el programa se de cuenta de ello.
   Por otro lado, con la semantica de las se"nales fiables, el gestor de la se"nal
permanece instalado una vez llamado, y se evita la condicion de carrera. Tam-
bien, ciertas llamadas del sistema puede ser reiniciadas y es posible hacer una
operacion de pausa atomica por medio de la funcion POSIX sigsuspend.



10.2.1    Se"nales en SVR4, BSD, y POSIX.1


La implementacion de se"nales SVR4 incorpora las funciones signal, sigset, sig-
hold, sigrelse, sigignore y sigpause.  La funcion signal bajo SVR4 es identica a
las clasicas se"nales UNIX V7, y proporcionaunicamente se"nales no fiables. Las
otras funciones si proporcionan se"nales con reinstalacion automatica del gestor
de la se"nal, pero no se soporta el reiniciado de las se"nales del sistema.
   Bajo BSD, se soportan las funciones signal, sigvec, sigblock, sigsetmask y
sigpause.  Todas las funciones proporcionan se"nales fiables con reiniciado de
las llamadas del sistema por defecto, pero dicho comportamiento puede ser
inhabilitado a voluntad por el programador.
   Bajo POSIX.1 se proporcionan las funciones sigaction, sigprocmask, sigpen-
ding, y sigsuspend. Notese que no existe la funcion signal y que, de acuerdo con
POSIX.1, debe despreciarse. Estas funciones proporcionan se"nales fiables, pero
no se define el comportamiento de las llamadas del sistema. Si se usa sigaction

10.2.  GESTION DE SEN"ALES                                             141



bajo SVR4 y BSD, el reiniciado de las llamadas del sistema esta deshabilitado
por defecto, pero puede activarse si se especifica el flag de se"nal SA_RESTART.
    Por lo tanto, la "mejor" forma de usar las se"nales en un programa es usar
sigaction, que permite especificar explicitamente el comportamiento de los ges-
tores de se"nales. Sin embargo, todavia hay muchas aplicaciones que usan signal,
y como podemos ver arriba, signal proporciona semanticas diferentes bajo SV4R
y BSD.



10.2.2     Opciones de Se"nales en Linux


En Linux se definen los siguiente valores para el miembro sa_flags de la es-
tructura sigaction.


    o SA_NOCLDSTOP: No enviar SIGCHLD cuando un proceso hijo se detiene.


    o SA_RESTART: Forzar el reiniciado de ciertas llamadas del sistema cuando
      sean interrumpidan por un gestor de se"nal.


    o SA_NOMASK: Deshabilitar la mascara de se"nales (que bloquea las se"nales
      durante la ejecucion de un gestor de se"nales).


    o SA_ONESHOT: Eliminar el gestor de se"nal tras la ejecucion.  Notese que
      SVR4 usa SA_RESETHAND para indicar lo mismo.


    o SA_INTERRUPT: Definido en Linux, pero no usado. Bajo SunOS, las llama-
      das del sistema se reiniciaban automaticamente, y este flag inhabilitaba
      ese comportamiento.


    o SA_STACK: Actualmente una operacion nula, a ser usada por las pilas de
      se"nales.


    Notese que POSIX.1 defineunicamente SA_NOCLDSTOP, y que hay varias
opciones mas definidas por SVR4 que no estan disponibles en Linux. Cuando se
porten aplicaciones que usen sigaction, puede que necesite modificar los valores
de sa_flags para obtener el comportamiento apropiado.



10.2.3     signal  en Linux


En Linux, la funcion signal  es equivalente a usar sigaction con las opciones
SA_ONESHOT y SA_NOMASK. Esto es, corresponde a la semantica clasica de se"nales
no fiables usada en SVR4.
    Si desea que signal use la semantica de BSD, la mayoria de los sistemas
Linux proporcionan una biblioteca de compatibilidad BSD con la cual se puede
enlazar. Para usar esta biblioteca, deberia a"nadir las opciones


   -I/usr/include/bsd  -lbsd


a la linea de ordenes de compilacion. Cuando porte aplicaciones que usen signal,
preste mucha atencion a las suposiciones que hace el programa sobre los gestores
de se"nales y modifique el codigo (o compile con las definiciones apropiadas) para
obtener el comportamiento adecuado.

142         CAPITULO 10.  CONVERSION DE APLICACIONES A LINUX



10.2.4    Se"nales soportadas por Linux


Linux soporta casi todas las se"nales proporcionadas por SVR4, BSD y POSIX,
con algunas excepciones:


   o  SIGEMT no esta soportada. Corresponde a un fallo de hardware en SVR4
      y BSD.


   o  SIGINFO no esta soportada.  Se usa para peticiones de informacion del
      teclado en SVR4.


   o  SIGSYS no esta soportada. Se refiere a una llamada del sistema no valida
      en  SVR4  y  BSD.  Si  enlaza  con  libbsd,  esta  se"nal  se  redefine  como
      SIGUNUSED.


   o  SIGABRT y SIGIOT son identicas.


   o  SIGIO, SIGPOLL, y SIGURG son identicas.


   o  SIGBUS se define como SIGUNUSED. Tecnicamente no existe un "error de
      bus" en el entorno Linux.
10.3      E/S de Terminal


Al igual que ocurre con las se"nales, el control de las E/S de terminales tiene
tres implementaciones diferentes: SVR4, BSD y POSIX.1.
   SVR4 usa la estructura termio y varias llamadas ioctl (tales como TCSETA,
TCGETA, etc.) con un dispositivo de terminal, para obtener y fijar los parametros
con la estructura termio. Esta estructura tiene la siguiente forma:


  struct  termio  {
     unsigned  short  c_iflag;   /*  Modos  de  Entrada  */
     unsigned  short  c_oflag;   /*  Modos  de  Salida  */
     unsigned  short  c_cflag;   /*  Modos  de  Control*/
     unsigned  short  c_lflag;   /*  Modos  de  Disciplina  de  L{\'\i}nea  */
     char  c_line;                 /*  Disciplina  de  L{\'\i}nea  */
     unsigned  char  c_cc[NCC];  /*  Caracteres  de  Control  */
  };


   En BSD, se usa la estructura sgtty junto con varias llamadas ioctl, tales
como TIOCGETP, TIOCSETP, etc.
   En POSIX, se usa la estructura termios, junto con varias funciones definidas
por POSIX.1, tales como tcsetattr and tcgetattr.  La estructura termios es
identica a la estructura struct  termio usada por SVR4, pero los tipos estan
renombrados (como tcflag_t en vez de unsigned  short) y se usa NCCS para
el tama"no del array c_cc.
   En Linux,  el nucleo soporta directamente tanto POSIX.1 termios como
SVR4 termio. Esto significa que si su programa usa uno de estos dos metodos
para acceder a las E/S de terminal, deberia compilar directamente en Linux.

10.4.  CONTROL E INFORMACION DE PROCESOS                     143



Si alguna vez esta en duda, es facil modificar el codigo que use termio para
usar termios, usando un peque"no conocimiento de ambos metodos. Por suerte
esto nunca deberia ser necesario.  Pero, si un programa intenta usar el campo
c_line de la estructura termio, preste especial atencion.  Para casi todas las
aplicaciones, este campo deberia ser N_TTY, y si el programa presupone que esta
disponible algun otro tipo de disciplina, puede que tenga problemas.

    Si  su  programa  usa  la  implementacion  BSD  sgtty,  puede  enlazar  con
libbsd.a como se ha indicado anteriormente.  Esto proporciona un sustitu-
to de ioctl que reenvia las peticiones de E/S de terminal en terminos de las
llamadas POSIX termios que usa el nucleo. Cuando compile este tipo de pro-
gramas, si hay simbolos indefinidos tales como TIOCGETP, entonces necesitara
enlazar con libbsd.
10.4      Control e Informacion de Procesos


Los programas como ps, top y free deben ser capaces de obtener informacion del
nucleo sobre los procesos y recursos del sistema. De forma similar, los depura-
dores y herramientas similares necesitan ser capaces de controlar e inspeccionar
un proceso en ejecucion. Diferentes versiones de UNIX han proporcionado estas
caracteristicas a traves de interfaces diferentes, y casi todas ellas son o bien de-
pendientes de la maquina o bien estan ligadas a un dise"no particular del nucleo.
Hasta ahora no ha habido una interfaz aceptada universalmente para este tipo
de interaccion nucleo-proceso.



10.4.1     Rutinas kvm


Muchos sistemas usan rutinas tales como kvm_open, kvm_nlist y kvm_read para
acceder directamente a las estructuras de datos del nucleo a traves del dispositi-
vo /dev/kmem. En general estos programas abriran /dev/kmem, leeran la tabla
de simbolos del nucleo, localizaran los datos del nucleo en ejecucion con esta
tabla y leeran las direcciones apropiadas en el espacio de direcciones del nucleo
con estas rutinas.  Puesto que esto requiere que el programa del usuario y el
nucleo se pongan de acuerdo en cuanto al tama"no y formato de las estructuras
leidas de esta forma, tales programas deben ser reconstruidos para cada nueva
revision del nucleo, tipo de CPU, etc.



10.4.2     ptrace y el sistema de ficheros /proc


La llamada del sistema ptrace se usa en 4.3BSD y SVID para controlar un
proceso y leer informacion sobre el.   Normalmente la usan los depuradores
para, por ejemplo, detener la ejecucion de un proceso en marcha y examinar
su estado. En SVR4, ptrace ha sido sustituida por el sistema de ficheros /proc,
que se muestra como un directorio que contiene unaunica entrada de fichero
por cada proceso en ejecucion y cuyo nombre es el ID del proceso. El programa
del usuario puede abrir el fichero correspondiente al proceso que le interesa
y generar varias llamadas ioctl sobre el para controlar su ejecucion u obtener
informacion del nucleo sobre el proceso.  De forma similar, el programa puede

144         CAPITULO 10.  CONVERSION DE APLICACIONES A LINUX



leer o escribir datos directamente en el espacio de direcciones del proceso a
traves del descriptor de fichero del sistema de ficheros /proc.



10.4.3    Control de Procesos en Linux


En Linux se soporta la llamada del sistema ptrace para el control de los pro-
cesos, y funciona como en 4.3BSD. Linux tambien proporciona el sistema de
ficheros /proc para obtener informacion de los procesos y el sistema, pero con
una semantica muy diferente.  En Linux, /proc consta de una serie de ficheros
que proporcionan informacion general del sistema tales como uso de memoria,
media de carga, estadisticas de los modulos cargados y estadisticas de la red.
Se puede acceder a estos ficheros usando read y write y se puede analizar su
contenido con scanf. El sistema de ficheros /proc de Linux tambien proporcio-
na un subdirectorio por cada proceso en ejecucion, cuyo nombre es el ID del
proceso.  Este subdirectorio contiene ficheros con informaciones tales como la
linea de ordenes, enlaces al directorio de trabajo actual y al fichero ejecutable,
descriptores de ficheros abiertos, etc. El nucleo proporciona toda esta informa-
cion al vuelo en respuesta a las peticiones de read. Esta implementacion no es
muy diferente del sistema de ficheros /proc disponible en Plan 9, pero tiene sus
inconvenientes_por ejemplo, para que una herramienta como ps liste una tabla
de informacion con todos los procesos en ejecucion se debe recorrer un monton
de directorios y abrir y leer un monton de ficheros. En comparacion, las rutinas
kvm usadas por otros sistemas UNIX leen directamente las estructuras de datos
del nucleo con solo unas pocas llamadas del sistema.
   Obviamente, las diferencias de cada implementacion son tan abismales que
el convertir las aplicaciones que las usen puede ser una tarea de titanes. Deberia
resaltarse el hecho de que el sistema de ficheros /proc de SVR4 es una bestia
completamente diferente del sistema de ficheros /proc que esta disponible en
Linux y no pueden ser usados en el mismo contexto.  No obstante, se puede
afirmar que cualquier programa que use las rutinas kvm o el sistema de ficheros
/proc de SVR4 no es realmente portable y por tanto dichas secciones de codigo
deben ser reescritas para cada sistema operativo.
   La llamada del sistema ptrace es casi identica a la de BSD, pero hay unas
pocas diferencias:


   o  Las peticiones PTRACE_PEEKUSER y PTRACE_POKEUSER de BSD se denomi-
      nan PTRACE_PEEKUSR y PTRACE_POKEUSR, respectivamente, en Linux.


   o  Se puede asignar valores a los registros usando la peticion PTRACE_POKEUSR
      con los desplazamientos indicados en /usr/include/linux/ptrace.h.


   o  Las peticiones de SunOS PTRACE_{READ,WRITE}{TEXT,DATA} no estan so-
      portadas, como tampoco lo estan PTRACE_SETACBKPT, PTRACE_SETWRBKPT,
      PTRACE_CLRBKPT o PTRACE_DUMPCORE. Las peticiones que faltan solo de-
      berian afectar a un peque"no numero de programas existentes.


   Linux no proporciona las rutinas kvm para la lectura del espacio de di-
recciones del nucleo desde un programa de usuario, pero algunos programas
(notablemente kmem_ps) implementan sus propias versiones de estas rutinas.

10.5.  COMPILACION CONDICIONAL PORTABLE                      145



En general, estas no son portables, y cualquier codigo que use las rutinas kvm
probablemente depende de la disponibilidad de ciertos simbolos o estructuras
de datos del nucleo_una suposicion poco segura.  El uso de las rutinas kvm
deberia considerarse especifico de la arquitectura.
10.5      Compilacion Condicional Portable


Si necesita hacer modificaciones al codigo existente para convertirlo a Linux,
puede que necesite usar pares ifdef. . . endif para rodear las partes del codigo
especificas de Linux, o en general, del codigo que correspoda a otras implemen-
taciones.  No existe un estandar real para seleccionar partes de codigo a ser
compiladas en funcion del sistema operativo, pero muchos programas usan la
convencion de definir SVR4 para el codigo System V, BSD para el codigo BSD y
linux para el codigo especifico de Linux.

    La  biblioteca  GNU  C  usada  por  Linux  le  permite  activar  varias  carac-
teristicas de la misma definiendo ciertas macros en tiempo de compilacion.
Estas son:


    o  __STRICT_ANSI__: Solo caracteristicas ANSI C.


    o  _POSIX_SOURCE: Caracteristicas POSIX.1.


    o  _POSIX_C_SOURCE: Si definido a 1, caracteristicas POSIX.1.  Si definido a
      2, caracteristicas POSIX.2.


    o  _BSD_SOURCE: Caracteristicas ANSI, POSIX y BSD.


    o  _SVID_SOURCE: Caracteristicas ANSI, POSIX y System V.


    o  _GNU_SOURCE: ANSI, POSIX, BSD, SVID y extensiones GNU. Este es el
      valor por defecto si no se define ninguna de las anteriores.


    Si usted define _BSD_SOURCE, se definira la definicion adicional _FAVOR_BSD
para la biblioteca.  Esto hara que ciertas cosas elijan el comportamiento BSD
en lugar del comportamiento POSIX o SVR4.  Por ejemplo, si esta definido
_FAVOR_BSD, setjmp y longjmp guardaran y restauraran la mascara de se"nales,
y getpgrp aceptara un parametro PID. Note que a pesar de todo, sigue te-
niendo que enlazar con libbsd para obtener el comportamiento BSD en las
caracteristicas mencionadas anteriormente en este articulo.

    En Linux, gcc define un cierto numero de macros automaticamente que
usted puede utilizar en su programa. Estas son:


    o  __GNUC__ (version GNU C principal, p.ej., 2)


    o  __GNUC_MINOR__ (version GNU C secundaria, p.ej., 5)


    o unix


    o i386

146         CAPITULO 10.  CONVERSION DE APLICACIONES A LINUX



   o  linux


   o  __unix__


   o  __i386__


   o  __linux__


   o  __unix


   o  __i386


   o  __linux


   Muchos programas usan:


  #ifdef  linux


   para rodear el codigo especifico de Linux.  Usando estas macros de tiempo
de compilacion puede adaptar facilmente el codigo existente para incluir o ex-
cluir cambios necesarios para portar el programa a Linux. Notese que, puesto
que Linux incorpora en general mas caracteristicas estilo System V, el mejor
codigo base para comenzar con un programa escrito para System V y BSD es
probablemente la version System V. De forma alternativa, se puede partir de
la base BSD y enlazar con libbsd.
10.6      Comentarios Adicionales

1

   Este capitulo cubre la mayoria de los asuntos relativos a la conversion, ex-
cepto las llamadas del sistema que faltan y que se indican en el capitulo de
llamadas del sistema, asi como los streams que aun no existen (hay rumores
de que deberia existir un modulo cargable de streams en ftp.uni-stutgart.de en
/pub/systems/linux/isdn).



__________________________________1
    A"nadidos por Sven Goldt

Capitulo  11


Llamadas  al  sistema  en  orden



alfabetico


Sven Goldt Guia Linux de Programacion


 _exit                          -   como exit pero con menos acciones (m+c)
accept                        -   aceptar conexiones en un socket (m+c!)
access                         -   comprobar permisos de usuario en un fichero (m+c)
acct                           -   no implementada aun (mc)
adjtimex                      -   obtener/ajustar variables de tiempo internas (-c)
afs_syscall                    -   reservada para el sist. de ficheros Andrew (-)
alarm                         -   envio de SIGALRM tras un tiempo especificado (m+c)
bdflush                       -   vuelca buffers modificados al disco (-c)
bind                          -   nombrar un socket para comunicaciones (m!c)
break                         -   no implementada aun (-)
brk                           -   cambiar el tamano del segmento de datos (mc)
chdir                          -   cambiar el directorio de trabajo (m+c)
chmod                        -   cambiar permisos en un fichero (m+c)
chown                        -   cambiar propietario de un fichero (m+c)
chroot                        -   cambiar el directorio raiz (mc)
clone                          -   vease fork (m-)
close                          -   cerrar un fichero (m+c)
connect                       -   enlazar dos sockets (m!c)
creat                          -   crear un fichero (m+c)
create_module                -   reservar espacio para un modulo del nucleo (-)
delete_module                -   descargar modulo del nucleo (-)
dup                           -   crear un duplicado de un descriptor de fichero (m+c)
dup2                          -   duplicar un descriptor (m+c)
execl, execlp, execle, ...       -   vease execve (m+!c)
execve                        -   ejecutar un fichero (m+c)
exit                           -   terminar un programa (m+c)
fchdir                         -   cambiar directorio de trabajo por referencia ()
fchmod                       -   vease chmod (mc)
fchown                        -   cambiar propietario de un fichero (mc)
fclose                         -   cerrar un fichero por referencia (m+!c)



                                        147

148CAPITULO 11.  LLAMADAS AL SISTEMA EN ORDEN ALFABETICO



fcntl                          -   control de ficheros/descriptores (m+c)
flock                          -   cambiar bloqueo de fichero (m!c)
fork                           -   crear proceso hijo (m+c)
fpathconf                     -   obtener info. de fichero por referencia (m+!c)
fread                          -   leer matriz de datos de un fichero (m+!c)
fstat                          -   obtener estado del fichero (m+c)
fstatfs                         -   obtener estado del sistema de ficheros por referencia (mc)
fsync                          -   escribir bloques modificados del fichero a disco (mc)
ftime                          -   obtener fecha del fichero, en segundos desde 1970 (m!c)
ftruncate                     -   cambiar tamano del fichero (mc)
fwrite                         -   escribir matriz de datos binarios a un fichero (m+!c)
get_kernel_syms               -   obtener tabla de simbolos del kernel o su tamano (-)
getdomainname              -   obtener nombre de dominio del sistema (m!c)
getdtablesize                 -   obtener tamano de la tabla de descriptores de fich. (m!c)
getegid                        -   obtener id. de grupo efectivo (m+c)
geteuid                       -   obtener id. de usuario efectivo (m+c)
getgid                         -   obtener id. de grupo real (m+c)
getgroups                     -   obtener grupos adicionales (m+c)
gethostid                     -   obtener identificador del huesped (m!c)
gethostname                  -   obtener nombre del huesped (m!c)
getitimer                      -   obtener valor de temporizador (mc)
getpagesize                   -   obtener tamano de pagina (m-!c)
getpeername                  -   obtener direccion remota de un socket (m!c)
getpgid                       -   obtener id. del grupo de procesos padre (+c)
getpgrp                       -   obtener id. del grupo padre del proceso (m+c)
getpid                        -   obtener id. del proceso (pid) (m+c)
getppid                       -   obtener id. del proceso padre (m+c)
getpriority                    -   obtener prioridades de usuario/grupo/proceso (mc)
getrlimit                      -   obtener limites de recursos (mc)
getrusage                     -   obtener uso de recursos (m)
getsockname                  -   obtener direccion de un socket (m!c)
getsockopt                    -   obtener opciones ajustadas en un socket (m!c)
gettimeofday                 -   obtener segundos pasados desde 1970 (mc)
getuid                        -   obtener id. de usuario real (uid) (m+c)
gtty                           -   no implementada aun ()
idle                           -   hacer candidato a expulsion al disco a un proceso (mc)
init_module                   -   incluir un modulo cargable (-)
ioctl                          -   manipulacion de un dispositivo de caracter (mc)
ioperm                        -   ajusta algunos permisos de e/s (m-c)
iopl                           -   ajusta permisos de e/s (m-c)
ipc                            -   comunicacion entre procesos (-c)
kill                            -   enviar una senal a un proceso (m+c)
killpg                         -   enviar una senal a un grupo de procesos (mc!)
klog                           -   vease syslog (-!)
link                           -   crear un enlace fisico a un fichero (m+c)
listen                         -   escuchar conexiones en un socket (m!c)

                                                                                149



llseek                         -   lseek para ficheros grandes (-)
lock                           -   no implementada aun ()
lseek                          -   cambia el puntero de un fichero abierto (m+c)
lstat                          -   obtiene estado de un fichero (mc)
mkdir                         -   crea un directorio(m+c)
mknod                        -   crea un dispositivo (mc)
mmap                        -   mapea un fichero en memoria (mc)
modify_ldt                    -   lee o escribe tabla de descriptores locales (-)
mount                        -   montar un sistema de ficheros (mc)
mprotect                      -   controla permisos de acceso a una zona de memoria (-)
mpx                          -   no implementada aun ()
msgctl                        -   control de mensajes ipc (m!c)
msgget                        -   obtiene un id. de cola de mensajes (m!c)
msgrcv                        -   recibe un mensaje ipc (m!c)
msgsnd                       -   envia un mensaje ipc (m!c)
munmap                      -   desmapea un fichero de memoria (mc)
nice                           -   cambia prioridad del proceso (mc)
oldfstat                       -   a extinguir
oldlstat                       -   a extinguir
oldolduname                  -   a extinguir
oldstat                        -   a extinguir
olduname                     -   a extinguir
open                          -   abrir un fichero (m+c)
pathconf                      -   obtener info. de un fichero (m+!c)
pause                         -   dormir hasta la llegada de una senal (m+c)
personality                   -   cambiar dominio de ejecucion en iBCS (-)
phys                          -   no implementada aun (m)
pipe                           -   crea una tuberia (m+c)
prof                           -   no implementada aun ()
profil                          -   perfil de ejecucion (m!c)
ptrace                        -   traza proceso hijo (mc)
quotactl                      -   no implementada aun ()
read                          -   lee datos de un fichero (m+c)
readv                         -   lee bloques de un fichero (m!c)
readdir                        -   lee un directorio (m+c)
readlink                       -   obtener contenido de un enlace simbolico (mc)
reboot                        -   reiniciar o controlar combinacion CTRL-ALT-DEL (-mc)
recv                           -   recibir mensaje de socket conectado (m!c)
recvfrom                      -   recibir mensaje de socket (m!c)
rename                       -   mover/renombrar fichero (m+c)
rmdir                         -   borrar directorio vacio (m+c)
sbrk                           -   vease brk (mc!)
select                         -   dormir hasta actividad en un descriptor de fichero (mc)
semctl                        -   control de semaforos ipc (m!c)
semget                        -   obtener id. de semaforo ipc (m!c)
semop                        -   operaciones en conj. de semaforos ipc (m!c)

150CAPITULO 11.  LLAMADAS AL SISTEMA EN ORDEN ALFABETICO



send                          -   enviar mensaje a un socket conectado (m!c)
sendto                        -   enviar mensaje a un socket (m!c)
setdomainname               -   ajustar dominio del sistema (mc)
setfsgid                       -   ajustar id. grupo del sistema de ficheros ()
setfsuid                       -   ajustar id. usuario del sistema de ficheros ()
setgid                         -   ajustar id. real de grupo (gid) (m+c)
setgroups                     -   ajustar grupos adicionales (mc)
sethostid                      -   ajustar identificador de huesped (mc)
sethostname                  -   ajustar nombre de huesped (mc)
setitimer                      -   ajustar temporizador (mc)
setpgid                        -   ajustar id. de grupo padre (m+c)
setpgrp                       -   sin efecto (mc!)
setpriority                    -   ajustar prioridad de proceso/usuario/grupo (mc)
setregid                       -   ajustar id. de grupo real/efectivo (mc)
setreuid                       -   ajustar id. de usuario real/efectivo (mc)
setrlimit                      -   ajustar limites para los recursos (mc)
setsid                         -   crear sesion (+c)
setsockopt                    -   cambiar opciones del socket (mc)
settimeofday                  -   poner la hora en segundos desde 1970 (mc)
setuid                         -   ajustar id. de usuario real (m+c)
setup                         -   iniciar dispositivos y montar la raiz (-)
sgetmask                     -   vease siggetmask (m)
shmat                         -   enganchar memoria a un segm. de memoria compartida (m!c)
shmctl                        -   manipulacion de mem. compartida ipc (m!c)
shmdt                        -   liberar memoria compartida en un segmento (m!c)
shmget                        -   obtener/crear segmento de memoria compartida (m!c)
shutdown                     -   desconectar socket (m!c)
sigaction                      -   obtener/ajustar manejador de senales (m+c)
sigblock                       -   bloquear senales (m!c)
siggetmask                    -   obtener senales bloqueadas (!c)
signal                         -   poner manejador de senal (mc)
sigpause                      -   usar nueva mascara de senales hasta la proxima senal (mc)
sigpending                    -   obtener senales bloqueadas pendientes (m+c)
sigprocmask                  -   obtener/ajustar mascara de bloqueos de senales (+c)
sigreturn                      -   no usada aun ()
sigsetmask                    -   ajustar mascara de bloqueos de senales (c!)
sigsuspend                    -   reemplaza a sigpause (m+c)
sigvec                         -   vease sigaction (m!)
socket                         -   crea un extremo de comunicacion para socket (m!c)
socketcall                     -   llamada general de sockets (-)
socketpair                    -   crea dos sockets conectados (m!c)
ssetmask                      -   vease sigsetmask (m)
stat                           -   obtener estado del fichero (m+c)
statfs                         -   obtener estado del sistema de ficheros (mc)
stime                         -   obtener segundos desde 1.1.1970 (mc)
stty                           -   no implementada aun ()

                                                                                151



swapoff                       -   detener el intercambio con un dispositivo o fichero (m-c)
swapon                       -   iniciar el intercambio con un dispositivo o fichero (m-c)
symlink                       -   crear un enlace simbolico (m+c)
sync                          -   volcar bloques modificados a disco (mc)
syscall                        -   ejecutar llamada al sistema (-!c)
sysconf                        -   obtener valor de una variable del sistema (m+!c)
sysfs                          -   obtener info. sobre sistemas de ficheros usados ()
sysinfo                        -   obtener info. sobre el sistema (m-)
syslog                         -   manipulacion del registro (m-c)
system                        -   ejecutar un comando de shell (m!c)
time                          -   obtener segundos desde 1.1.1970 (m+c)
times                         -   obtener tiempos del proceso (m+c)
truncate                      -   cambiar tamano de un fichero (mc)
ulimit                         -   obtener/ajustar limites de fichero (c!)
umask                        -   ajustar mascara de creacion de ficheros (m+c)
umount                       -   desmontar un sistema de ficheros (mc)
uname                        -   obtener info. del sistema (m+c)
unlink                        -   borrar un fichero no bloqueado (m+c)
uselib                         -   usar libreria compartida (m-c)
ustat                          -   no implementada anu (c)
utime                         -   motificar info. de tiempo en nodo-i (m+c)
utimes                        -   vease utime (m!c)
vfork                          -   vease fork (m!c)
vhangup                      -   colgar virtualmente el terminal actual (m-c)
vm86                         -   entrar en modo vm86 (m-c)
wait                           -   esperar terminacion de proceso (m+!c)
wait3                         -   espera terminacion de un proceso (bsd) (m!c)
wait4                         -   espera terminacion de un proceso (bsd) (mc)
waitpid                       -   espera terminacion de un proceso (m+c)
write                          -   escribir datos a un fichero (m+c)
writev                        -   escribir bloques de datos a un fichero (m!c)


    (m) hay pagina de manual.
(+) cumple norma POSIX.
(-) Especifica de Linux.
(c) de libc.
(!) no es solo una llamada al sistema. Usa otras


    Sven Goldt The Linux Programmer's Guide

152CAPITULO 11.  LLAMADAS AL SISTEMA EN ORDEN ALFABETICO

Capitulo  12


Abreviaturas


  ANSI        American National Standard for Information Systems
              (Estandar Nacional Americano para Sistemas de Informacion)
  API         Application Programming Interface
              (Interfaz de Programacion de Aplicaciones)
  ASCII       American Standard Code for Information Interchange
              (Codigo Estandar Americano para el Intercambio de Informacion)
  AT 386      Advanced Technology Intel 80386
              (PC basado en 386, "tecnologia avanzada")
  FIPS        Federal Information Processing Standard
              (Estandar Federal de Proceso de Informacion)
  FSF         Free Software Foundation
              (Fundacion para el Software Libre)
  IEEE        Institute of Electrical and Electronics Engineers, Inc.
              (Instituto de Ingenieros de Electricidad y Electronica)
  IPC         Inter Process Communication
              (Comunicacion entre Procesos)
  ISO         International Organization for Standards
              (Organizacion Internacional de Estandares)
  POSIX       Portable Operating System Interface for uniX
              (Interfaz de programacion transportable entre sistemas operativos UniX)
  POSIX.1     IEEE Std. 1003.1-1990 Estandar de Tecnologia de Informacion -
              Interfaz para Portabilidad entre Sistemas Operativos (POSIX) - Part 1:
              Interfaz de Programacion de Aplicaciones (API)
                                        153
