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 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 _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: #include #include 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 #include #include 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 #include #include 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 #include #include 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. #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 /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 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 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 #include #include #include #include #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 #include #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 ___________________________________________________________________________________ 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. 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 #include #include #include 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. #include #include #include #include #include #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 \n"); fprintf(stderr, " (r)ecibir \n"); fprintf(stderr, " (b)orrar\n"); fprintf(stderr, " (m)odo \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. 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 (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. 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 #include #include #include #include #include #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(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 \n"); fprintf(stderr, " (b)loquear \n"); fprintf(stderr, " (d)esbloquear \n"); fprintf(stderr, " (b)orrar\n"); fprintf(stderr, " (m)odo \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 #include #include #include #include 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. #include #include #include #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 \n"); fprintf(stderr, " (l)eer\n"); fprintf(stderr, " (b)orrar\n"); fprintf(stderr, " (m)odo \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 ... 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