Está en la página 1de 82

GNU/Linux:

Programacin de Sistemas
Pablo Garaizar Sagarminaga garaizar@eside.deusto.es

GNU/Linux: Programacin de Sistemas


Pablo Garaizar Sagarminaga

email: garaizar@eside.deusto.es web: htt :// aginas ersonales.deusto.es/garaizar blog: htt ://blog.txi inet.com

!acultad de "ngenier#a $ %S"&% Uni'ersidad de &eusto (ilbao

)abla de contenido

1. PROGRAMACIN EN GNU/LINUX...........................................5 *.* Llamadas al sistema..................................................... .....+ *., Programas- rocesos- hilos............................................. .... *.,.* %structuras de datos............................................. ../ *.,., %stados de los rocesos en Linux...........................0 *.,.1 "denti2icati'os de roceso................................. ......3 *.,.4 Plani2icacin....................................................... ...** *.1 %l G55............................................ .................................*, *.1.* 5om ilacin b6sica....................................... ........*, *.1., Paso a aso...................................................... .....*1 *.1.1 Librer#as................................................... .............*4 *.1.4 7 timizaciones................................ .....................*4 *.1.+ &ebugging..................................... .......................*+ *.4 ma8e world.............................................................. ........*+ *.4.* 9a8e2ile- el guin de ma8e...................................*. *.+ Programando en 5 ara GNU/Linux............................. ....,: *.+.* ;ola- mundo<....................................... .................,: *.+., Llamadas sencillas............................................ ....,* *.+.1 9ane=o de directorios...........................................1, *.+.4 >ugando con los ermisos.....................................1+ *.+.+ 5reacin ? du licacin de rocesos.....................10 *.+.. 5omunicacin entre rocesos..............................41 *.+./ 5omunicacin or red......................................... ..., 2. LICENCIA....................................................................80

ii

@ndice de 2iguras

FIGURA 1.1.1 MECANISMO DE PETICIN DE SERVICIOS AL KERNEL..........6 FIGURA 1.5.2 LOS DESCRIPTORES DE FICHERO INICIALES DE UN PROCESO. ..............................................................................24 FIGURA 1.5. DUPLICACIN DE PROCESOS MEDIANTE FORK!"............. # FIGURA 1.5.4 PROCESOS RECI$IENDO SE%ALES& RUTINAS DE CAPTURA ' PROCESO DE SE%ALES.......................................................48 FIGURA 1.5.5 LA LLAMADA A LA FUNCIN ALARM!" GENERAR( UNA SE%AL SIG)ALARM HACIA EL MISMO PROCESO *UE LA INVOCA...............4# FIGURA 1.5.6 UNA TU$ER+A ES UNIDIRECCIONAL& COMO LOS TEL,FONOS DE 'OGUR.......................................................................52 FIGURA 1.5.- EL PROCESO PADRE ' SU HI.O COMPARTEN DATOS MEDIANTE UNA TU$ER+A................................................................5 FIGURA 1.5.8 DOS PROCESOS SE COMUNICAN $IDIRECCIONALMENTE CON DOS TU$ER+AS...............................................................55 FIGURA 1.5.# COMUNICACIN MEDIANTE CAPAS DE PROTOCOLOS DE RED. 64

iii

@ndice de tablas

TA$LA 1.2.1 CREDENCIALES DE UN PROCESO ' SUS SIGNIFICADOS.........11 TA$LA 1.4.2 LISTA DE LAS VARIA$LES AUTOM(TICAS M(S COMUNES EN MAKEFILES..................................................................18 TA$LA 1.5. LISTA DE LOS POSI$LES VALORES DEL ARGUMENTO /FLAGS0. ..............................................................................2 TA$LA 1.5.4 LISTA DE LOS POSI$LES VALORES DEL ARGUMENTO /MODE0. ..............................................................................2 TA$LA 1.5.5 LISTA DE LOS POSI$LES VALORES DEL ARGUMENTO /1HENCE0. ..............................................................................28

i'

*.Programacin en GNU/Linux

E
*.*

n este texto repasaremos conceptos de multiprogramacin como las definiciones de programa, proceso e hilos, y explicaremos el mecanismo de llamadas al sistema que emplea Linux para poder aceptar las peticiones desde el entorno de usuario. Seguidamente veremos las posibilidades que nos ofrece el Compilador de C de G !, GCC, y programaremos nuestros primeros e"ecutables para G !#Linux. $espu%s de repasar las llamadas al sistema m&s comunes, anali'aremos las particularidades de ! () a la hora de mane"ar directorios, permisos, etc., y nos adentraremos en la Comunicacin (nterproceso *(+C,. -inalmente abordaremos de forma introductoria la programacin de soc.ets de red, para dotar de capacidades telem&ticas a nuestros programas.

Llamadas al sistema

G !#Linux es un Sistema /perativo multitarea en el que van a convivir un gran n0mero de procesos. Es posible, bien por un fallo de programacin o bien por un intento malicioso, que alguno de esos procesos haga cosas que atenten contra la estabilidad de todo el sistema. +or ello, con vistas a proteger esa estabilidad, el n0cleo o .ernel del sistema funciona en un entorno totalmente diferente al resto de programas. Se definen entonces dos modos de e"ecucin totalmente separados1 el modo .ernel y el modo usuario. Cada uno de estos modos de e"ecucin dispone de memoria y procedimientos diferentes, por lo que un programa de usuario no podr& ser capa' de da2ar al n0cleo. 3qu4 se plantea una duda1 si el n0cleo del sistema es el 0nico capa' de manipular los recursos f4sicos del sistema *hard5are,, y %ste se e"ecuta en un modo de e"ecucin totalmente dis"unto al del resto de los programas, 6cmo es posible que un peque2o programa hecho por m4 sea capa' de leer y escribir en disco7 8ien, la duda es lgica, porque todav4a no hemos hablado de las 9llamadas o peticiones al sistema: *9syscalls:,.
+

Programacin de Sistemas

Las syscalls o llamadas al sistema son el mecanismo por el cual los procesos y aplicaciones de usuario acceden a los servicios del n0cleo. Son la interfa' que proporciona el n0cleo para reali'ar desde el modo usuario las cosas que son propias del modo .ernel *como acceder a disco o utili'ar una tar"eta de sonido,. La siguiente figura explica de forma gr&fica cmo funciona la syscall readA 1 B

!igura *.*.*

9ecanismo de eticin de ser'icios al 8ernel.

El proceso de usuario necesita acceder al disco para leer, para ello utili'a la syscall read*, utili'ando la interfa' de llamadas al sistema. El n0cleo atiende la peticin accediendo al hard5are y devolviendo el resultado al proceso que inici la peticin. Este procedimiento me recuerda al comedor de un restaurante, en %l todos los clientes piden al camarero lo que desean, pero nunca entran en la cocina. El camarero, despu%s de pasar por la cocina, traer& el plato que cada cliente haya pedido. ing0n comensal podr4a estropear la cocina, puesto que no tiene acceso a ella. +r&cticamente todas las funciones que utilicemos desde el espacio de e"ecucin de usuario necesitar&n solicitar una peticin al .ernel mediante una syscall, esto es, la e"ecucin de las aplicaciones de usuario se canali'a a trav%s del sistema de peticiones al sistema. Este hecho es importante a la hora de fi"ar controles y registros en el sistema, ya que si utili'amos nuestras propias versiones de las syscalls para ello, estaremos abarcando todas las aplicaciones y procesos del espacio de e"ecucin de usuario. (maginemos un 9camarero: malicioso que capturase todas las peticiones de todos los clientes y envenenase todos los platos antes de servirlos... nuestro restaurante deber4a cuidarse muy bien de qu% personal contrata y nosotros deberemos ser cautelosos tambi%n a la hora de cargar drivers o mdulos en nuestro n0cleo.

*.,

Programas- rocesos- hilos...

!n proceso es una entidad activa que tiene asociada un con"unto de atributos1 cdigo, datos, pila, registros e identificador 0nico. ;epresenta la entidad de e"ecucin utili'ada por el Sistema /perativo. -recuentemente se conocen tambi%n con el nombre de tareas *9tasks:,.

Programacin de Sistemas

!n programa representa una entidad pasiva. Cuando un programa es reconocido por el Sistema /perativo y tiene asignado recursos, se convierte en proceso. Es decir, la e"ecucin de cdigo implica la existencia de un entorno concreto. Generalmente un roceso: Es la unidad de asignacin de recursos1 el Sistema /perativo va asignando los recursos del sistema a cada proceso. Es una unidad de despacho1 un proceso es una entidad activa que puede ser e"ecutada, por lo que el Sistema /perativo conmuta entre los diferentes procesos listos para ser e"ecutados o despachados. Sin embargo, en algunos Sistemas /perativos estas dos unidades se separan, entendi%ndose la segunda como un hilo o thread. Los hilos no generan un nuevo proceso sino que producen flu"os de e"ecucin dis"untos dentro del mismo proceso. 3s4 pues, un hilo o 9proceso ligero: *9lightweight process, LWP:, comparte los recursos del proceso, as4 como la seccin de datos y de cdigo del proceso con el resto de hilos. Esto hace que la creacin de hilos y el cambio de e"ecucin entre hilos sea menos costoso que el cambio de contexto entre procesos, aumentando el rendimiento global del sistema. !n Sistema /perativo multiusuario y multiprogramado *multitarea, pretende crear la ilusin a sus usuarios de que se dispone del sistema al completo. La capacidad de un procesador de cambiar de tarea o contexto es infinitamente m&s r&pida que la que pueda tener una persona normal, por lo que habitualmente el sistema cumple este ob"etivo. Es algo parecido a lo que pasa en un restaurante de comida r&pida1 por muy r&pido que seas comiendo, normalmente la velocidad de servir comida es mucho mayor. Si un camarero fuese atendi%ndote cada < minutos, podr4as tener la sensacin de que eres el cliente m&s importante del local, pero en realidad lo que est& haciendo es compartir sus servicios *recursos, entre todos los clientes de forma r&pida *9time-sharing:,.

*.,.*

%structuras de datos

Si queremos implementar la e"ecucin de varias tareas al mismo tiempo, los cambios de contexto entre tareas y todo lo concerniente a la multiprogramacin, es necesario disponer de un modelo de procesos y las estructuras de datos relacionadas para ello. !n modelo de procesos t4pico consta de los siguientes elementos1 +C8 *+rocess Control 8loc.,1 un bloque o estructura de datos que contiene la informacin necesaria de cada proceso. +ermite almacenar el contexto de cada uno de los procesos con el ob"eto de ser reanudado posteriormente. Suele ser un con"unto de identificadores

Programacin de Sistemas

de proceso, tablas de mane"o de memoria, estado de los registros del procesador, apuntadores de pila, etc. =abla de +rocesos1 la tabla que contiene todos los +C8s o bloques de control de proceso. Se actuali'a a medida que se van creando y eliminando procesos o se producen transiciones entre los estados de los mismos. Estados y =ransiciones de los +rocesos1 los procesos se ordenan en funcin de su informacin de +lanificacin, es decir, en funcin de su estado. 3s4 pues, habr& procesos bloqueados en espera de un recurso, listos para la e"ecucin, en e"ecucin, terminando, etc. >ector de (nterrupciones1 contiene un con"unto de apuntadores a rutinas que se encargar&n de atender cada una de las interrupciones que puedan producirse en el sistema.

En Linux esto est& implementado a trav%s de una estructura de datos denominada tas8Cs t ruc . tEs el +C8 de Linux, en ella se almacena toda la informacin relacionada con un proceso1 identificadores de proceso, tablas de mane"o de memoria, estado de los registros del procesador, apuntadores de pila, etc. La =abla de +rocesos no es m&s que un array de tas8Cs t ruc , ten la versin ?.@.x de Linux desaparece el array tas8DE como tal y se definen arrays para buscar procesos en funcin de su identificativo de proceso *+($, como idash1
extern struct tas8Cstruct F idhashDP"&;GS;CSHEI

determina el n0mero de tareas capaces de ser gestionadas por esa tabla *definida en 9/usr/src/linux/include/linux/sched.h:,. +or defecto P"&;GS;CSH vale.<A? *Jde2ine P"&;GS;CSH A4:3. KK ,B,, es decir, es posible gestionar <A? tareas concurrentemente desde un 0nico proceso inicial o 9init:. +odremos tener tantos procesos 9init: o iniciales como C+!s tenga nuestro sistema1
P"&;GS;CSH extern struct tas8Cstruct FinitCtas8sDNLC5PUSEI NLC5PUS determina el n0mero de procesadores disponibles en el sistema *definida en 9/usr/src/linux/include/linux/sched.h:,. +or defecto NLC5PUS vale.A, pero si se habilita el soporte para multiprocesador, SB+, este n0mero puede crecer *hasta C? en la versin del .ernel1 ?.@.AD, por e"emplo,.

*.,.,

%stados de los rocesos en Linux

Como ya hemos comentado, los procesos van pasando por una serie de estados discretos desde que son creados hasta que terminan o mueren. Los diferentes estados sirven para saber cmo se encuentra un proceso en cuanto a su e"ecucin, con el ob"eto de llevar un me"or control y aumentar el

Programacin de Sistemas

rendimiento del sistema. o tendr4a sentido, por e"emplo, cederle tiempo de procesador a un proceso que sabemos que sabemos a ciencia cierta que est& a la espera de un recurso todav4a no liberado. En Linux el estado de cada proceso se almacena dentro de un campo de la estructura tas8Cs t ruc . t$icho campo, 9state:, ir& variando en funcin del estado de e"ecucin en el que se encuentre el proceso, pudiendo tomar los siguientes valores1 )GSMCLUNN"NG A: B1 (ndica que el proceso en cuestin se est& e"ecutando o listo para e"ecutarse. En este segundo caso, el proceso dispone de todos los recursos necesarios excepto el procesador. )GSMC "N)%LLUP) "(L% A*1B el proceso est& suspendido a la espera de alguna se2al para pasar a listo para e"ecutarse. Generalmente se debe a que el proceso est& esperando a que otro proceso del sistema le preste alg0n servicio solicitado. )GSMCUN"N)%LLUP) "(L% A, B 1 el proceso est& bloqueado esperando a que se le conceda alg0n recurso hard5are que ha solicitado *cuando una se2al no es capa' de 9despertarlo:,. )GSMCH79("% A4 B1 el proceso ha finali'ado pero a0n no se ha eliminado todo rastro del mismo del sistema. Esto es habitualmente causado porque el proceso padre todav4a lo espera con una wai t A . B )GSMCS)7PP%& A0 B1 el proceso ha sido detenido por una se2al o bien mediante el uso de t race A B para ser tra'ado. En funcin del estado de la tarea o proceso, estar& en una u otra cola de procesos1 Cola de E"ecucin o runqueue1 procesos en estado )GSMCLUNN"NG . Colas de Espera o wait queues1 procesos en estado )GSMC "N)%LLUP) "(L% )GSMC "N "N)%LLUP) "(L% . Los procesos en estado )GSMCH79("% )GSMCS)7PP%& no necesitan colas para ser gestionados.

*.,.1

"denti2icati'os de roceso

=odos los procesos del sistema tienen un identificativo 0nico que se conoce como (dentificativo de +roceso o +($. El +($ de cada proceso es como su $ ( *$ocumento acional de (dentidad,, todo el mundo tiene el suyo y cada n0mero identifica a un su"eto en concreto. Si queremos ver los +($s de los procesos que est&n actualmente presentes en nuestro sistema, podemos hacerlo mediante el uso del comando 9 s:, que nos informa del estado de los procesos1
txi i@neon:NO s xa P"& ))P S)G) )"9% 5799GN& *Q S :::+ init D,E

Programacin de Sistemas

*:

,Q SR 1Q SRN 4Q SR +Q SR .Q SR /+ Q SR *+0 Q S *.: Q S */+ Q S 1*1 Q S 1*3 Q S 1,, Q S 11: tt?* S 11* tt?, S 11, tt?1 S 111 tt?4 S 114 tt?+ S 11+ tt?. S ,,30+ Q S ,,30/ Q S ,,300 ts/: S ,1,3, ts/: L

:::: D8e'entdE :::1 D8so2tirSdC5PU:E ::*, D8swa dE :::: Dbd2lushE :::1 D8u datedE ::*, D8=ournaldE *:+* /sbin/s?slogd :::: /sbin/8logd :::: /usr/sbin/inetd :::: /usr/sbin/sshd :::: /usr/sbin/atd :::4 /usr/sbin/cron :::: /sbin/gett? 104:: :::: /sbin/gett? 104:: :::: /sbin/gett? 104:: :::: /sbin/gett? 104:: :::: /sbin/gett? 104:: :::: /sbin/gett? 104:: :::: /usr/sbin/sshd :::: /usr/sbin/sshd :::: $bash :::: s xa

tt?* tt?, tt?1 tt?4 tt?+ tt?.

En la primera columna vemos cmo cada uno de los procesos, incluido el propio 9 s xa : tienen un identificativo 0nico o +($. 3dem&s de esto, es posible saber qui%n ha sido el proceso padre u originario de cada proceso, consultando su ++($, es decir, el +arent +rocess ($. $e esta manera es bastante sencillo hacernos una idea de cu&l ha sido el &rbol de creacin de los procesos, que podemos obtener con el comando 9 st ree :1
txi i@neon:NO stree init$T$atd U$cron U$.FDgett?E U$inetd U$8e'entd U$8=ournald U$8logd U$sshd$$$sshd$$$sshd$$$bash$$$ stree V$s?slogd

Como vemos, el comando 9 s t ree : es el proceso hi"o de un int%rprete de comandos *bash, que a su ve' es hi"o de una sesin de SSE *Secure Shell,. /tro dato de inter%s al e"ecutar este comando se da en el hecho de que el proceso init es el proceso padre de todos los dem&s procesos. Esto ocurre siempre1 primero se crea el proceso init, y todos los procesos siguientes se crean a partir de %l. 3dem&s de estos dos identificativos existen lo que se conocen como 9credenciales del proceso:, que informan acerca del usuario y grupo que lo ha lan'ado. Esto se utili'a para decidir si un determinado proceso puede acceder a un recurso del sistema, es decir, si sus credenciales son suficientes para los permisos del recurso. Existen varios identificativos utili'ados como credenciales, todos ellos almacenados en la estructura tas8Cstruct1
/F rocess credentials F/

Programacin de Sistemas

**

uidCt uid-euid-suid-2suidI gidCt gid-egid-sgid-2sgidI

Su significado es el siguiente
"denti2icati'os reales "denti2icati'os e2ecti'os "denti2icati'os guardados "denti2icati'os de acceso a 2icheros uid euid suid 2suid "denti2icati'o de usuario real asociado al roceso "denti2icati'o de usuario e2ecti'o asociado al roceso "denti2icati'o de usuario guardado asociado al roceso "denti2icati'o de usuario asociado al roceso ara los controles de acceso a 2icheros "denti2icati'o de gru o real asociado al roceso egid "denti2icati'o de gru o e2ecti'o asociado al roceso sgid "denti2icati'o de gru o guardado asociado al roceso 2sgid "denti2icati'o de gru o asociado al roceso ara los controles de acceso a 2icheros gid

)abla *.,.* 5redenciales de un roceso ? sus signi2icados.

*.,.4

Plani2icacin

El planificador o scheduler en Linux se basa en las prioridades est&ticas y din&micas de cada una de las tareas. 3 la combinacin de ambas prioridades se la conoce como 9bondad de una tarea: *9tasks goodness:,, y determina el orden de e"ecucin de los procesos o tareas1 cuando el planificador est& en funcionamiento se anali'a cada una de las tareas de la Cola de E"ecucin y se calcula la 9bondad: de cada una de las tareas. La tarea con mayor 9bondad: ser& la prxima que se e"ecute. Cuando hay tareas extremadamente importantes en e"ecucin, el planificador se llama en intervalos relativamente amplios de tiempo, pudi%ndose llegar a periodos de F,@ segundos sin ser llamado. Esto puede me"orar el rendimiento global del sistema evitando innecesarios cambios de contexto, sin embargo es posible que afecte a su interatividad, aumentando lo que se conoce como 9latencias de planificacin: *9scheduling latencies:,. %l lani2icador de Linux utiliza un contador Sue genera una interru cin cada *: milisegundos. 5ada 'ez Sue se roduce dicha interru cin el lani2icador decrementa la rioridad din6mica de la tarea en e=ecucin. Una 'ez Sue este contador ha llegado a cero- se realiza una llamada a la 2uncin schedu le A-B Sue se encarga de la lani2icacin de las tareas. Por lo tanto- una tarea con una rioridad or de2ecto de ,: odr6 2uncionar durante :-, segundos A,:: milisegundosB antes de Sue otra tarea en el sistema tenga la osibilidad de e=ecutarse. Por lo tanto- tal ? como coment6bamosuna tarea con m6xima rioridad A4:B odr6 e=ecutarse durante :-4 segundos sin ser detenida or un e'ento de lani2icacin. El n0cleo del sistema se apoya en las estructuras tas8Cs t rucpara t planificar la e"ecucin de las tareas, ya que, adem&s de los campos comentados, esta

Programacin de Sistemas

*,

estructura contiene mucha informacin desde el punto de vista de la planificacin1


'o la t i l e l ong s 1 ta nos te

informa del estado de la tarea, indicando si la tarea es e"ecutable o si es interrumpible *puede recibir se2ales, o no. l ong counte 1rrepresenta la parte din&mica de la 9bondad: de una tarea. (nicialmente se fi"a al valor de la prioridad est&tica de la tarea. l ong r io r i t representa ?: la parte est&tica de la 9bondad: de la tarea. l ong needC resched 1 se anali'a antes de volver a la tarea en curso despu%s de haber llamado a una syscall, con el ob"eto de comprobar si es necesario volver a llamar a schedu le A Bpara planificar de nuevo la e"ecucin de las tareas. uns igned l ong o l i 1c? inidica la pol4tica de planificacin empleada1 -(-/, ;/! $ ;/8( , etc. uns igned r t C r i o r 1 ise t ? utili'a para determinar la 9bondad: de una tarea al utili'ar tiempo real por soft5are. s t ruc t m mCst ruc t Fm m 1 apunta a la informacin de gestin d memoria de la tarea. 3lgunas tareas comparten memoria por lo que pueden compartir una 0nica estructura m mCst ruc t .

*.1

%l G55

Las siglas GCC significan actualmente 9GNU ompiler ollection9 *9Coleccin de compiladores G !:,. 3ntes estas mismas siglas significaban 9GNU ompiler: *9Compilador C de G !:,, si bien ahora se utili'an para denominar a toda una coleccin de compiladores de diversos lengua"es como C, CGG, /b"etive C, Chill, -ortran, y Hava. Esta coleccin de compiladores est& disponible para pr&cticamente todos los Sistemas /perativos, si bien es caracter4stica de entornos ! () libres y se incluye en la pr&ctica totalidad de distribuciones de G !#Linux. En su desarrollo participan voluntarios de todas las partes del mundo y se distribuye ba"o la licencia G+L *9General Pu!lic License:, lo que lo hace de libre distribucin1 est& permitido hacer copias de %l y regalarlas o venderlas siempre que se incluya su cdigo fuente y se mantenga la licencia. osotros nos referiremos al GCC 0nicamente como el compilador de C est&ndar en G !#Linux.

*.1.*

5om ilacin b6sica

GCC es un compilador de l4nea de comandos, aunque existen numerosos ($E o entornos de desarrollo que incluyen a GCC como su motor de compilacin. La manera m&s simple de llamar a GCC es esta1
gcc codigo.c Wo e=ecutable

Programacin de Sistemas

*1

3s4 el GCC compilar& el cdigo fuente que haya en 9cod igo .c : y generar& un fichero e"ecutable en 9e =ecutab le :. Si todo el proceso se ha desarrollado correctamente, el GCC no devuelve ning0n mensa"e de confirmacin. En realidad la opcin 9$o: para indicar el fichero de salida no es necesaria, y si no se indica se guarda el resultado de la compilacin en el fichero 9a.out:. Buchos proyectos de soft5are est&n formados por m&s de un fichero fuente, por lo que habr& que compilar varios ficheros para generar un 0nico e"ecutable. Esto se puede hacer de forma sencilla llamando a GCC con varios ficheros fuente y un e"ecutable1
gcc menu.c bd.c motor.c Wo =uego

Sin embargo es bastante probable que todos los ficheros fuente de un mismo proyecto no se encuentren en el mismo directorio, y que conforme el proyecto cre'ca, existan muchos ficheros de cabeceras *los t4picos 9.h:, y se alo"en en directorios diferentes. +ara evitar problemas a la hora de tratar con proyectos seme"antes, podemos hacer uso de la opcin 9$": e incluir los ficheros que sean necesario. (maginemos que tenemos un proyecto en el que todos los ficheros fuente est&n dentro del directorio 9src: y todos los ficheros de cabeceras est&n en el directorio 9include:. +odr4amos compilar el proyecto de la siguiente manera1
gcc ./src/F.c W"include Wo =uego

*.1.,

Paso a aso

Easta ahora hemos dado por hecho que es normal que un compilador realice todos los pasos necesarios para obtener un e"ecutable partiendo del cdigo fuente, si bien esto no tiene por qu% ser as4. 3 la hora de generar un e"ecutable hay una serie de procesos implicados1 A. Edicin del cdigo fuente I cdigo fuente. ?. +reprocesado I cdigo fuente preprocesado. C. Compilacin I cdigo ensamblador. @. Ensamblado I cdigo ob"eto. <. Enla'ado I e"ecutable. Bediante el GCC pueden reali'arse todos ellos secuencialmente hasta conseguir el e"ecutable. Eso es lo que hemos estado haciendo en los e"emplos anteriores, pero en ocasiones es conveniente parar el proceso en un paso intermedio para evaluar los resultados1 Con la opcin 9$%: detenemos el proceso en la etapa de preprocesado, obteniendo cdigo fuente preprocesado. Con la opcin 9$S: se detiene en la etapa de compilacin, pero no ensambla el cdigo. Con la opcin 9$c:, compila y ensambla el cdigo, pero no lo enla'a, obteniendo cdigo ob"eto.

Programacin de Sistemas

*4

Si no indicamos ninguna de estas opciones, se reali'ar&n las cuatro fases de las que se encarga el GCC1 preprocesado, compilacin, ensamblado y enla'ado.

3hora ya controlamos m&s el proceso. Cuando un proyecto involucra muchos ficheros es bastante normal que no todas sus partes tengan las mismas opciones de compilacin. +or ello es muy 0til generar separadamente los respectivos cdigos ob"eto, y cuando ya est%n todos generados, enla'arlos para obtener el e"ecutable1
gcc gcc gcc gcc Wc bd.c Wo bd.o Wc motor.c Wlgra hics Wo motor.o Wc menu.c Wlcurses Wo menu.o bd.o motor.o menu.o Wo =uego

*.1.1

Librer#as

Conforme un proyecto va ganando entidad se hace casi irremediable el uso de librer4as *realmente son 9bibliotecas:, de funciones, que permiten reutili'ar cdigo de manera cmoda y eficiente. +ara utili'ar librer4as est&ndar en el sistema es necesario emplear la opcin 9$ l : a la hora de llamar a GCC1
gcc Wc menu.c Wlcurses Wo menu.o

La compilacin de este fichero *9menu.c :, requiere que est% instalada la librer4a curses o ncurses en el sistema, por e"emplo *la librer4a se llamar& casi con seguridad 9l i bncurses :,. Si la librer4a no es una librer4a est&ndar en el sistema, sino que pertenece 0nicamente a nuestro proyecto, podremos indicar la ruta empleando la opcin 9$L:1
gcc Wc motor.c WL./libs/librer#a$motor Wo motor.o

*.1.4

7 timizaciones

El GCC incluye opciones de optimi'acin en cuanto al cdigo generado. Existen C niveles de optimi'acin de cdigo1 A. Con 9$7*: conseguimos optimi'aciones en bloques repetitivos, operaciones con coma flotante, reduccin de saltos, optimi'acin de mane"o de par&metros en pila, etc. ?. Con 9$7,: conseguimos todas las optimi'aciones de 9$7*: m&s me"oras en el abastecimiento de instrucciones al procesador, optimi'aciones con respecto al retardo ocasionado al obtener datos del 9heap: o de la memoria, etc. C. Con 9$71: conseguimos todas las optimi'aciones de 9$7,: m&s el desenrollado de bucles y otras prestaciones muy vinculadas con el tipo de procesador.

Programacin de Sistemas

*+

Si queremos tener un control total acerca de las opciones de optimi'acin empleadas podremos utili'ar la opcin 9$ 2 :1 9$ 2 2 as tmath :1 genera optimi'aciones sobre las operaciones de coma flotante, en ocasiones salt&ndose restricciones de est&ndares (EEE o 3 S(. 9$ 2 i n l i ne $ 2unc t :1 ions expande todas las funciones 9inline: durante la compilacin. 9$ 2un ro l l $ l oo :1 s desenrolla todos los bucles, convirti%ndolos en una secuencia de instrucciones. Se gana en velocidad a costa de aumentar el tama2o del cdigo.

*.1.+

&ebugging

Los errores de programacin o 9!ugs: son nuestros compa2eros de via"e a la hora de programar cualquier cosa. Es muy com0n programar cualquier aplicacin sencill4sima y que por alguna m&gica ra'n no funcione correctamente, o lo haga slo en determinadas ocasiones *esto desespera a0n m&s,. +or ello, muchas veces tenemos que hacer 9de!ugging:, ir a la ca'a y captura de nuestros 9!ugs:.JLa manera m&s ruin de buscar fallos todos la conocemos, aunque muchas veces nos d% vergKen'a reconocerlo, en lugar de pelearnos con 9de!uggers:, llenar el cdigo de llamadas a r in t 2 A B sacando resultados intermedios es lo m&s divertido. En muchas ocasiones hacemos de ello un arte y utili'amos variables de preprocesado para indicar qu% parte del cdigo es de 9de!ug: y cu&l no. +ara indicar una variable de preprocesado en GCC se utili'a la opcin 9$ & :1
gcc W&&%(UG rueba.c Wo rueba

Si queremos optar por una alternativa m&s profesional, qui'& convenga utili'ar las opciones 9$ g : o 9$ ggdb: para generar informacin extra de 9de!ug: en nuestro e"ecutable y poder seguir de forma m&s cmoda su e"ecucin mediante el G$8 *9GNU "e!ugger:,. Si deseamos obtener todas las posibles advertencias en cuanto a generacin del e"ecutable partiendo de nuestro cdigo fuente, emplearemos 9$ Ral :, l para solicitar todos los 9warnings: en los que incurra nuestro cdigo. 3s4 mismo, podr4amos utili'ar la opcin 9$ ans: i o 9$ edantic: para tratar de acercar nuestros programas al est&ndar 3 S( C.

*.4

ma8e world

Eemos visto en el anterior apartado cmo el desarrollo de un programa puede involucrar muchos ficheros diferentes, con opciones de compilacin muy diversas y comple"as. Esto podr4a convertir la programacin de herramientas que involucren varios ficheros en un verdadero infierno. Sin embargo, ma8e permite gestionar la compilacin y creacin de e"ecutables, aliviando a los programadores de %stas tareas. Con ma8e deberemos definir solamente una ve' las opciones de compilacin de cada mdulo o programa. El resto de llamadas ser&n

Programacin de Sistemas

*.

sencillas gracias a su funcionamiento mediante reglas de compilacin. 3dem&s, ma8e es capa' de llevar un control de los cambios que ha habido en los ficheros fuente y e"ecutables y optimi'a el proceso de edicinJ compilacinJdepuracin evitando recompilar los mdulos o programas que no han sido modificados.

*.4.*

9a8e2ile- el guin de ma8e

Los Ba.efiles son los ficheros de texto que utili'a ma8e para llevar la gestin de la compilacin de programas. Se podr4an entender como los guiones de la pel4cula que quiere hacer ma8e , o la base de datos que informa sobre las dependencias entre las diferentes partes de un proyecto. =odos los Ba.efiles est&n ordenados en forma de reglas, especificando qu% es lo que hay que hacer para obtener un mdulo en concreto. El formato de cada una de esas reglas es el siguiente1
ob=eti'o : de endencias comandos

En 9ob =e t i 'o : definimos el mdulo o programa que queremos crear, despu%s de los dos puntos y en la misma l4nea podemos definir qu% otros mdulos o programas son necesarios para conseguir el 9ob =e t i 'o :. +or 0ltimo, en la l4nea siguiente y sucesivas indicamos los comandos necesarios para llevar esto a cabo. Es muy importante que los comandos est%n separados por un tabulador de el comien'o de l4nea. 3lgunos editores como el mcedit cambian los tabuladores por L espacios en blanco, y esto hace que los Ba.efiles generados as4 no funcionen. !n e"emplo de regla podr4a ser el siguiente1
=uego : 'entana.o motor.o bd.o gcc W7, Wc =uego.c Wo =uego.o gcc W7, =uego.o 'entana.o motor.o bd.o Wo =uego

+ara crear 9= uego: es necesario que se hayan creado 9'entana .o :, 9motor.o: y 9bd.o: *t4picamente habr& una regla para cada uno de esos ficheros ob"eto en ese mismo Ba.efile,. En los siguientes apartados anali'aremos un poco m&s a fondo la sintaxis de los Ba.efiles. *.4.*.* 5omentarios en 9a8e2iles Los ficheros Ba.efile pueden facilitar su comprensin mediante comentarios. =odo lo que est% escrito desde el car&cter 9M: hasta el final de la l4nea ser& ignorado por ma8e. Las l4neas que comiencen por el car&cter 9M: ser&n tomadas a todos los efectos como l4neas en blanco. Es bastante recomendable hacer uso de comentarios para dotar de mayor claridad a nuestros Ba.efiles. +odemos incluso a2adir siempre una cabecera con la fecha, autor y n0mero de versin del fichero, para llevar un control de versiones m&s eficiente.

Programacin de Sistemas

*/

*.4.*., Xariables Es muy habitual que existan variables en los ficheros Ba.efile, para facilitar su portabilidad a diferentes plataformas y entornos. La forma de definir una variable es muy sencilla, basta con indicar el nombre de la variable *t4picamente en may0sculas, y su valor, de la siguiente forma1
55 Y gcc W7,

Cuando queramos acceder al contenido de esa variable, lo haremos as41


OA55B =uego.c Wo =uego

Es necesario tener en cuenta que la expansin de variables puede dar lugar a problemas de expansiones recursivas infinitas, por lo que a veces se emplea esta sintaxis1
55 :Y gcc 55 :Y OA55B W7,

Empleando 9:Y: en lugar de 9Y: evitamos la expansin recursiva y por lo tanto todos los problemas que pudiera acarrear. 3dem&s de las variables definidas en el propio Ba.efile, es posible hacer uso de las variables de entorno, accesibles desde el int%rprete de comandos. Esto puede dar pie a formulaciones de este estilo1
SL5 Y OA;79%B/src =uego : gcc OAS5LB/F.c Wo =uego

Empleando 9:Y: en lugar de 9Y: evitamos la expansin recursiva y por lo tanto todos los problemas que pudiera acarrear. !n tipo especial de variables lo constituyen las variables autom&ticas, aquellas que se eval0an en cada regla. 3 m4, personalmente, me recuerdan a los par&metros de un script. En la siguiente tabla tenemos una lista de las m&s importantes1
Xariable O@ OF OZ O[ OQ &escri cin Se sustitu?e or el nombre del ob=eti'o de la resente regla. Se sustitu?e or la ra#z de un nombre de 2ichero. Se sustitu?e or la rimera de endencia de la resente regla. Se sustitu?e or una lista se arada or es acios de cada una de las de endencias de la resente regla. Se sustitu?e or una lista se arada or es acios de cada una de las de endencias de la resente regla Sue sean m6s nue'as Sue el ob=eti'o de la regla. Se sustitu?e or la arte corres ondiente al subdirectorio de la ruta del 2ichero corres ondiente a un ob=eti'o Sue se encuentre en un subdirectorio. Se sustitu?e or la arte corres ondiente al nombre del 2ichero de la ruta del 2ichero corres ondiente a un ob=eti'o Sue se encuentre en un subdirectorio.

OA@&B

OA@!B

Programacin de Sistemas

*0

)abla *.4., Lista de las 'ariables autom6ticas m6s comunes en 9a8e2iles. *.4.*.1 Leglas 'irtuales Es relativamente habitual que adem&s de las reglas normales, los ficheros Ba.efile pueden contener reglas virtuales, que no generen un fichero en concreto, sino que sirvan para reali'ar una determinada accin dentro de nuestro proyecto soft5are. ormalmente estas reglas suelen tener un ob"etivo, pero ninguna dependencia. El e"emplo m&s t4pico de este tipo de reglas es la regla 9c lean : que incluyen casi la totalidad de Ba.efiles, utili'ada para 9limpiar: de ficheros e"ecutables y ficheros ob"eto los directorios que haga falta, con el propsito de rehacer todo la prxima ve' que se llame a 9ma8e :1
clean : rm W2 =uego F.o

Esto provocar4a que cuando alguien e"ecutase 9ma8e c lean :, el comando asociado se e"ecutase y borrase el fichero 9= uego: y todos los ficheros ob"eto. Sin embargo, como ya hemos dicho, este tipo de reglas no suelen tener dependencias, por lo que si existiese un fichero que se llamase 9clean: dentro del directorio del Ba.efile, ma8e considerar4a que ese ob"etivo ya est& reali'ado, y no e"ecutar4a los comandos asociados1
txi i@neon:NO touch clean txi i@neon:NO ma8e clean ma8e: Vclean\ est6 actualizado.

+ara evitar este extra2o efecto, podemos hacer uso de un ob"etivo especial de ma8e, .P;7NP. =odas las dependencias que incluyamos en este ob"etivo obviar&n la presencia de un fichero que coincida con su nombre, y se e"ecutar&n los comandos correspondientes. 3s4, si nuestro anterior Ba.efile hubiese a2adido la siguiente l4nea1
.P;7NP : clean

habr4a evitado el anterior problema de manera limpia y sencilla. *.4.*.4 Leglas im l#citas o todos los ob"etivos de un Ba.efile tienen por qu% tener una lista de comandos asociados para poder reali'arse. En ocasiones se definen reglas que slo indican las dependencias necesarias, y es el propio ma8e quien decide cmo se lograr&n cada uno de los ob"etivos. >e&moslo con un e"emplo1
=uego : =uego.o =uego.o : =uego.c

Con un Ba.efile como este, ma8e ver& que para generar 9=uego: es preciso generar previamente 9=uego.o: y para generar 9=uego.o: no existen comandos

Programacin de Sistemas

*3

que lo puedan reali'ar, por lo tanto, ma8e presupone que para generar un fichero ob"eto basta con compilar su fuente, y para generar el e"ecutable final, basta con enla'ar el fichero ob"eto. 3s4 pues, impl4citamente e"ecuta las siguientes reglas1
cc Wc =uego.c Wo =uego.o cc =uego.o Wo =uego

Generando el e"ecutable, mediante llamadas al compilador est&ndar. *.4.*.+ Leglas atrn Las reglas impl4citas que acabamos de ver, tienen su ra'n de ser debido a una serie de 9reglas patrn: que impl4citamente se especifican en los Ba.efiles. osotros podemos redefinir esas reglas, e incluso inventar reglas patrn nuevas. Ee aqu4 un e"emplo de cmo redefinir la regla impl4cita anteriormente comentada1
].o : ].c OA55B OA5!LGGSB OZ $o O@

Es decir, para todo ob"etivo que sea un 9. o: y que tenga como dependencia un 9. c:, e"ecutaremos una llamada al compilador de C *OA55B, con los modificadores que est%n definidos en ese momento *OA5!LGGSB,, compilando la primera dependencia de la regla *OZ, el fichero 9.c:, para generar el propio ob"etivo *O@, el fichero 9.o:,. *.4.*.. "n'ocando al comando ma8e Cuando nosotros invocamos al comando ma8e desde la l4nea de comandos, lo primero que se busca es un fichero que se llama 9GNUma8e2ile:, si no se encuentra se busca un fichero llamado 9ma8e2ile: y si por 0ltimo no se encontrase, se buscar4a el fichero 99a8e2ile:. Si no se encuentra en el directorio actual ninguno de esos tres ficheros, se producir& un error y ma8e no continuar&1
txi i@neon:NO ma8e ma8e: FFF No se es eci2ic ning^n ob=eti'o ? no se encontr ning^n ma8e2ile. Glto.

Existen adem&s varias maneras de llamar al comando ma8e con el ob"eto de hacer una tra'a o de!ug del Ba.efile. Las opciones 9$d:, 9$n:, y 9$R: est&n expresamente indicadas para ello. /tra opcin importante es 9$=N:, donde indicaremos a ma8e que puede e"ecutar hasta 9N: procesos en paralelo, muy 0til para m&quinas potentes. *.4.*./ %=em lo de 9a8e2ile La manera m&s sencilla de entender cmo funciona ma8e es con un Ba.efile de e"emplo1
J 9a8e2ile de e=em lo J J 'ersion :.*

Programacin de Sistemas

,:

J 55 :Y gcc 5!LGGS :Y $7, 97&UL7S Y 'entana.o gestion.o bd.o =uego .P;7NP : clean install all : OA97&UL7SB ].o : ].c OA55B OA5!LGGSB Wc OZ.c Wo O@ 'entana.o : 'entana.c bd.o : bd.c gestion.o : gestion.c 'entana.o bd.o OA55B OA5!LGGSB Wc OZ.c Wo O@ OA55B OF $o O@ =uego: =uego.c 'entana.o bd.o gestion.o OA55B OA5!LGGSB Wc OZ.c Wo O@ OA55B OF $o O@ clean:

rm W2 OA97&UL7SB

install:

c =uego /usr/games/=uego

*.+

Programando en 5 ara GNU/Linux

Llevamos varios apartados hablando de todo lo que rodea a la programacin en G !#Linux, pero no terminamos de entrar en materia. En lo sucesivo comen'aremos desde lo m&s b&sico, para ir posteriormente viendo las llamadas al sistema m&s comunes y terminar con (ntercomunicacin Entre +rocesos *(+C, y soc.ets en redes =C+#(+.

*.+.*

;ola- mundo<

Si hay un programa obligatorio a la hora de empe'ar a programar en un lengua"e de programacin, ese es el m4tico 9Eola, mundoN:. La man4a de utili'ar un programa que saque por pantalla 9Eola, mundoN: para mostrar un programa de e"emplo en un determinado lengua"e se remonta Ouna ve' m&sJ a los or4genes de C y ! (), con Perningan, ;itchie, =hompson y compa24a haciendo de las suyas. +ara programar un 9Eola, mundoN: en C para G !#Linux simplemente tendremos que editar un fichero, 9ho la . c :, que contenga algo similar a esto1

Programacin de Sistemas

,*

Jinclude Zstdio.hK int mainA int argc- char Farg'DE B _ rint2A `;ola- mundo<anb BI return :I

Queda fuera del &mbito de este libro explicar de forma detallada la sintaxis de C, por lo que pasaremos a anali'ar el proceso de compilacin desde nuestro fichero fuente *9ho la . c :, al fichero e"ecutable *9ho la:,1
txi i@neon:NO gcc hola.c Wo hola txi i@neon:NO ./hola ;ola- mundo< txi i@neon:NO

Como podemos observar, el proceso es muy sencillo. Eay que tener especial cuidado en a2adir el directorio a la hora de llamar al e"ecutable *9. / ho la :, porque en G !#Linux la variable PG); no contiene al directorio actual. 3s4, por mucho que hagamos 9cd: para cambiar a un determinado directorio, siempre tendremos que incluir el directorio en la llamada al e"ecutable, en este caso incluimos el directorio actual, es decir, 9.:.

*.+.,

Llamadas sencillas

Con el 9Eola, mundoN: hemos empleado la funcin est&ndar de C, rint2AB. En la librer4a glibC, la librer4a est&ndar de C de G !, rint2AB est& implementada como una serie de llamadas al sistema que seguramente reali'ar&n algo parecido a esto1 A. 3brir el fichero S)&7U) *salida est&ndar, para escritura. ?. 3nali'ar y calcular la cadena que hay que sacar por S)&7U). C. Escribir en el fichero S)&7U) la anterior cadena. En realidad, como vemos, rint2AB desemboca en varias llamadas al sistema, para abrir ficheros, escribir en ellos, etc. +or lo tanto, no siempre que utilicemos una funcin de C se llamar& a una 0nica syscall o llamada al sistema, sino que funciones relativamente comple"as pueden dar lugar a varias sycalls. La manera m&s sencilla de entender el sistema es utili'ando las funciones b&sicas, aquellas que se corresponden fielmente con la syscall a la que llaman. Entre las m&s b&sicas est&n las de mane"o de ficheros. Ra hemos dicho que en ! () en general, y en G !#Linux en particular, todo es un fichero, por lo que estas syscalls son el 38C de la programacin de sistemas en ! (). Comencemos por crear un fichero. Existen dos maneras de abrir un fichero, o enAB y creatAB. 3ntiguamente o enAB slo pod4a abrir ficheros que ya

Programacin de Sistemas

,,

estaban creados por lo que era necesario hacer una llamada a c rea t A B para llamar a o enA B posteriormente. 3 d4a de hoy o enAB es capa' de crear ficheros, ya que se ha a2adido un nuevo par&metro en su prototipo1
int creatA const char F athname- modeCt mode B int o enA const char F athname- int 2lags B int o enA const char F athname- int 2lags- modeCt mode B

Como vemos, la nueva o enAB es una suma de las funcionalidades de la original y de creatAB. /tra cosa que puede llamar la atencin es el hecho de que el tipo del par&metro 9mode: es 9modeCt:. Esta clase de tipos es bastante utili'ado en ! () y suelen corresponder a un 9int: o 9unsigned int: en la mayor4a de los casos, pero se declaran as4 por compatibilidad hacia atr&s. +or ello, para emplear estas syscalls se suelen incluir los ficheros de cabecera1
o enAB Jinclude Zs?s/t? es.hK Jinclude Zs?s/stat.hK Jinclude Z2cntl.hK

El funcionamiento de o enAB es el siguiente1 al ser llamada intenta abrir el fichero indicado en la cadena 9 athname: con el acceso que indica el par&metro 92lags:. Estos 9flags: indican si queremos abrir el fichero para lectura, para escritura, etc. La siguiente tabla especifica los valores que puede tomar este par&metro1
"ndicador 7CL&7NLP 7CRL7NLP 7CL&RL 7CLGN&79 7CS%dU%N)"GL 7C)%9P7LGLP 7C5L%G) 7C%e5L Xalor :::: :::* :::, ::*: ::,: ::4: :*:: :,:: &escri cin %l 2ichero se abre slo ara lectura. %l 2ichero se abre slo ara escritura. %l 2ichero se abre ara lectura ? escritura. %l 2ichero se abre ara ser accedido de 2orma aleatoria At# ico de discosB. %l 2ichero se abre ara ser accedido de 2orma secuencial At# ico de cintasB. %l 2ichero es de car6cter tem oral. %l 2ichero deber6 ser creado si no exist#a re'iamente. Pro'oca Sue la llamada a o en 2alle si se es eci2ica la o cin 7C5L%G) ? el 2ichero ?a exist#a. Si el 2ichero es un dis ositi'o de terminal A))PB- no se con'ertir6 en la terminal de control de roceso A5))PB. !i=a el tamafo del 2ichero a cero b?tes. %l a untador de escritura se situa al 2inal del 2ichero- se escribir6n al 2inal los nue'os datos. La a ertura del 2ichero ser6 no bloSueante. %s eSui'alente a 7CN&%LGP. !uerza a Sue todas las escrituras en el 2ichero se terminen antes de Sue se retorne de la llamada al sistema. %s eSui'alente a 7C!SPN5. Las escrituras en el 2ichero ueden realizarse de manera as#ncrona. %l acceso a disco se roducir6 de 2orma directa.

7CN75))P 7C)LUN5 7CGPP%N& 7CN7N(L75M 7CSPN5 7CGSPN5 7C&"L%5)

:4:: *::: ,::: 4::: *:::: ,:::: 4::::

Programacin de Sistemas 7CLGLG%!"L% 7C&"L%5)7LP 7CN7!7LL7R *::::: ,::::: 4::::: Utilizado slo ara 2icheros extremadamente grandes. %l 2ichero debe ser un directorio. !uerza a no seguir los enlaces simblicos. gtil en entornos cr#ticos en cuanto a seguridad.

,1

)abla *.+.1 Lista de los osibles 'alores del argumento `2lagsb. La lista es bastante extensa y los valores est&n pensados para que sea posible concatenar o sumar varios de ellos, es decir, hacer una /; lgica entre los diferentes valores, consiguiendo el efecto que deseamos. 3s4 pues, podemos ver que en realidad una llamada a c reat A B tiene su equivalente en o enA B, de esta forma1
o enA athname- 7C5L%G) U 7C)LUN5 U 7CRL7NLP- mode B

El argumento 9mode : se encarga de definir los permisos dentro del Sistema de -icheros *de la manera de la que lo hac4amos con el comando 9chmod :,. La lista completa de sus posibles valores es esta1
"ndicador SC"L7); SC"R7); SC"e7); SC"LGLP SC"LGLP SC"LGLP SC"LUSL SC"RUSL SC"eUSL SC"SX)e SC"SG"& SC"SU"& SC"LReU Xalor :::: :::* :::, ::*: ::,: ::4: :*:: :,:: :4:: *::: ,::: 4::: SC"LUSL T SC"RUSL T SC"eUSL SC"LGLP T SC"RGLP T SC"eGLP SC"L7); T SC"R7); T SC"e7); &escri cin Gcti'ar el bit de lectura ara todo los usuarios. Gcti'ar el bit de escritura ara todo los usuarios. Gcti'ar el bit de e=ecucin ara todo los usuarios. Gcti'ar el bit de lectura ara todo los usuarios ertenecientes al gru o. Gcti'ar el bit de escritura ara todo los usuarios ertenecientes al gru o. Gcti'ar el bit de e=ecucin ara todo los usuarios ertenecientes al gru o. Gcti'ar el bit de lectura ara el ro ietario. Gcti'ar el bit de escritura ara el ro ietario. Gcti'ar el bit de e=ecucin ara el ro ietario. Gcti'a el `stic8? bitb en el 2ichero. Gcti'a el bit de SU"& en el 2ichero. Gcti'a el bit de SG"& en el 2ichero. Gcti'ar el bit de lectura- escritura ? e=ecucin ara el ro ietario. Gcti'ar el bit de lectura- escritura ? e=ecucin ara todo los usuarios ertenecientes al gru o. Gcti'ar el bit de lectura- escritura ? e=ecucin ara todo los usuarios.

SC"LReG

SC"LRe7

)abla *.+.4 Lista de los osibles 'alores del argumento `modeb. =odos estos valores se definen en un fichero de cabecera , por lo que conviene incluirlo1
Jinclude Zs?s/stat.hK

!na llamada correcta a o enA B devuelve un entero que corresponde al descriptor de fichero para mane"ar el fichero abierto. Cada proceso mane"a

Programacin de Sistemas

,4

una tabla de descriptores de fichero que le permiten mane"ar dichos ficheros de forma sencilla. (nicialmente las entradas F, A y ? de esa tabla est&n ocupadas por los ficheros S)&"N, S)&7U) y S)&%LL respectivamente, es decir, la entrada est&ndar, la salida est&ndar y la salida de error est&ndar1

!igura *.+.,

Los descri tores de 2ichero iniciales de un roceso.

+odr4amos entender esa tabla de descriptores de fichero como un Eotel en el que inicialmente las tres primeras habitaciones est&n ocupadas por los clientes S)&"N, S)&7U) y S)&%LL. Conforme vayan viniendo m&s clientes *se abran nuevos archivos,, se les ir& acomodando en las siguientes habitaciones. 3s4 un fichero abierto nada m&s iniciarse el proceso, es bastante probable que tenga un descriptor de fichero cercano a ?. En este 9Eotel: siempre se asigna la 9habitacin: m&s ba"a a cada nuevo cliente. Esto habr& que tomarlo en cuenta en futuros programas. 8ien, ya sabemos abrir ficheros y crearlos si no existieran, pero no podemos ir de"ando ficheros abiertos sin cerrarlos convenientemente. Ra sab%is que C se caracteri'a por tratar a sus programadores como personas responsables y no presupone ninguna ni2era del estilo del recolector de basuras, o similares. +ara cerrar un fichero basta con pasarle a la syscall closeAB el descriptor de fichero como argumento1
int closeA int 2dB

;esulta bastante sencillo. >eamos todo esto en accin en un e"emplo1


Jinclude Zs?s/t? es.hK Jinclude Zs?s/stat.hK Jinclude Z2cntl.hK int mainA int argc- char Farg'DE B _ int 2dI i2A A2d Y o enA arg'D*E- 7CL&RL BB YY $* B _ errorA ho enh BI exitA $* BI c

Programacin de Sistemas

,+

rint2A h%l 2ichero abierto tiene el descri tor ]d.anh- 2d BI closeA 2d BI return :I c

(nicialmente tenemos los ficheros de cabecera necesarios, tal y como hemos venido explicando hasta aqu4. Seguidamente declaramos la variable 92d: que contendr& el descriptor de fichero, y reali'amos una llamada a o enA B, guardando en 92d: el resultado de dicha llamada. Si 92d: es OA significa que se ha producido un error al abrir el fichero, por lo que saldremos advirtiendo del error. En caso contrario se contin0a con la e"ecucin del programa, mostrando el descriptor de fichero por pantalla y cerrando el fichero despu%s. El funcionamiento de este programa puede verse aqu41
txi i@neon:NO gcc 2ichero.c Wo 2ichero txi i@neon:NO ./2ichero 2ichero.c %l 2ichero abierto tiene el descri tor 1.

El siguiente paso lgico es poder leer y escribir en los ficheros que mane"emos. +ara ello emplearemos dos syscalls muy similares1 readAB y writeAB. 3qu4 tenemos sus prototipos1
ssizeCt readA int 2d- 'oid Fbu2- sizeCt count B ssizeCt writeA int 2d- 'oid Fbu2- sizeCt count B

La primera de ellas intenta leer 9count: bytes del descriptor de fichero definido en 92d:, para guardarlos en el buffer 9bu2:. $ecimos 9intenta: porque es posible que en ocasiones no consiga su ob"etivo. 3l terminar, readAB devuelve el n0mero de bytes le4dos, por lo que comparando este valor con la variable 9count: podemos saber si ha conseguido leer tantos bytes como ped4amos o no. Los tipos de datos utili'ados para contar los bytes le4dos pueden resultar un tanto extra2os, pero no son m&s que enteros e esta versin de G !#Linux, como se puede ver en el fichero de cabeceras1
txi i@neon:NO gre ssize /usr/include/bits/t? es.h t? ede2 int CCssizeCtI /F )? e o2 a b?te count- or error. F/ txi i@neon:NO gre ssize /usr/include/unistd.h Ji2nde2 CCssizeCtCde2ined t? ede2 CCssizeCt ssizeCtI J de2ine CCssizeCtCde2ined DiE

El uso de la funcin writeAB es muy similar, basta con llenar el buffer 9bu2: con lo que queramos escribir, definir su tama2o en 9count: y especificar el fichero en el que escribiremos con su descriptor de fichero en 92d:. >eamos todo esto en accin en un sencillo e"emplo1
Jinclude Zs?s/t? es.hK Jinclude Zs?s/stat.hK Jinclude Z2cntl.hK

Programacin de Sistemas

,.

Jde2ine S)&7U) * Jde2ine S"H% +*, int mainA int argc- char Farg'DE B _ int 2d- readb?tesI char bu22erDS"H%EI i2A A2d Y o enA arg'D*E- 7CL&RL BB YY $* B _ errorA ho enh BI exitA $* BI c whileA Areadb?tes Y readA 2d- bu22er- S"H% BB <Y : B _ writeA S)&7U)- bu22er- S"H% BI F/ writeA S)&7U)- bu22er- readb?tes BI c closeA 2d BI return :I c

/F

Como se puede observar, inicialmente definimos dos constantes, S)&7U) para decir que el descriptor de fichero que define la salida est&ndar es A, y S "H%, que indica el tama2o del buffer que utili'aremos. Seguidamente declaramos las variables necesarias e intentamos abrir el fichero pasado por par&metro *arg'D*E, con acceso de lectura#escritura. Si se produce un error *la salida de o enAB es OA,, salimos indicando el error, si no, seguimos. $espu%s tenemos un bucle en el que se va a leer del fichero abierto *92d:, de S"H% en S"H% bytes hasta que no quede m&s *readAB devuelva F bytes le4dos,. En cada vuelta del bucle se escribir& lo le4do por la S)&7U), la salida est&ndar. -inalmente se cerrar& el descriptor de fichero con closeAB. En resumidas cuentas este programa lo 0nico que hace es mostrar el contenido de un fichero por la salida est&ndar, parecido a lo que hace el comando 9cat: en la mayor4a de ocasiones. Existe una l4nea de cdigo que est& comentada en el listado anterior1
/F writeA S)&7U)- bu22er- S"H% BI F/

%n esta llamada a writeAB no se est6 teniendo en cuenta lo Sue ha de'uelto la llamada a readAB anterior- sino Sue se ha?a le#do lo Sue se ha?a le#do- se intentan escribir S"H% b?tes- es decir +*, b?tes. jduk suceder6 al llamar al rograma con esta l#nea en lugar de con la otraQ (ien- si el 2ichero Sue asamos como ar6metro es medianamente grande- los rimeros ciclos del bucle while 2uncionar6n correctamente- ?a Sue readAB de'ol'er6 +*, como n^mero de b?tes le#dos- ? writeAB los escribir6 correctamente. Pero en la ^ltima iteracin del bucle- readAB leer6 menos de +*, b?tes- orSue

Programacin de Sistemas

,/

es mu? robable Sue el tamafo del 2ichero asado or ar6metro sea m^lti lo de +*, b?tes. %ntonces- readA B habr6 le#do menos +*, b?tes ? wr i te A B seguir6 tratando de escribir +*, b?tes. resultado es Sue writeAB escribir6 caracteres `basurab Sue encuentran en ese momento en memoria:
txi i@neon:N O gcc 2iles.c $o 2iles txi i@neon:N O ./2iles 2iles.c Jinclude Zs?s/t? es.hK Jinclude Zs?s/stat.hK Jinclude Z2cntl.hK Jde2ine S)&7U) * Jde2ine S"H% +*, int mainAint argc- char Farg'DEB _ int 2d- readb?tesI char bu22erDS"H%EI i2A A2dYo enAarg'D*E- 7CL&RLBB YY $* B _ errorAho enhBI exitA$*BI c whileA Areadb?tesYreadA2d- bu22er- S"H%BB <Y : B _ writeAS)&7U)- bu22er- readb?tesBI F/ writeAS)&7U)- bu22er- S"H%BI c closeA2dBI return :I c @ @Nl@\@mnoj4nojpq]@\@r0noj&s@ hs@\@enojt\@enoj@txi i@neon:N O

no de %l se

/F

=al y como muestra este e"emplo, inicialmente el programa funciona bien, pero si no tenemos en cuenta los bytes le4dos por readAB, al final terminaremos escribiendo caracteres 9basura:. /tra funcin que puede ser de gran ayuda es lsee8AB. Buchas veces no queremos posicionarnos al principio de un fichero para leer o para escribir, sino que lo que nos interesa es posicionarnos en un despla'amiento concreto relativo al comien'o del fichero, o al final del fichero, etc. La funcin lsee8AB nos proporciona esa posibilidad, y tiene el siguiente prototipo1
o22Ct lsee8Aint 2ildes- o22Ct o22set- int whenceBI

Los par&metros que recibe son bien conocidos, 92ildes: es el descriptor de fichero, 9o22set: es el despla'amiento en el que queremos posicionarnos, relativo a lo que indique 9whence:, que puede tomar los siguientes valores1

Programacin de Sistemas "ndicador S%%MCS%) S%%MC5UL S%%MC%N& Xalor : * , &escri cin Posiciona el untero a `o22setb b?tes desde el comienzo del 2ichero. Posiciona el untero a `o22setb b?tes desde la osicin actual del untero.. Posiciona el untero a `o22setb b?tes desde el 2inal del 2ichero.

,0

)abla *.+.+ Lista de los osibles 'alores del argumento `whenceb. +or e"emplo, si queremos leer un fichero y saltarnos una cabecera de ?FF bytes, podr4amos hacerlo as41
Jinclude Zs?s/t? es.hK Jinclude Zs?s/stat.hK Jinclude Z2cntl.hK Jde2ine S)&7U) * Jde2ine S"H% +*, int mainA int argc- char Farg'DE B _ int 2d- readb?tesI char bu22erDS"H%EI i2A A2d Y o enA arg'D*E- 7CL&RL BB YY $* B _ errorA ho enh BI exitA $* BI c lsee8A 2d-,::- S%%MCS%) BI whileA Areadb?tes Y readA 2d- bu22er- S"H% BB <Y : B _ writeA S)&7U)- bu22er- S"H% BI c closeA2dBI return :I

Esta funcin tambi%n utili'a tipos de variables algo 9esot%ricos:, como al igual que los tipos vistos hasta ahora, no son m&s que otra forma de llamar a un entero largo y se mantienen por compatibilidad entre los diferentes ! ()1
o 2 2 C,t que txi t? t? t? i@neon:NO gre o22Ct /usr/include/bits/t? es.h ede2 long int CCo22CtI /F )? e o2 2ile sizes and o22sets. F/ ede2 CCSuadCt CClo22CtI /F )? e o2 2ile sizes and o22sets. F/ ede2 CClo22Ct CCo22.4CtI

Ra sabemos crear, abrir, cerrar, leer y escribir, Scon esto se puede hacer de todoN +ara terminar con las funciones relacionadas con el mane"o de

Programacin de Sistemas

,3

ficheros veremos chmodA B , chownA B y statAB, para modificar el modo y el propietario del fichero, o acceder a sus caracter4sticas, respectivamente. La funcin chmodAB tiene el mismo uso que el comando del mismo nombre1 cambiar los modos de acceso permitidos para un fichero en concreto. +or mucho que estemos utili'ando C, nuestro programa sigue su"eto a las restricciones del Sistema de -icheros, y slo su propietario o root podr&n cambiar los modos de acceso a un fichero determinado. 3l crear un fichero, bien con creatAB o bien con o enAB, %ste tiene un modo que estar& en funcin de la m&scara de modos que est% configurada *ver 9man umas8:,, pero podremos cambiar sus modos inmediatamente haciendo uso de una de estas funciones1
int chmodAconst char F ath- modeCt modeBI int 2chmodAint 2ildes- modeCt modeBI

>iendo el prototipo de cada funcin, podemos averiguar su funcionamiento1 la primera de ellas, chmodAB, modifica el modo del fichero indicado en la cadena 9 ath:. La segunda, 2chmodAB, recibe un descriptor de fichero, 92ildes:, en lugar de la cadena de caracteres con la ruta al fichero. El par&metro 9mode: es de tipo 9modeCt:, pero en G !#Linux es equivalente a usar una variable de tipo entero. Su valor es exactamente el mismo que el que usar4amos al llamar al comando 9chmod:, por e"emplo1
chmodA `/home/txi i/ ruebab- :... BI

+ara modificar el propietario del fichero usaremos las siguientes funciones1


int chownAconst char F ath- uidCt owner- gidCt grou BI int 2chownAint 2d- uidCt owner- gidCt grou BI int lchownAconst char F ath- uidCt owner- gidCt grou BI

Con ellas podremos cambiar el propietario y el grupo de un fichero en funcin de su ruta * chownAB y lchownAB , y en funcin del descriptor de fichero * 2chownAB ,. El propietario *9owner:, y el grupo *9grou :, son enteros que identifican a los usuarios y grupos, tal y como especifican los ficheros 9/etc/ asswd: y 9/etc/grou :. Si fi"amos alguno de esos dos par&metros *9owner: o 9grou :, con el valor OA, se entender& que deseamos que permane'ca como estaba. La funcin lchownAB es id%ntica a chownAB salvo en el tratamiento de enlaces simblicos a ficheros. En versiones de Linux anteriores a ?.A.LA *y distintas de ?.A.@T,, chownAB no segu4a enlaces simblicos. -ue a partir de Linux ?.A.LA cuando chownAB comen' a seguir enlaces simblicos y se cre una nueva syscall, lchownAB, que no segu4a enlaces simblicos. +or lo tanto, si queremos aumentar la seguridad de nuestros programas, emplearemos lchownAB, para evitar malentendidos con enlaces simblicos confusos. Cuando el propietario de un fichero e"ecutable es modificado por un usuario normal *no root,, los bits de S!($ y SG($ se deshabilitan. El est&ndar +/S() no especifica claramente si esto deber4a ocurrir tambi%n cuando root reali'a la misma accin, y el comportamiento de Linux depende

Programacin de Sistemas

1:

de la versin del .ernel que se est% empleando. !n e"emplo de su uso podr4a ser el siguiente1
gidCt gru o Y *::I /F *:: es el G"& del gru o users F/ chownA `/home/txi i/ ruebab- $*- gru oBI

Con esta llamada estamos indicando que queremos modificar el propietario y grupo del fichero 9/ home/ tx i i / rueba :, de"ando el propietario como estaba *JA,, y modificando el grupo con el valor AFF, que corresponde al grupo 9users:1
txi i@neon:NO gre *:: /etc/grou users:x:*:::

Ra slo nos queda saber cmo acceder a las caracter4sticas de un fichero, mediante el uso de la funcin s ta t A.B Esta funcin tiene un comportamiento algo diferente a lo visto hasta ahora1 utili'a una estructura de datos con todas las caracter4sticas posibles de un fichero, y cuando se llama a s ta t A B se pasa una referencia a una estructura de este tipo. 3l final de la syscall, tendremos en esa estructura todas las caracter4sticas del fichero debidamente cumplimentadas. Las funciones relacionadas con esto son las siguientes1
int statAconst char F2ileCname- struct stat Fbu2BI int 2statAint 2iledes- struct stat Fbu2BI int lstatAconst char F2ileCname- struct stat Fbu2BI

Es decir, muy similares a chownA B, 2 chownA B y lchownAB, pero en lugar de precisar los propietarios del fichero, necesitan como segundo par&metro un puntero a una estructura de tipo 9stat:1
struct stat _ de'Ct stCde'I /F dis ositi'o F/ inoCt stCinoI /F numero de inode F/ modeCt stCmodeI /F modo del 2ichero F/ nlin8Ct stCnlin8I /F numero de hard lin8s F/ uidCt stCuidI /F U"& del ro ietarioF/ gidCt stCgidI /F G"& del ro ietario F/ de'Ct stCrde'I /F ti o del dis ositi'o F/ o22Ct stCsizeI /F tamafo total en b?tes F/ bl8sizeCt stCbl8sizeI /F tamafo de bloSue re2erido F/ bl8cntCt stCbloc8sI /F numero de bloSues asignados F/ timeCt stCatimeI /F ultima hora de acceso F/ timeCt stCmtimeI /F ultima hora de modi2icacin F/ timeCt stCctimeI /F ultima hora de cambio en inodo F/ cI

Como vemos, tenemos acceso a informacin muy detallada y precisa del fichero en cuestin. El siguiente e"emplo muestra todo esto en funcionamiento1
Jinclude Zs?s/t? es.hK Jinclude Zs?s/stat.hK Jinclude Z2cntl.hK

Programacin de Sistemas

1*

int mainA int argc- char Farg'DE B _ struct stat estructuraI i2A A lstatA arg'D*E- sestructura B B Z : B _ errorA hlstath BI exitA $* BI c rint2A hPro iedades del 2ichero Z]sKanh- arg'D*E BI rint2A hi$nodo: ]danh- estructura.stCino BI rint2A hdis ositi'o: ]d- ]danh- ma=orA estructura.stCde' BminorA estructura.stCde' B BI rint2A hmodo: ]Joanh- estructura.stCmode BI rint2A h'inculos: ]danh- estructura.stCnlin8 BI rint2A h ro ietario: ]danh- estructura.stCuid BI rint2A hgru o: ]danh- estructura.stCgid BI rint2A hti o del dis ositi'o: ]danh- estructura.stCrde' BI rint2A htamafo total en b?tes: ]ldanh- estructura.stCsize BI rint2A htamafo de bloSue re2erido: ]danhestructura.stCbl8size BI rint2A hnumero de bloSues asignados: ]danhestructura.stCbloc8s BI rint2A hultima hora de acceso: ]shctimeA sestructura.stCatime B BI rint2A hultima hora de modi2icacin: ]shctimeA sestructura.stCmtime B BI rint2A hultima hora de cambio en inodo: ]shctimeA sestructura.stCctime B BI return :I c

Eay algunos detalles destacables en el anterior cdigo1 Eemos llamado a las funciones ma=or A By minor A B , para obtener los bits de mayor peso y de menor peso del campo stCde', con el fin de mostrar la informacin de forma m&s ra'onable. !tili'amos 9]Jo: para mostrar de forma octal el modo de acceso del fichero, sin embargo aparecen m&s cifras octales que las @ que conocemos. Esto es porque tambi%n se nos informa en ese campo del tipo de fichero *si es un directorio, un dispositivo de bloques, secuencial, un -(-/, etc.,. Eemos empleado la funcin ctimeAB para convertir el formato de fecha interno a un formato legible por las personas normales. !n posible resultado de la e"ecucin del cdigo anterior puede ser este1
txi i@neon:NO gcc stat.c $o stat txi i@neon:NO ./stat stat.c Pro iedades del 2ichero Zstat.cK i$nodo: *.3:.1*

Programacin de Sistemas

1,

dis ositi'o: 1- 1 modo: :*::.44 'inculos: * ro ietario: *::: gru o: *:: ti o del dis ositi'o: : tamafo total en b?tes: *,/4 tamafo de bloSue re2erido: 4:3. numero de bloSues asignados: 0 ultima hora de acceso: )ue No' *, *1:11:*+ ,::, ultima hora de modi2icacin: )ue No' *, *1:11:*, ,::, ultima hora de cambio en inodo: )ue No' *, *1:11:*, ,::,

*.+.1

9ane=o de directorios

Ra hemos visto las syscalls m&s b&sicas Oy m&s importantesJ a la hora de mane"ar ficheros, pero muchas veces con esto no basta para funcionar dentro del Sistema de -icheros. =ambi%n es necesario controlar en qu% directorio estamos, cmo crear o borrar un directorio, poder saltar a otro directorio o incluso recorrer un &rbol de directorios al completo. En este apartado estudiaremos cada una de esas funciones detalladamente. Comencemos por lo m&s sencillo1 6dnde estoy7 Es decir, 6cu&l es el directorio de traba"o actual *CU$,7 Las funciones encargada de proporcionarnos ese dato son getcwdA B , getcur rentCd i r CnameA yB getwdAB, y tienen los siguientes prototipos1
char FgetcwdAchar Fbu2- sizeCt sizeBI char FgetCcurrentCdirCnameA'oidBI char FgetwdAchar Fbu2BI

La funcin getcwdAB devuelve una cadena de caracteres con la ruta completa del directorio de traba"o actual, que almacenar& en el buffer 9bu2:, de tama2o 9size:. Si el directorio no cabe en el buffer, retornar& NULL, por lo que es conveniente usar alguna de las otras dos funciones. >eamos un e"emplo de su funcionamiento1
Jinclude Zunistd.hK int mainA int argc- char Farg'DE B _ char bu22erD+*,EI rint2A h%l directorio actual es: ]sanhgetcwdA bu22er- $* B BI return :I c

Este programa funciona correctamente para el directorio actual *9/home/txi i:,, como podemos observar1
txi i@neon:N O ./getcwd %l directorio actual es: /home/txi i

Programacin de Sistemas

11

txi i@neon:N O

/tra posibilidad para obtener el directorio actual podr4a ser la de leer la variable en entorno 9PR& :. Cuando hacemos un 9echo OPR& : en el int%rprete de comandos, conseguimos la misma informacin que getcwdAB. +or lo tanto, podr4amos servirnos de la funcin geten'AB para tomar el valor de la variable de entorno 9PR&:. +ara m&s detalles, consultar la p&gina del man de geten'AB. Si lo que queremos es movernos a otro directorio, deberemos utili'ar alguna de estas funciones1
int chdirAconst char F athBI int 2chdirAint 2dBI

Como en anteriores ocasiones, su funcionamiento es el mismo, slo que en la primera el nuevo directorio de traba"o es pasado como una cadena de caracteres, y en la segunda como un descriptor de fichero previamente abierto. 3mbas devuelven F si todo ha ido bien, y OA si se ha producido alg0n error. +ara crear y borrar directorios tenemos una serie de funciones a nuestra disposicin, con prototipos muy familiares1
int m8dirAconst char F athname- modeCt modeBI int rmdirAconst char F athnameBI

3mbas son el fiel refle"o de los comandos que representan1 rmdirAB borra el directorio especificado en 9 athname: y exige que %ste est% vac4o, m8dirAB crea el directorio especificado en 9 athname:, con el modo de acceso especificado en el par&metro 9mode: *t4picamente un valor octal como 9:/++:, etc.,. !n e"emplo de su mane"o aclarar& todas nuestras posibles dudas1
Jinclude Zunistd.hK int mainA int argc- char Farg'DE B _ char bu22erD+*,EI rint2A h%l directorio actual es: ]sanhgetcwdA bu22er- $* B BI chdirA h..h BI m8dirA h./directorio*h- :/++ BI m8dirA h./directorio,h- :/++ BI rmdirA h./directorio*h BI return :I c

+robemos a ver si todo funciona correctamente1


txi txi txi txi i@neon:NO i@neon:NO i@neon:NO i@neon:NO gcc directorios.c $o directorios m8dir rueba m' directorios rueba/ cd rueba/

Programacin de Sistemas

14

txi i@neon:N/ ruebaO ./directorios %l directorio actual es: /home/txi i/ rueba txi i@neon:N/ ruebaO ls directorios txi i@neon:N/ ruebaO cd .. txi i@neon:NO ls directorios.c directorio, txi i@neon:NO ls $ld directorio,/ drwxr$xr$x , txi i users 4:3. ,::,$**$*, *3:** directorio,/

+arece que s4. $e momento estamos teniendo bastante suerte, pero porque todo lo visto hasta ahora era muy f&cil. >amos a ver si somos capaces de darle m&s vidilla a esto, y poder hacer un recorrido de directorios a trav%s de las complicadas y tenebrosas estructuras d i rent . Las funciones relacionadas con el listado de directorios son las siguientes1
&"L Fo endirAconst char FnameBI struct dirent FreaddirA&"L FdirBI int closedirA&"L FdirBI

Con la primera de ellas conseguimos una variable de tipo &"L en funcin de una ruta definida por la cadena de caracteres 9name :. !na ve' obtenida dicha variable de tipo &"L, se la pasamos como par&metro a la funcin readdirAB, que nos proporcionar& un puntero a una estructura de tipo dirent, es decir, a la entrada del directorio en concreto a la que hemos accedido. En esa estructura dirent tendremos todos los datos de la entrada de directorio que a la que estamos accediendo1 inodo, distancia respecto del comien'o de directorio, tama2o de la entrada y nombre1
struct dirent _ inoCt dCinoI // numero de i$node de la entrada de directorio o22Ct dCo22I // o22set wcharCt dCreclenI // longitud de este registro char dCnameD9GeCL7NGCNG9%T*E // nombre de esta entrada c

3 primera vista parece comple"a, pero ya hemos lidiado con estructuras m&s grandes como stat, y, adem&s, slo nos interesa el 0ltimo campo. 8ueno, ya estamos en disposiciones de recorrer un directorio1 lo abriremos con o endirAB, iremos leyendo cada una de sus entradas con readdirAB hasta que no queden m&s *readdirAB no devuelva NULL,, y cerraremos el directorio con closedirAB, es simple1
Jinclude Zstdio.hK Jinclude Zstdlib.hK Jinclude Zdirent.hK int mainA int argc- char Farg'DE B _ &"L FdirI struct dirent FmiCdirentI i2A argc <Y , B _ rint2A h]s: ]s directorioanh- arg'D:E- arg'D:E BI

Programacin de Sistemas

1+

exitA $* BI c i2A Adir Y o endirA arg'D*E BB YY NULL B _ errorA ho endirh BI exitA $* BI c whileA AmiCdirent Y readdirA dir BB <Y NULL B rint2A h]sanh- miCdirent$KdCname BI closedirA dir BI return :I c

El resultado de la e"ecucin de este programa se parece mucho al esperado1


txi i@neon:NO gcc dirs.c $o dirs txi i@neon:NO ./dirs ./dirs: ./dirs directorio txi i@neon:NO ./dirs . . .. 2iles.c 2iles stat.c stat ma8e2ile clean getcwd.c getcwd directorios.c dirs.c rueba directorio, dirs

*.+.4

>ugando con los ermisos

3ntes de meternos con la comunicacin entre procesos me gustar4a comentar algunas curiosidades sobre los permisos en G !#Linux. Como ya hemos dicho al principio de este cap4tulo, mientras un programa se est& e"ecutando dispone de una serie de credenciales que le permiten acreditarse frente al sistema a la hora de acceder a sus recursos, es decir, son como la tar"eta de acceso en un edificio muy burocrati'ado como pueda ser el +ent&gono1 si tu tar"eta es de nivel <, no puedes acceder a salas de nivel T o superior, las puertas no se abren *y adem&s es probable que quede un registro de tus intentos fallidos,. $entro de esas credenciales, las que m&s se suelen utili'ar son el u id y el g id, as4 como el euid y el egid. Estas dos pare"as informan de qu% usuario real y efectivo est& e"ecutando el programa en cuestin, para dotarle de unos privilegios o de otros.

Programacin de Sistemas

1.

+ara la mayor4a de programas, con el eu id es suficiente1 si eres 9efectivamente: el usuario root, tienes privilegios de root durante la e"ecucin de esa tarea, a pesar de que tu usuario real sea otro. Esto sucede mucho en e"ecutables que tienen el bit de S!($ activado1 convierten a quien los e"ecuta en el usuario propietario de ese e"ecutable. Si dicho usuario era root, al e"ecutarlos te conviertes moment&neamente en root. Esto permite, por e"emplo, que un usuario normal pueda cambiar su contrase2a, es decir, modificar el fichero 9/ e tc / shadow :, a pesar de no tener grandes privilegios en el sistema. El comando 9 asswd: hace de puerta de enlace, por as4 llamarlo, entre la peticin del usuario y la modificacin del fichero protegido1
txi i@neon:NO ls $l /etc/shadow $rw$r$$$$$ * root shadow *10: ,::,$**$*, ,::*, /etc/shadow txi i@neon:NO asswd txi i 5hanging assword 2or txi i AcurrentB UN"e assword: (ad: new and old assword are too similar Ahummmm...B %nter new UN"e assword: Let? e new UN"e assword: (ad: new assword is too sim le Aarghhh<<<<B Let? e new UN"e assword: %nter new UN"e assword: asswd: assword u dated success2ull? Au222<<B txi i@neon:NO which asswd /usr/bin/ asswd txi i@neon:NO ls $l /usr/bin/ asswd $rwsr$xr$x * root root ,+.4: ,::,$*:$*4 :4::+ /usr/bin/ asswd

Como vemos inicialmente, el fichero 9/etc/shadow: est& protegido contra escritura para todos los usuarios excepto para root, y aun as4 *Sdespu%s de desesperarme un pocoN,, he podido cambiar mi contrase2a, es decir, modificarlo. Esto es posible gracias a que el programa 9/usr/bin/ asswd: que he utili'ado, tiene a root como propietario, y el bit de S!($ activado *9$rw2r$xr$ x:,. 6Cmo gestionar todo esto en nuestros programas en C7 !tili'ando las siguientes funciones1
uidCt getuidA'oidBI uidCt geteuidA'oidBI int setuidAuidCt uidBI int seteuidAuidCt euidBI int setreuidAuidCt ruid- uidCt euidBI

Con las dos primeras obtenemos tanto el uid como el euid del proceso en e"ecucin. Esto puede resultar 0til para hacer comprobaciones previas. El programa 9nma :, por e"emplo, comprueba si tienes privilegios de root *es decir, si euid es F, antes de intentar reali'ar ciertas cosas. Las otras tres funciones sirven para cambiar nuestro uid, euid o ambos, en funcin de las posibilidades, esto es, siempre y cuando el sistema nos lo permita1 bien porque somos root, bien porque queremos degradar nuestros privilegios. Las tres retornan F si todo ha ido bien, o OA si ha habido alg0n error. Si les pasamos OA como par&metro, no har&n ning0n cambio, por lo tanto1

Programacin de Sistemas

1/

setuidAuidCt uidB eSui'ale a setreuidAuidCt ruid- $*B seteuidAuidCt euidB eSui'ale a setreuidA$*- uidCt euidBI

3nalicemos ahora un caso curioso1 antiguamente, cuando no se utili'aba bash como int%rprete de comandos, algunos intrusos utili'aban una t%cnica que se conoce vulgarmente con el nombre de 9mochila: o 9puerta trasera:. Esta t%cnica se basaba en el hecho de que una ve' conseguido un acceso como root al sistema, se de"aba una puerta trasera para lograr esos privilegios el resto de veces que se quisiera, de la siguiente forma1
neon:NJ cd /'ar/tm / neon:/'ar/tm J c /bin/sh . neon:/'ar/tm J chmod Ts sh neon:/'ar/tm J m' sh .,1erw=itc1tS1.sw

+rimero consegu4an acceso como root *de la forma que fuera,, seguidamente copiaban en un lugar seguro una copia de un int%rprete de comandos, y habilitaban su bit de S!($. -inalmente lo escond4an ba"o una apariencia de fichero temporal. La prxima ve' que ese intruso accediese al sistema, a pesar de no ser root y de que root haya parcheado el fallo que dio lugar a esa escalada de privilegios *fallo en alg0n servicio, contrase2a sencilla, etc.,, utili'ando esa 9mochila: podr& volver a tener una shell de root1
txi i@neon:NO /'ar/tm /.,1erw=itc1tS1.sw sh$,.:+bJ whoami root sh$,.:+bJ

3ctualmente, con bash, esto no pasa. 8ash es un poco m&s precavida y se cuida mucho de las shells con el bit de S!($ activado. +or ello, adem&s de fi"arse slo en el eu id del usuario que llama a bash, comprueba tambi%n el u id. !tili'ando las funciones que hemos visto, seremos capaces de enga2ar completamente a bash1
Jinclude Zstdio.hK Jinclude Zunistd.hK Jinclude Zs?s/t? es.hK int mainA int argc- char FFarg' B _ uidCt uid- euidI uid Y getuidABI euid Y geteuidABI setreuidA euid- euid BI s?stemA h/bin/bashh BI return :I

Programacin de Sistemas

10

$e esta manera, "usto antes de llamar a 9/ b in /bash : nos hemos asegurado de que tanto el u id como el euid corresponden a root y la 9mochila: funcionar&1
neon:/'ar/tm J gcc mochila.c $o .,1erw=itc1tS1.sw neon:/'ar/tm J chmod Ts .,1erw=itc1tS1.sw neon:/'ar/tm J ls $l .,1erw=itc1tS1.sw $rwsr$sr$x * root root +::1 ,::,$**$*, ,::+, .,1erw=itc1tS1.sw neon:/'ar/tm J exit exit txi i@neon:NO /'ar/tm /.,1erw=itc1tS1.sw sh$,.:+bJ whoami root sh$,.:+bJ

+or este tipo de "ueguitos es por los que conviene revisar a diario los cambios que ha habido en los S!($s del sistema VJ,

*.+.+

5reacin ? du licacin de rocesos

!na situacin muy habitual dentro de un programa es la de crear un nuevo proceso que se encargue de una tarea concreta, descargando al proceso principal de tareas secundarias que pueden reali'arse as4ncronamente o en paralelo. Linux ofrece varias funciones para reali'ar esto1 s?stemAB, 2or8AB y execAB. Con s?stemAB nuestro programa consigue detener su e"ecucin para llamar a un comando de la shell *9/bin/sh: t4picamente, y retornar cuando ste haya acabado. Si la shell no est& disponible, retorna el valor A?W, o OA si se produce un error de otro tipo. Si todo ha ido bien, s?stemAB devuelve el valor de retorno del comando e"ecutado. Su prototipo es el siguiente1
int s?stemAconst char FstringBI

$onde 9string: es la cadena que contiene el comando que queremos e"ecutar, por e"emplo1
s?stemA`clearbBI

Esta llamada limpiar4a de caracteres la terminal, llamando al comando 9clear:. Este tipo de llamadas a s?stemAB son muy peligrosas, ya que si no indicamos el PG); completo *9/usr/bin/clear:,, alguien que cono'ca nuestra llamada *bien porque anali'a el comportamiento del programa, bien por usar el comando strings, bien porque es muy muy muy saga',, podr4a modificar el PG); para que apunte a su comando clear y no al del sistema *imaginemos que el programa en cuestin tiene privilegios de root y ese clear se cambia por una copia de /bin/sh1 el intruso conseguir4a una shell de root,. La funcin s?stemAB bloquea el programa hasta que retorna, y adem&s tiene problemas de seguridad impl4citos, por lo que desaconse"o su uso m&s all& de programas simples y sin importancia.

Programacin de Sistemas

13

La segunda manera de crear nuevos procesos es mediante 2 o r8 A.B Esta funcin crea un proceso nuevo o 9proceso hi"o: que es exactamente igual que el 9proceso padre:. Si 2 o r8 A B se e"ecuta con %xito devuelve1 3l padre1 el +($ del proceso hi"o creado. 3l hi"o1 el valor F. +ara entendernos, 2 o r8 A B clona los procesos *bueno, realmente es c lone A B quien clona los procesos, pero 2or8AB hace algo bastante similar,. Es como una m&quina para replicar personas1 en una de las dos cabinas de nuestra m&quina entra una persona con una pi'arra en la mano. Se activa la m&quina y esa persona es clonada. En la cabina contigua hay una persona id%ntica a la primera, con sus mismos recuerdos, misma edad, mismo aspecto, etc. pero al salir de la m&quina, las dos copias miran sus pi'arras y en la de la persona original est& el n0mero de copia de la persona copiada y en la de la 9persona copia: hay un cero1

!igura *.+.1

&u licacin de rocesos mediante 2or8AB.

En la anterior figura vemos como nuestro incauto voluntario entra en la m&quina replicadora con la pi'arra en blanco. Cuando la activamos, tras una descarga de neutrinos capa' de provocarle anginas a ;adiactivoman, obtenemos una copia exacta en la otra cabina, slo que en cada una de las pi'arras la m&quina ha impreso valores diferentes1 9A?C:, es decir, el identificativo de la copia, en la pi'arra del original, y un 9F: en la pi'arra de la copia. o hace falta decir que suele ser bastante traum&tico salir de una m&quina como esta y comprobar que tu pi'arra tiene un 9F:, darte cuenta que no eres m&s que una vulgar copia en este mundo. +or suerte, los procesos no se deprimen y siguen funcionando correctamente. >eamos el uso de 2or8AB con un sencillo e"emplo1
Jinclude Zs?s/t? es.hK Jinclude Zunistd.hK Jinclude Zstdio.hK int mainAint argc- char Farg'DEB _ idCt idI i2 A A idY2or8ABB YY : B

Programacin de Sistemas

4:

_ /F hi=o F/ rint2AhSo? el hi=o A]d- hi=o de ]dBanh- get idABget idABBI c else _ /F adre F/ rint2AhSo? el adre A]d- hi=o de ]dBanh- get idABget idABBI c return :I c

Guardamos en la variable 9 id: el resultado de 2 o r8 A.B Si es F, resulta que estamos en el proceso hi"o, por lo que haremos lo que tenga que hacer el hi"o. Si es distinto de cero, estamos dentro del proceso padre, por lo tanto todo el cdigo que vaya en la parte 9else: de esa condicional slo se e"ecutar& en el proceso padre. La salida de la e"ecucin de este programa es la siguiente1
txi i@neon:NO gcc 2or8.c Wo 2or8 txi i@neon:NO ./2or8 So? el adre A+.3- hi=o de 1*4B So? el hi=o A+/:- hi=o de +.3B txi i@neon:NO gre bash 1*4

La salida de las dos llamadas a rint2AB, la del padre y la del hi"o, son as4ncronas, es decir, podr4a haber salido primero la del hi"o, ya que est& corriendo en un proceso separado, que puede e"ecutarse antes en un entorno multiprogramado. El hi"o, <WF, afirma ser hi"o de <TD, y su padre, <TD, es a su ve' hi"o de la shell en la que nos encontramos, CA@. Si quisi%ramos que el padre esperara a alguno de sus hi"os deberemos dotar de sincronismo a este programa, utili'ando las siguientes funciones1
idCt waitAint FstatusB idCt wait idA idCt id- int Fstatus- int o tionsBI

La primera de ellas espera a cualquiera de los hi"os y devuelve en la variable entera 9status: el estado de salida del hi"o *si el hi"o ha acabado su e"ecucin sin error, lo normal es que haya devuelto cero,. La segunda funcin, wait idAB, espera a un hi"o en concreto, el que especifiquemos en 9 id:. Ese +($ o identificativo de proceso lo obtendremos al hacer la llamada a 2or8AB para ese hi"o en concreto, por lo que conviene guardar el valor devuelto por 2or8AB. En el siguiente e"emplo combinaremos la llamada a wait idAB con la creacin de un &rbol de procesos m&s comple"o, con un padre y dos hi"os1
Jinclude Zs?s/t? es.hK Jinclude Zunistd.hK Jinclude Zstdio.hK int mainAint argc- char Farg'DEB

Programacin de Sistemas

4*

idCt id*- id,I int status*- status,I i2 A A id*Y2or8ABB YY : B _ /F hi=o F/ rint2AhSo? el rimer hi=o A]d- hi=o de ]dBanhget idAB- get idABBI c else _ /F adre F/ i2 A A id,Y2or8ABB YY : B _ /F segundo hi=o F/ rint2AhSo? el segundo hi=o A]d- hi=o de ]dBanhget idAB- get idABBI c else _ /F adre F/ /F %s eramos al rimer hi=o F/ wait idA id*- sstatus*- :BI /F %s eramos al segundo hi=o F/ wait idA id,- sstatus,- :BI rint2AhSo? el adre A]d- hi=o de ]dBanhget idAB- get idABBI c c return :I

El resultado de la e"ecucin de este programa es este1


txi i@neon:NO gcc doshi=os.c Wo doshi=os txi i@neon:NO ./ doshi=os So? el rimer hi=o A*++:1- hi=o de *++:,B So? el segundo hi=o A*++:4- hi=o de *++:,B So? el adre A*++:,- hi=o de *+4/*B txi i@neon:NO gre bash *+4/*

Con wai t id A aseguramos B que el padre va a esperar a sus dos hi"os antes de continuar, por lo que el mensa"e de 9So? e l adre: . .siempre . saldr& el 0ltimo. Se pueden crear &rboles de procesos m&s comple"os, veamos un e"emplo de un proceso hi"o que tiene a su ve' otro hi"o, es decir, de un proceso abuelo, otro padre y otro hi"o1
Jinclude Zs?s/t? es.hK Jinclude Zunistd.hK Jinclude Zstdio.hK int mainAint argc- char Farg'DEB _ idCt id*- id,I int status*- status,I

Programacin de Sistemas

4,

i2 A A id*Y2or8ABB YY : B _ /F hi=o A*a generacionB Y adre F/ i2 A A id,Y2or8ABB YY : B _ /F hi=o A,a generacionB Y nieto F/ rint2AhSo? el nieto A]d- hi=o de ]dBanhget idAB- get idABBI c else _ /F adre A,a generacionB Y adre F/ waitAsstatus,BI rint2AhSo? el adre A]d- hi=o de ]dBanhget idAB- get idABBI c c else _ /F adre A*a generacionB Y abuelo F/ waitAsstatus*BI rint2AhSo? el abuelo A]d- hi=o de ]dBanh- get idABget idABBI c return :I

R el resultado de su e"ecucin ser4a1


txi i@neon:NO gcc hi=o adrenieto.c $o hi=o adrenieto txi i@neon:NO ./hi=o adrenieto So? el nieto A*++.+- hi=o de *++.4B So? el adre A*++.4- hi=o de *++.1B So? el abuelo A*++.1- hi=o de *+4/*B txi i@neon:NO gre bash *+4/*

=al y como hemos dispuesto las llamadas a wai t A , B parad"icamente el abuelo esperar& a que se muera su hi"o *es decir, el padre,, para terminar, y el padre a que se muera su hi"o *es decir, el nieto,, por lo que la salida de este programa siempre tendr& el orden1 nieto, padre, abuelo. Se pueden hacer &rboles de procesos mucho m&s comple"os, pero una ve' visto cmo hacer m0ltiples hi"os y cmo hacer m0ltiples generaciones, el resto es bastante trivial. /tra manera de crear nuevos procesos, bueno, m&s bien de modificar los existentes, es mediante el uso de las funciones exec A B . Con estas funciones lo que conseguimos es reemplazar la imagen del proceso actual por la de un comando o programa que invoquemos, de manera similar a como lo hac4amos al llamar a s?s temA B . En funcin de cmo queramos reali'ar esa llamada, elegiremos una de las siguientes funciones1
int execlA const char F ath- const char Farg- ...BI int execl A const char F2ile- const char Farg- ...BI int execleA const char F ath- const char Farg - ...char F const en' DEBI int exec'A const char F ath- char Fconst arg'DEBI int exec' A const char F2ile- char Fconst arg'DEBI int exec'e Aconst char F2ilename- char Fconst arg' DEchar Fconst en' DEBI

Programacin de Sistemas

41

El primer argumento es el fichero e"ecutable que queremos llamar. Las funciones que contienen puntos suspensivos en su declaracin indican que los par&metros del e"ecutable se incluir&n ah4, en argumentos separados. Las funciones terminadas en 9e: * exec le A B y exec'e A B , reciben un 0ltimo argumento que es un puntero a las variables de entorno. !n e"emplo sencillo nos sacar& de dudas1
Jinclude Zunistd.hK Jinclude Zstdlib.hK Jinclude Zstdio.hK int mainAint argc- char Farg'DEB _ char FargsDE Y _ h/bin/lsh- NULL cI exec'Ah/bin/lsh- argsBI rint2AhSe ha roducido un error al e=ecutar exec'.anhBI return :I

La funcin elegida, exec'A B , recibe dos argumentos, el path al fichero e"ecutable *9/ b in / :, l s y un array con los par&metros que queremos pasar. Este array tiene la misma estructura que arg'DE, es decir, su primer elemento es el propio programa que queremos llamar, luego se va rellenando con los argumentos para el programa y por 0ltimo se finali'a con un puntero nulo *NULL,. El rint2AB final no deber4a salir nunca, ya que para ese entonces exec'AB se habr& encargado de reempla'ar la imagen del proceso actual con la de la llamada a 9/bin/ls:. La salida de este programa es la siguiente1
txi i@neon:NO gcc exec'.c $o exec' txi i@neon:NO./exec' doshi=os exec' 2il, 2iles.c hi=o adrenieto.c doshi=os.c exec'.c 2il,.c hi=o adrenieto

*.+..

5omunicacin entre rocesos

En un sistema multiprogramado, con un montn de procesos funcionando al mismo tiempo, es necesario establecer mecanismos de comunicacin entre los procesos para que puedan colaborar entre ellos. Existen varios enfoques a la hora de implementar esta comunicacin. +odemos considerar a las se2ales como la forma m&s primitiva de comunicacin entre procesos. El sistema utili'a se2ales para informar a un determinado proceso sobre alguna condicin, reali'ar esperas entre procesos, etc. Sin embargo la se2al en s4 no es portadora de datos, a fin de cuentas es una 9se2a: que se hacen de un proceso a otro, que permite a un proceso enterarse de una determinada condicin, pero sin poder transmitirse cantidades grandes de informacin entre ambos procesos. !n gesto con la mano puede servirte para detenerte mientras vas andando por

Programacin de Sistemas

44

un pasillo, pero dif4cilmente te transmitir& toda la informacin contenida en 9El Qui"ote: *al menos con las t%cnicas que yo cono'co,. +or lo tanto, adem&s de las se2ales, es preciso disponer de mecanismos que permitan intercambiar datos entre los procesos. El enfoque m&s obvio de todos es utili'ar ficheros del sistema para poder escribir y leer de ellos, pero esto es lento, poco eficiente e inseguro, aunque muy sencillo de hacer. El siguiente paso podr4a ser utili'ar una tuber4a o un -(-/ para intercomunicar los procesos a trav%s de %l. El rendimiento es superior respecto al enfoque anterior, pero slo se utili'an en casos sencillos. (maginemos lo costoso que ser4a implementar un mecanismo de sem&foros de esta manera. Como evolucin de todo lo anterior lleg el sistema (+C *(nter +rocess Communication, de System >, con sus tres tipos de comunicacin diferentes1 sem&foros, colas de mensa"es y segmentos de memoria compartida. 3ctualmente el est&ndar de (+C System > ha sido reempla'ado por otro est&ndar, el (+C +/S(). 3mbos implementan caracter4sticas avan'adas de los sistemas de comunicacin entre procesos de manera bastante eficiente, por lo que convendr4a pensar en su empleo a la hora de reali'ar una aplicacin multiproceso bien dise2ada. *.+...* Sefales Cuando implementamos un programa, l4nea a l4nea vamos definiendo el curso de e"ecucin del mismo, con condicionales, bucles, etc. Sin embargo hay ocasiones en las que nos interesar4a contemplar sucesos as4ncronos, es decir, que pueden suceder en cualquier momento, no cuando nosotros los comprobemos. La manera m&s sencilla de contemplar esto es mediante el uso de se2ales. La p%rdida de la conexin con el terminal, una interrupcin de teclado o una condicin de error como la de un proceso intentando acceder a una direccin inexistente de memoria podr4an desencadenar que un proceso recibiese una se2al. !na ve' recibida, es tarea del proceso atrapar o capturarla y tratarla. Si una se2al no se captura, el proceso muere. En funcin del sistema en el que nos encontremos, bien el n0cleo del Sistema /perativo, bien los procesos normales pueden elegir entre un con"unto de se2ales predefinidas, siempre que tengan los privilegios necesarios. Es decir, no todos los procesos se pueden comunicar con procesos privilegiados mediante se2ales. Esto provocar4a que un usuario sin privilegios en el sistema ser4a capa' de matar un proceso importante mandando una se2al S "GM "LL , por e"emplo. +ara mostrar las se2ales que nos proporciona nuestro n0cleo y su identificativo num%rico asociado, usaremos el siguiente comando1
txi i@neon:NO 8ill $l *B S"G;UP ,B S"G"N) 1B S"GdU") 4B S"G"LL +B S"G)LGP .B S"GG(L) /B S"G(US 0B S"G!P% 3B S"GM"LL *:B S"GUSL* **B S"GS%GX *,B S"GUSL, *1B S"GP"P% *4B S"GGLL9 *+B S"G)%L9 */B S"G5;L& *0B S"G57N) *3B S"GS)7P ,:B S"G)S)P ,*B S"G))"N ,,B S"G))7U ,1B S"GULG ,4B S"Ge5PU ,+B S"Ge!SH

Programacin de Sistemas

4+

,.B S"GX)GLL9 ,/B S"GPL7! ,0B S"GR"N5; ,3B S"G"7 1:B S"GPRL 1*B S"GSPS 1,B S"GL)9"N 11B S"GL)9"NT* 14B S"GL)9"NT, 1+B S"GL)9"NT1 1.B S"GL)9"NT4 1/B S"GL)9"NT+ 10B S"GL)9"NT. 13B S"GL)9"NT/ 4:B S"GL)9"NT0 4*B S"GL)9"NT3 4,B S"GL)9"NT*: 41B S"GL)9"NT** 44B S"GL)9"NT*, 4+B S"GL)9"NT*1 4.B S"GL)9"NT*4 4/B S"GL)9"NT*+ 40B S"GL)9Ge$*+ 43B S"GL)9Ge$*4 +:B S"GL)9Ge$*1 +*B S"GL)9Ge$*, +,B S"GL)9Ge$** +1B S"GL)9Ge$*: +4B S"GL)9Ge$3 ++B S"GL)9Ge$0 +.B S"GL)9Ge$/ +/B S"GL)9Ge$. +0B S"GL)9Ge$+ +3B S"GL)9Ge$4 .:B S"GL)9Ge$1 .*B S"GL)9Ge$, .,B S"GL)9Ge$* .1B S"GL)9Ge

La mayor4a de los identificativos num%ricos son los mismos en diferentes arquitecturas y sistemas ! (), pero pueden cambiar, por lo que conviene utili'ar el nombre de la se2al siempre que sea posible. Linux implementa las se2ales usando informacin almacenada en la tas8Cs t rucdel t proceso. El n0mero de se2ales soportadas est& limitado normalmente al tama2o de palabra del procesador. 3nteriormente, slo los procesadores con un tama2o de palabra de T@ bits pod4an mane"ar hasta T@ se2ales, pero en la versin actual del .ernel *?.@.AD, disponemos de T@ se2ales incluso en arquitecturas de C?bits. =odas las se2ales pueden ser ignoradas o bloqueadas, a excepcin de y S "GM "LL , que son imposibles de ignorar. En funcin del tratamiento que especifiquemos para cada se2al reali'aremos la tarea predeterminada, una propia definida por el programador, o la ignoraremos *siempre que sea posible,. Es decir, nuestro proceso modifica el tratamiento por defecto de la se2al reali'ando llamadas al sistema que alteran la sigaction de la se2al apropiada. +ronto veremos cmo utili'ar esas llamadas al sistema en C.
S "GS)7P

!na limitacin importante de las se2ales es que no tienen prioridades relativas, es decir, si dos se2ales llegan al mismo tiempo a un proceso puede que sean tratadas en cualquier orden, no podemos asegurar la prioridad de una en concreto. /tra limitacin es la imposibilidad de tratar m0ltiples se2ales iguales1 si nos llegan A@ se2ales S "G57N) a la ve', por e"emplo, el proceso funcionar& como si hubiera recibido slo una. Cuando queremos que un proceso espere a que le llegue una se2al, usaremos la funcin auseA B . Esta funcin provoca que el proceso *o thread, en cuestin 9duerma: hasta que le llegue una se2al. +ara capturar esa se2al, el proceso deber& haber establecido un tratamiento de la misma con la funcin s igna l A . B3qu4 tenemos los prototipos de ambas funciones1
int auseA'oidBI t? ede2 'oid AFsighandlerCtBAintBI sighandlerCt signalAint signum- sighandlerCt handlerBI

La funcin auseA B no parece tener demasiada complicacin1 no recibe ning0n par&metro y retorna OA cuando la llamada a la funcin que captura la se2al ha terminado. La funcin s igna l A tiene B un poco m&s de miga1 recibe dos par&metros, el n0mero de se2al que queremos capturar *los n0meros en el sistema en concreto en el que nos encontremos los podemos obtener e"ecutando 98ill Wl:, como ya hemos visto,, y un puntero a una funcin que se

Programacin de Sistemas

4.

encargar& de tratar la se2al especificada. Esto puede parecer confuso, as4 que aclaremos esto con un e"emplo1
Jinclude Zsignal.hK Jinclude Zunistd.hK 'oid tra erAintBI

int mainAint argc- char Farg'DEB _ int iI 2orAiY*IiZY.4IiTTB signalAi- tra erBI rint2Ah"denti2icati'o de roceso: ]danh- get idAB BI auseABI rint2Ah5ontinuando...anhBI return :I c 'oid tra erAint sigB _ signalAsig- tra erBI rint2AhLecibida la sefal: ]danh- sigBI c

La explicacin de este peque2o programa es bastante simple. (nicialmente declaramos una funcin que va a recibir un entero como par&metro y se encargar& de capturar una se2al * t ra er A B ,. Seguidamente capturamos todas las se2ales de A a T@ haciendo T@ llamadas a s igna l A ,B pasando como primer par&metro el n0mero de la se2al *i, y como segundo par&metro la funcin que se har& cargo de dicha se2al *tra er,. Seguidamente el programa indica su +($ llamando a get idAB y espera a que le llegue una se2al con la funcin auseAB. El programa esperar& indefinidamente la llegada de esa se2al, y cuando le enviemos una *por e"emplo, pulsando ControlGC,, la funcin encargada de gestionarla * tra erAB , ser& invocada. Lo primero que hace tra erAB es volver a enla'ar la se2al en cuestin a la funcin encargada de gestionarla, es decir, ella misma, y luego saca por la salida est&ndar la se2al recibida. 3l terminal la e"ecucin de tra erAB, se vuelve al punto donde est&bamos * auseAB , y se continua1
txi i@neon:NO gcc tra er.c $o tra txi i@neon:NO ./tra er "denti2icati'o de roceso: *+/:, Lecibida la sefal: , 5ontinuando... txi i@neon:NO er

Como podemos observar, capturar una se2al es bastante sencillo. (ntentemos ahora ser nosotros los emisores de se2ales a otros procesos. Si queremos enviar una se2al desde la l4nea de comandos, utili'aremos el comando 98ill:. La funcin de C que hace la misma labor se llama,

Programacin de Sistemas

4/

originalmente, 8 i l l .A Esta B funcin puede enviar cualquier se2al a cualquier proceso, siempre y cuando tengamos los permisos adecuados *las credenciales de cada proceso, explicadas anteriormente, entran ahora en "uego *u id, euid, etc., ,. Su prototipo es el siguiente1
int 8illA idCt id- int sigBI

o tiene mucha complicacin, recibe dos par&metros, el +($ del proceso que recibir& la se2al, y la se2al. El tipo idCt es un tipo heredado de ! (), que en Linux en concreto corresponde con un entero. El siguiente cdigo de e"emplo reali'a la misma funcin que el comando 98ill:1
Jinclude Zs?s/t? es.hK Jinclude Zsignal.hK Jinclude Zunistd.hK int mainAint argc- char Farg'DEB _ idCt idI int sigI i2AargcYY1B _ idYA idCtBatoiAarg'D*EBI sigYatoiAarg'D,EBI 8illA id- sigBI c else _ rint2Ah]s: ]s id signalanh- arg'D:E- arg'D:EBI return $*I c return :I c

+ara probarlo, he programado un peque2o shell script que capturar& las se2ales S"G;UP, S"G"N), S"GdU"), S"G!P%, S"GGLGL9 y S"G)%L91
J</bin/sh echo h5a turando signals...h tra hecho S"G;UP recibidah * tra hecho S"G"N) recibida h , tra hecho S"GdU") recibida h 1 tra hecho S"G!P% recibida h 0 tra hecho S"GGLGL9 recibida h *4 tra hecho S"G)%L9 recibida h *+ while true do : done

Simplemente saca un mensa"e por pantalla cuando reciba la se2al en concreto y permanece en un bucle infinito sin hacer nada. >amos a enviarle unas cuantas se2ales desde nuestro programa anterior1

Programacin de Sistemas

40

txi txi D*E txi

i@neon:NO gcc 8iller.c $o 8iller i@neon:NO./tra .sh s *+/1. i@neon:NO 5a turando signals...

txi i@neon:NO./8iller ./8iller: ./8iller id signal txi i@neon:NO./8iller *+/1. 0 txi i@neon:NO S"G!P% recibida txi i@neon:NO./8iller *+/1. *+ txi i@neon:NO S"G)%L9 recibida txi i@neon:NO ./8iller *+/1. 3 txi i@neon:NO gre tra .sh D*ET Milled ./tra .sh

+rimeramente llamamos al shell script 9t ra . sh : para que se e"ecute en segundo plano *mediante 9s:,. 3ntes de pasar a segundo plano, se nos informa que el proceso tiene el +($ A<WCT. 3l e"ecutar el programa 98iller: vemos que recibe dos par&metros1 el +($ y el n0mero de se2al. +robamos a mandar unas cuantas se2ales que tiene capturadas y se comporta como es esperado, mostrando la se2al recibida por pantalla. Cuando le enviamos la se2al D *S"GM"LL, incapturable,, el proceso de 9tra .sh: muere.

!igura *.+.4

Procesos recibiendo sefales- rutinas de ca tura ? roceso de sefales.

En la figura anterior observamos el comportamiento de diferentes procesos en funcin de las se2ales que reciben y sus rutinas de tratamiento de se2ales1 el primero de ellos no est& preparado para capturar la se2al que le llega, por lo que terminar& su e"ecucin al no saber cmo tratar la se2al. El segundo tiene una rutina asociada que captura se2ales, tra er, por lo que es capa' de capturar la se2al S"GCUSL* y gestionarla adecuadamente. El tercer proceso tambi%n

Programacin de Sistemas

43

dispone de la rutina capturadora de se2ales y de la funcin asociada, pero le llega una se2al incapturable, S "GCM "LL , por lo que no es capa' tampoco de gestionarla y termina su e"ecucin. Existe una manera de utili'ar 8 i l l Ade B forma masiva1 si en lugar de un +($ le pasamos como par&metro 9 id: un cero, matar& a todos los procesos que est%n en el mismo grupo de procesos que el actual. Si por el contrario pasamos 9JA: en el par&metro 9 id:, intentar& matar a todos los procesos menos al proceso 9initX y a s4 mismo. +or supuesto, esto deber4a usarse en casos muy se2alados, nunca me"or dicho /1J,. !na utili'acin bastante potente de las se2ales es el uso de S"GGLGL9 para crear tempori'adores en nuestros programas. Con la funcin alarmAB lo que conseguimos es que nuestro proceso se env4e a s4 mismo una se2al S"GGLGL9 en el n0mero de segundos que especifiquemos. El prototipo de alarmAB es el siguiente1
unsigned int alarmAunsigned int secondsBI

En su 0nico par&metro indicamos el n0mero de segundos que queremos esperar desde la llamada a alarmAB para recibir la se2al S"GGLGL9.

!igura *.+.+

La llamada a la 2uncin alarmAB generar6 una sefal S"GCGLGL9 hacia el mismo roceso Sue la in'oca.

El valor devuelto es el n0mero de segundos que quedaban en la anterior alarma antes de fi"ar esta nueva alarma. Esto es importante1 slo disponemos de un tempori'ador para usar con alarmAB, por lo que si llamamos seguidamente otra ve' a alarmAB, la alarma inicial ser& sobrescrita por la nueva. >eamos un e"emplo de su utili'acin1
Jinclude Zsignal.hK Jinclude Zunistd.hK 'oid tra erAintBI

int mainAint argc- char Farg'DEB _ int iI signalA*4- tra erBI

rint2Ah"denti2icati'o de roceso: ]danh- get idAB BI

Programacin de Sistemas

+:

alarmA+BI auseABI alarmA1BI auseABI 2orAIIB _ alarmA*BI auseABI c return :I c 'oid tra erAint sigB _ signalAsig- tra erBI rint2AhL"""""""""NG<anhBI c

Este programa es bastante similar al que hemos dise2ado antes para capturar se2ales, slo que ahora en lugar de capturarlas todas, capturar& 0nicamente la A@, S "GGLGL9 . Cuando reciba una se2al S "GGLGL9 , sacar& 9L"""""""""NG: por pantalla. El cuerpo del programa indica que se fi"ar& una alarma de < segundos y luego se esperar& hasta recibir una se2al, luego la alarma se fi"ar& a los C segundos y se volver& a esperar, y finalmente se entrar& en un bucle en el que se fi"e una alarma de A segundo todo el rato. El resultado es que se mostrar& un mensa"e 9L"""""""""NG: a los < segundos, luego a los C segundos y despu%s cada segundo1
txi i@neon:NO gcc alarma.c $o alarma txi i@neon:NO ./alarma "denti2icati'o de roceso: *+0:* L"""""""""NG< L"""""""""NG< L"""""""""NG< L"""""""""NG< L"""""""""NG< L"""""""""NG< L"""""""""NG<

+ara terminar esta seccin, veremos cmo es relativamente sencillo que procesos creados mediante 2or8AB sean capaces de comunicarse utili'ando se2ales. En este e"emplo, el hi"o env4a varias se2ales S"GUSL* a su padre y al final termina por matarlo, envi&ndole la se2al S"GM"LL *hay muchos hi"os que son as4 de agradecidos,1
Jinclude Jinclude Jinclude Jinclude Zs?s/t? es.hK Zstdio.hK Zunistd.hK Zsignal.hK

'oid tra erAint sigB _ signalAsig- tra erBI rint2AhS"GUSL*anhBI c

Programacin de Sistemas

+*

int mainAint argc- char Farg'DEB _ idCt adre- hi=oI adre Y get idABI signalA S"GUSL*- tra er BI

i2 A Ahi=oY2or8ABB YY : B _ /F hi=o F/ slee A*BI 8illA adre- S"GUSL*BI slee A*BI 8illA adre- S"GUSL*BI slee A*BI 8illA adre- S"GUSL*BI slee A*BI 8illA adre- S"GM"LLBI exitA:BI c else _ /F adre F/ 2or AIIBI c return :I c

Con la funcin s lee A,B el hi"o espera un determinado n0mero de segundos antes de continuar. El uso de esta funcin puede intervenir con el uso de a la rmA , B as4 que habr& que utili'arlas con cuidado. La salida de este parricidio es la siguiente1
txi i@neon:NO gcc signal2or8.c $o signal2or8 txi i@neon:NO./signal2or8 S"GUSL* S"GUSL* S"GUSL* Milled

*.+..., )uber#as Las tuber4as o 9pipes: simplemente conectan la salida est&ndar de un proceso con la entrada est&ndar de otro. ormalmente las tuber4as son de un solo sentido, imaginemos lo desagradable que ser4a que estuvi%ramos utili'ando un lavabo y que las tuber4as fueran bidireccionales, lo que emanaran los desagKes ser4a bastante repugnante. +or esta ra'n, las tuber4as suelen ser 9hal#-duple$:, es decir, de un 0nico sentido, y se requieren dos tuber4as 9hal#-duple$: para hacer una comunicacin en los dos sentidos, es decir 9#ull-duple$:. Las tuber4as son, por tanto, flu"os unidireccionales de bytes que conectan la salida est&ndar de un proceso con la entrada est&ndar de otro proceso.

Programacin de Sistemas

+,

Cuando dos procesos est&n enla'ados mediante una tuber4a, ninguno de ellos es consciente de esta redireccin, y act0a como lo har4a normalmente. 3s4 pues, cuando el proceso escritor desea escribir en la tuber4a, utili'a las funciones normales para escribir en la salida est&ndar. Lo 0nico especial que sucede es que el descriptor de fichero que est& utili'ando ya no corresponde al terminal *ya no se escriben cosas por la pantalla,, sino que se trata de un fichero especial que ha creado el n0cleo. El proceso lector se comporta de forma muy similar1 utili'a las llamadas normales para recoger valores de la entrada est&ndar, solo que %sta ya no se corresponde con el teclado, sino que ser& el extremo de la tuber4a. Los procesos est&n autori'ados a reali'ar lecturas no bloqueantes de la tuber4a, es decir, si no hay datos para ser le4dos o si la tuber4a est& bloqueada, se devolver& un error. Cuando ambos procesos han terminado con la tuber4a, el inodo de la tuber4a es desechado "unto con la p&gina de datos compartidos. El uso de un pipe a m4 me recuerda a los antiguos 9tel%fonos: que hac4amos mi hermana y yo con dos envases de yogur y una cuerda muy fina. !n4amos los fondos de los yogures con la cuerda, y cuando %sta estaba muy tensa, al hablar por un yogur, se escuchaba en la otra parte. Era un m%todo divertido de contar secretos, pero ten4a el mismo inconveniente que los pipes1 si uno de los dos estaba hablando, el otro no pod4a hacerlo al mismo tiempo o no val4a para nada. Era una comunicacin unidireccional, al contrario de lo que pasa con los tel%fonos modernos1

!igura *.+..

Una tuber#a es unidireccional- como los telk2onos de ?ogur.

La utili'acin de tuber4as mediante el uso de la shell es 9el pan nuestro de cada d%a:, cualquier administrador de sistemas medianamente preparado encadena comandos y comandos mediante tuber4as de forma natural1
txi i@neon:NO cat /etc/ asswd U gre bash U wc Wlines **

Los comandos 9cat:, 9gre : y 9wc: se lan'an en paralelo y el primero va 9alimentando: al segundo, que posteriormente 9alimenta: al tercero. 3l final tenemos una salida filtrada por esas dos tuber4as. Las tuber4as empleadas son destruidas al terminar los procesos que las estaban utili'ando.

Programacin de Sistemas

+1

!tili'ar tuber4as en C es tambi%n bastante sencillo, si bien a veces es necesario emplear l&pi' y papel para no liarse con tanto descriptor de fichero. Ra vimos anteriormente que para abrir un fichero, leer y escribir en %l y cerrarlo, se empleaba su descriptor de fichero. !na tuber4a tiene en realidad dos descriptores de fichero1 uno para el extremo de escritura y otro para el extremo de lectura. Como los descriptores de fichero de ! () son simplemente enteros, un pipe o tuber4a no es m&s que un array de dos enteros1
int tuberiaD,EI

+ara crear la tuber4a se emplea la funcin i e A , B que abre dos descriptores de fichero y almacena su valor en los dos enteros que contiene el array de descriptores de fichero. El primer descriptor de fichero es abierto como 7CL&7NLP , es decir, slo puede ser empleado para lecturas. El segundo se abre como 7CRL7NLP, limitando su uso a la escritura. $e esta manera se asegura que el pipe sea de un solo sentido1 por un extremo se escribe y por el otro se lee, pero nunca al rev%s. Ra hemos dicho que si se precisa una comunicacin 9#ull-duple$:, ser& necesario crear dos tuber4as para ello.
int tuberiaD,EI i eAtuberiaBI

!na ve' creado un pipe, se podr&n hacer lecturas y escrituras de manera normal, como si se tratase de cualquier fichero. Sin embargo, no tiene demasiado sentido usar un pipe para uso propio, sino que se suelen utili'ar para intercambiar datos con otros procesos. Como ya sabemos, un proceso hi"o hereda todos los descriptores de ficheros abiertos de su padre, por lo que la comunicacin entre el proceso padre y el proceso hi"o es bastante cmoda mediante una tuber4a. +ara asegurar la unidireccionalidad de la tuber4a, es necesario que tanto padre como hi"o cierren los respectivos descriptores de ficheros. En la siguiente figura vemos cmo un proceso padre puede enviarle datos a su hi"o a trav%s de una tuber4a. +ara ello el proceso padre cierra el extremo de lectura de la tuber4a, mientras que el proceso hi"o cierra el extremo de escritura de la misma1

!igura *.+./

%l roceso adre ? su hi=o com arten datos mediante una tuber#a.

Programacin de Sistemas

+4

La tuber4a 9p: se hereda al hacer el 2 o r8 A B que da lugar al proceso hi"o, pero es necesario que el padre haga un c lose A B de D:E *el lado de lectura de la tuber4a,, y el hi"o haga un closeAB de D*E *el lado de escritura de la tuber4a,. !na ve' hecho esto, los dos procesos pueden emplear la tuber4a para comunicarse *siempre unidireccionalmente,, haciendo writeAB en D*E y readAB en D:E, respectivamente. >eamos un e"emplo de este tipo de situacin1
Jinclude Jinclude Jinclude Jinclude Zs?s/t? es.hK Z2cntl.hK Zunistd.hK Zstdio.hK

Jde2ine S"H% +*, int mainA int argc- char FFarg' B _ idCt idI int D,E- readb?tesI char bu22erDS"H%EI i eA BI

i2 A A idY2or8ABB YY : B _ // hi=o closeA D*E BI /F cerramos el lado de escritura del i e F/ whileA Areadb?tesYreadA D:E- bu22er- S"H% BB K :B writeA *- bu22er- readb?tes BI closeA D:E BI c else _ // adre closeA D:E BI /F cerramos el lado de lectura del i e F/ strc ?A bu22er- h%sto llega a tra'es de la tuberiaanh BI writeA D*E- bu22er- strlenA bu22er B BI closeA D*E BI c wait idA id- NULL- : BI exitA : BI c

La salida de este programa no es muy espectacular, pero muestra el funcionamiento del mismo1 se crean dos procesos y uno *el padre, le comunica un mensa"e al otro *el hi"o, a trav%s de una tuber4a. El hi"o al recibir el mensa"e, lo escribe por la salida est&ndar *hace un writeAB en el descriptor de fichero A,. +or 0ltimo cierran los descriptores de ficheros utili'ados, y el padre espera al hi"o a que finalice1
txi i@neon:NO gcc i e2or8.c Wo i e2or8 txi i@neon:NO ./ i e2or8 %sto llega a tra'es de la tuberia

Programacin de Sistemas

++

>eamos ahora cmo implementar una comunicacin bidireccional entre dos procesos mediante tuber4as. Como ya hemos venido diciendo, ser& preciso crear dos tuber4as diferentes *aD, E y bD, E,, una para cada sentido de la comunicacin. En cada proceso habr& que cerrar descriptores de ficheros diferentes. >amos a emplear el pipe aD,E para la comunicacin desde el padre al hi"o, y el pipe bD,E para comunicarnos desde el hi"o al padre. +or lo tanto, deberemos cerrar1 En el padre1 o el lado de lectura de aD,E. o el lado de escritura de bD,E. En el hi"o1 o el lado de escritura de aD,E. o el lado de lectura de bD,E. =al y como se puede ver en la siguiente figura1

!igura *.+.0

&os rocesos se comunican bidireccionalmente con dos tuber#as.

El cdigo anterior se puede modificar para que la comunicacin sea bidireccional1


Jinclude Jinclude Jinclude Jinclude Zs?s/t? es.hK Z2cntl.hK Zunistd.hK Zstdio.hK

Jde2ine S"H% +*, int mainA int argc- char FFarg' B _ idCt idI

Programacin de Sistemas

+.

int aD,E- bD,E- readb?tesI char bu22erDS"H%EI i eA a BI i eA b BI i2 A A idY2or8ABB YY : B _ // hi=o closeA aD*E BI /F cerramos el lado de escritura del i e F/ closeA bD:E BI /F cerramos el lado de lectura del i e F/ whileA Areadb?tesYreadA aD:E- bu22er- S"H% B B K :B writeA *- bu22er- readb?tes BI closeA aD:E BI strc ?A bu22er- hSo? tu hi=o hablandote or la otra tuberia.anh BI writeA bD*E- bu22er- strlenA bu22er B BI closeA bD*E BI c else _ // adre closeA aD:E BI /F cerramos el lado de lectura del i e F/ closeA bD*E BI /F cerramos el lado de escritura del i e F/ strc ?A bu22er- hSo? tu adre hablandote or una tuberia.anh BI writeA aD*E- bu22er- strlenA bu22er B BI closeA aD*EBI whileA Areadb?tesYreadA bD:E- bu22er- S"H% BB K :B writeA *- bu22er- readb?tes BI closeA bD:EBI c wait idA id- NULL- : BI exitA : BI c

La salida de este e"emplo es tambi%n bastante simple1


txi i@neon:NO dos i es2or8.c $o dos i es2or8 txi i@neon:NO ./dos i es2or8 So? tu adre hablandote or una tuberia. So? tu hi=o hablandote or la otra tuberia.

3vancemos en cuanto a conceptos tericos. La funcin du A B duplica un descriptor de fichero. 3 simple vista podr4a parecer trivial, pero es muy 0til a la hora de utili'ar tuber4as. Ra sabemos que inicialmente, el descriptor de fichero F corresponde a la entrada est&ndar, el descriptor A a la salida est&ndar y el descriptor ? a la salida de error est&ndar. Si empleamos du A B para duplicar alguno de estos descriptores en uno de los extremos de una tuber4a, podremos reali'ar lo mismo que hace la shell cuando enla'a dos comandos mediante una tuber4a1 reconducir la salida est&ndar de un proceso a la entrada est&ndar del siguiente. El prototipo de la funcin du AB es el siguiente1

Programacin de Sistemas

+/

int du Aint old2dBI int du ,Aint old2d- int new2dBI

La funcin du A B asigna el descriptor m&s ba"o de los disponibles al descriptor antiguo, por lo tanto, para asignar la entrada est&ndar a uno de los lados de un pipe es necesario cerrar el descriptor de fichero F "usto antes de llamar a du A B1
closeA : BI du A D*E BI

Como du A B duplica siempre el descriptor m&s ba"o disponible, si cerramos el descriptor F "usto antes de llamarla, ese ser& el descriptor que se duplique. +ara evitar l4os de cerrar descriptores con vistas a ser duplicados y dem&s, se creo du ,A B, que simplemente recibe los dos descriptores de fichero y los duplica1
du ,A D*E- : BI

El siguiente e"emplo emplea estas llamadas para concatenar la e"ecucin de dos comandos, 9ca t: y 9wc :. El proceso hi"o reali'a un 9cat: de un fichero, y lo encamina a trav%s de la tuber4a. El proceso padre recibe ese fichero por la tuber4a y se lo pasa al comando 9wc: para contar sus l4neas1
Jinclude Jinclude Jinclude Jinclude Zs?s/t? es.hK Z2cntl.hK Zunistd.hK Zstdio.hK

Jde2ine 5799GN&* h/bin/cath Jde2ine 5799GN&, h/usr/bin/wch int mainA int argc- char FFarg' B _ idCt idI int D,EI i eA BI i2 A A idY2or8ABB YY : B _ // hi=o closeA D:EBI /F cerramos el lado de lectura del i e F/ du ,A D*E- *BI /F S)&7U) Y extremo de salida del i e F/ closeA D*EBI /F cerramos el descri tor de 2ichero Sue sobra tras el du , F/ execl A5799GN&*- 5799GN&*- arg'D*E- NULLBI errorAherrorhBI /F si estamos aSu#- algo ha 2allado F/ CexitA*BI /F salir sin 2lush F/

c else _ // adre closeA D*EBI /F cerramos el lado de escritura del i e F/

Programacin de Sistemas

+0

du ,A D:E- :BI /F S)&"N Y extremo de entrada del i e F/ closeA D:EBI /F cerramos el descri tor de 2ichero Sue sobra tras el du , F/ execl A5799GN&,- 5799GN&,- NULLBI errorAherrorhBI /F si estamos aSui- algo ha 2allado F/ exitA*BI /F salir con 2lush F/ c return :I c

La salida de este programa es bastante predecible, el resultado es el mismo que encadenar el comando 9cat: del fichero pasado por par&metro, con el comando 9wc :1
txi i@neon:NO gcc i ecommand.c $o i ecommand txi i@neon:NO ./ i ecommand i ecommand.c +: *+, 30: txi i@neon:NO cat i ecommand.c U wc +: *+, 30:

Como vemos, las llamadas a comandos y su intercomunicacin mediante tuber4as puede ser un proceso bastante lioso, aunque se utili'a en multitud de ocasiones. Es por esto que se crearon las llamadas popen*, y pclose*,. Bediante popen*, tenemos todo el traba"o sucio reducido a una sola llamada que crea un proceso hi"o, lan'a un comando, crea un pipe y nos devuelve un puntero a fichero para poder utili'ar la salida de ese comando como nosotros queramos. La definicin de estas funciones es la siguiente1
!"L% F o enAconst char Fcommand- const char Ft? eBI int closeA!"L% FstreamBI

El siguiente cdigo es una muestra clara de cmo se puede hacer una llamada utili'ando tuber4as y procesos hi"o, de forma sencill4sima1
Jinclude Jinclude Jinclude Jinclude Jinclude Zunistd.hK Zstdio.hK Zstdlib.hK Z2cntl.hK Zlimits.hK

Jde2ine S"H% P"P%C(U! int mainAint argc- char Farg'DEB _ !"L% F2ileI char FcommandY hls .hI char bu22erDS"H%EI 2ileY o enA command- hrh BI whileA <2eo2A 2ile B B _ 2scan2A 2ile- h]sh- sbu22er BI

Programacin de Sistemas

+3

rint2A h]sanh- bu22er BI

closeA 2ile BI return :I c

uestro programa simplemente crea un proceso hi"o que ser& reempla'ado por una llamada al comando 9l s :, . y se nos devolver& un puntero a un fichero que ser& el resultado de ese comando. Leemos ese fichero y lo escribimos por pantalla. 3l finali'ar, cerramos la tuber4a con c lose A.B La salida de este programa, es esta1
txi i@neon:NO gcc o en.c $o o en txi i@neon:NO ./ o en dos i es2or8 dos i es2or8.c i ecommand i ecommand.c i e2or8 i e2or8.c o en o en.c

Linux tambi%n soporta tuber4as con nombre, denominadas habitualmente &'&(s, *&irst in &irst out, debido a que las tuber4as funcionan como una cola1 el primero en entrar es el primero en salir. 3 diferencia de las tuber4as sin nombre, los -(-/s no tiene car&cter temporal sino que perduran aunque dos procesos hayan de"ado de usarlos. +ara crear un -(-/ se puede utili'ar el comando 9m82 i 2o 9 o bien llamar a la funcin de C m82 i 2o A1B
int m82i2oAconst char F athname- modeCt modeBI

Esta funcin recibe dos par&metros1 9 athname : indica la ruta en la que queremos crear el -(-/, y 9mode : indica el modo de acceso a dicho -(-/. Cualquier proceso es capa' de utili'ar un -(-/ siempre y cuando tengan los privilegios necesarios para ello. o nos extenderemos m&s en la creacin de tuber4as con nombre ya que su mane"o es bastante similar a lo visto hasta ahora. *.+...1 "P5 S?stem X 5olas de mensa=es Bediante las colas de mensa"es un proceso puede escribir mensa"es que podr&n ser le4dos por uno o m&s procesos diferentes. En G !#Linux este mecanismo est& implementado mediante un array de colas de mensa"es, msgSue . Cada posicin de este array es una estructura de tipo msgidCds que gestionar& la cola mediante punteros a los mensa"es introducidos en ella. Estas colas, adem&s, controlan cu&ndo fue la 0ltima ve' que se escribi en ellas, y proporcionan dos colas de espera1 una para escritores de la cola y otra para lectores de la cola.

Programacin de Sistemas

.:

Cuando un proceso escribe un mensa"e en la cola de escritura, %ste se posiciona al final de la misma *tiene una gestin -(-/, si es que existe espacio suficiente para ser albergado *Linux limita el n0mero y tama2o de los mensa"es para evitar ataques de $enegacin de Servicio,. +revio a cualquier escritura, el sistema comprueba si realmente el proceso est& autori'ado para escribir en la cola en cuestin, comparando las credenciales del proceso con los permisos de la cola. 3simismo, cuando un proceso quiere leer de esa cola, se reali'a una comprobacin similar, para evitar que procesos no autori'ados lean mensa"es importantes. Si un proceso desea leer un mensa"e de la cola y no existe ning0n mensa"e del tipo deseado, el proceso se a2adir& a la cola de espera de lectura y se cambiar& de contexto para que de"e de estar activo. La implementacin pr&ctica en C de colas de mensa"es queda fuera del alcance de este texto. Sem62oros !n sem&foro es un mecanismo del sistema para evitar la colisin cuando dos o m&s procesos necesitan un recurso. Los sem&foros (+C refle"an bastante fielmente la definicin cl&sica de $i".stra, realmente son variables enteras con operaciones atmicas de iniciali'acin, incremento y decremento con bloqueo. Cada sem&foro es un contador que se iniciali'a a un determinado valor. Cuando un proceso hace uso del recurso asignado a ese sem&foro, el contador se decrementa una unidad. Cuando ese proceso libera el recurso, el contador del sem&foro se incrementa. 3s4 pues, el contador de un sem&foro siempre registra el n0mero de procesos que pueden utili'ar el recurso actualmente. $icho contador puede tener valores negativos, si el n0mero de procesos que precisan el recurso es mayor al n0mero de procesos que pueden ser atendidos simult&neamente por el recurso. +or recurso entendemos cualquier cosa que pueda ser susceptible de ser usada por un proceso y pueda causar un interbloqueo1 una regin de memoria, un fichero, un dispositivo f4sico, etc. (maginemos que creamos un sem&foro para regular el uso de una impresora que tiene capacidad para imprimir tres traba"os de impresin simult&neamente. El valor del contador del sem&foro se iniciali'ar4a a tres. Cuando llega el primer proceso que desea imprimir un traba"o, el contador del sem&foro se decrementa. El siguiente proceso que quiera imprimir todav4a puede hacerlo, ya que el contador a0n tiene un valor mayor que cero. Conforme vayan llegan procesos con traba"os de impresin, el contador ir& disminuyendo, y cuando llegue a un valor inferior a uno, los procesos que soliciten el recurso tendr&n que esperar. !n proceso a la espera de un recurso controlado por un sem&foro siempre es privado del procesador, el planificador detecta esta situacin y cambia el proceso en e"ecucin para aumentar el rendimiento. Conforme los traba"os de impresin vayan acabando, el contador del sem&foro ir& increment&ndose y los procesos a la espera ir&n siendo atendidos.

Programacin de Sistemas

.*

Es muy importante la caracter4stica de atomicidad de las operaciones sobre un sem&foro. +ara evitar errores provocados por condiciones de carrera *9race conditions:,, los sem&foros protegen su contador, asegurando que todas las operaciones sobre esa variable entera *lectura, incremento, decremento, son atmicas, es decir, no ser&n interrumpidas a la mitad de su e"ecucin. ;ecordamos que estamos en un entorno multiprogramado en el que ning0n proceso se asegura que vaya a ser e"ecutado de principio a fin sin interrupcin. Las actuali'aciones y consultas de la variable contador de un sem&foro (+C son la excepcin a este hecho1 una ve' iniciadas, no son interrumpidas. Con esto se consigue evitar fallos a la hora de usar un recurso protegido por un sem&foro1 imaginemos que en un entorno en el que hay cuatro procesadores traba"ando concurrentemente, cuatro procesos leen a la ve' el valor del contador del sem&foro anterior *impresora,. Supongamos que tiene un valor inicial de tres. Los cuatro procesos leen un valor positivo y deciden usar el recurso. $ecrementan el valor del contador, y cuando se disponen a usar el recurso, resulta que hay cuatro procesos intentando acceder a un recurso que slo tiene capacidad para tres. La proteccin de la variable contador evita este hecho, por eso es tan importante. La implementacin pr&ctica en C de sem&foros (+C queda fuera del alcance de este texto. 9emoria com artida La memoria compartida es un mecanismo para compartir datos entre dos o m&s procesos. $ichos procesos comparten una parte de su espacio de direccionamiento en memoria, que puede coincidir en cuanto a direccin virtual o no. Es decir, imaginemos que tenemos dos libros compartiendo una p&gina. El primer libro es 9El Qui"ote de la Bancha:, y el segundo es un libro de texto de TY de primaria. La p&gina <F del primer libro es compartida por el segundo, pero puede que no corresponda con el n0mero de p&gina <F, sino que est% en la p&gina A?@. Sin embargo la p&gina es la misma, a pesar de que no est% en el mismo sitio dentro del direccionamiento de cada proceso. Los accesos a segmentos de memoria compartida son controlados, como ocurre con todos los ob"etos (+C System >, y se hace un chequeo de los permisos y credenciales para poder usar dicho segmento. Sin embargo, una ve' que el segmento de memoria est& siendo compartido, su acceso y uso debe ser regulado por los propios procesos que la comparten, utili'ando sem&foros u otros mecanismos de sincroni'acin. La primera ve' que un proceso accede a una de las p&ginas de memoria virtual compartida, tiene lugar un fallo de p&gina. El Sistema /perativo trata de solventar este fallo de p&gina y se da cuenta de que se trata de una p&gina correspondiente a un segmento de memoria compartida. Entonces, se busca la p&gina correspondiente a esa p&gina de memoria virtual compartida, y si no existe, se asigna una nueva p&gina f4sica. La manera mediante la que un proceso de"a de compartir una regin o segmento de memoria compartida es bastante similar a lo que sucede con los enlaces entre ficheros1 al borrarse un enlace no se procede al borrado del

Programacin de Sistemas

.,

fichero enla'ado a no ser que ya no existan m&s enlaces a dicho fichero. Cuando un proceso se desenla'a o desencadena de un segmento de memoria, se comprueba si hay m&s procesos utili'&ndolo. Si es as4, el segmento de memoria contin0a como hasta entonces, pero de lo contrario, se libera dicha memoria. Es bastante recomendable bloquear en memoria f4sica la memoria virtual compartida para que no sea reempla'ada *9swapping:, por otras p&ginas y se almacene en disco. Si bien un proceso puede que no use esa p&gina en mucho tiempo, su car&cter compartido la hace susceptible de ser m&s usada y el reempla'o provocar4a una ca4da del rendimiento. La implementacin pr&ctica en C de la comunicacin mediante memoria compartida queda fuera del alcance de este texto.

*.+./

5omunicacin or red

Buchas de las utilidades que usamos todos los d4as, como el correo electrnico o los navegadores 5eb, utili'an protocolos de red para comunicarse. En este apartado veremos cmo utili'ar esos protocolos, comprenderemos todo lo que rodea a una comunicacin a trav%s de los interfaces de red y aprenderemos a programar clientes y servidores sencillos. *.+./.* (re'e re aso a las redes )5P/"P 3ntes de afrontar la configuracin de red de nuestros equipos, vamos a desempolvar nuestras nociones sobre redes =C+#(+. Siempre que se habla de redes de ordenadores, se habla de protocolos. !n protocolo no es m&s que un acuerdo entre dos entidades para entenderse. Es decir, si yo le digo a un amigo que le de"o una llamada perdida cuando llegue a su portal, para que ba"e a abrirme, habremos establecido un protocolo de comunicacin, por e"emplo. Los ordenadores 2uncionan de una 2orma m6s o menos arecida. 5uando Sueremos establecer una conexin entre ordenadores mediante una red- ha? muchos 2actores en =uego: rimero est6 el medio 2#sico en el Sue se roducir6 la conexin Acable- ondas electromagnkticas- etc.B- or otro lado est6 la tecnolog#a Sue se utilizar6 Atar=etas de red %thernet- modems- etc.B- or otro los aSuetes de datos Sue en'iaremos- las direcciones o destinos dentro de una red... &ado Sue ha? tantos elementos Sue inter'ienen en una comunicacin- se establecen rotocolos o normas ara entidades del mismo ni'el- es decir- se di'ide todo lo Sue inter'iene en la comunicacin en di2erentes ca as. %n el ni'el m6s ba=o de todas las ca as estar#a el ni'el 2#sico: el medio Sue se 'a a utilizarlos 'olta=es utilizados- etc. Sobre esa ca a se constru?e la siguienteen donde trans2ormamos las sefales elkctricas en datos ro iamente dichos. %sta ser#a la ca a de enlace de datos. Posteriormente- se 'an estableciendo una serie de ca as intermedias- cada una con ma?or re2inamiento de la in2ormacin

Programacin de Sistemas

.1

Sue mane=a- hasta llegar a la ca a de a licacin- Sue es con la Sue interact^an la ma?or#a de rogramas Sue utilizamos ara conectarnos a redes: na'egadores- clientes de correo- etc. 3s4, por e"emplo, si queremos mandar un correo electrnico a un amigo, utili'aremos la aplicacin indicada para mandar un mensa"e, en este caso nuestro cliente de correo preferido *mutt, Pmail, mo'illa...,. El cliente de correo enviar& el mensa"e al servidor, para que %ste lo encamine hasta el bu'n de nuestro amigo, pero en ese env4o suceder&n una serie de pasos que pueden parecernos transparentes en un principio1 +rimeramente se establece una conexin con el servidor, para ello la aplicacin *el cliente de correo, enviar& a las capas m&s ba"as de protocolos de red una peticin al servidor de correo. Esa capa, aceptar& la peticin, y reali'ar& otro encargo a una capa inferior, solicitando enviar un paquete de datos por la red. La capa inferior, a su ve', pedir& a la capa f4sica enviar una serie de se2ales el%ctricas por el medio, para hacer su cometido. =al y como est& enfocada la Xpila de protocolos de redX, cada Xtraba"oX de red comple"o se divide en partes cada ve' m&s sencillas hasta llegar a la capa f4sica, que se encargar& de la transmisin el%ctrica. Es como si el "efe de una empresa de video"uegos mandara a su subdirector que hiciera un "uego de accin. El subdirector ir4a a donde sus subordinados y les pedir4a un guin, unos gr&ficos, un motor de animaciones, etc. Los encargados de los gr&ficos ir4an a donde sus subordinados y les pedir4an, la portada, los decorados, los persona"es, etc. Zstos, a su ve', se repartir4an en grupos y cada uno har4a un traba"o m&s concreto, y as4 sucesivamente. Es decir, una idea comple"a, se divide en traba"os concretos y sencillos para hacerse, estructur&ndose en capas. En el caso espec4fico que nos interesa, la pila de protocolos que utili'amos se denomina =C+#(+ , porque dos de sus protocolos principales se llaman =C+ *capa de transporte, e (+ *capa de red,.

Programacin de Sistemas

.4

!igura *.+.3

5omunicacin mediante ca as de rotocolos de red.

Cuando utili'amos estos protocolos, cada uno de los posibles destinos de una red necesita un nombre diferente o direccin (+. 3l igual que sucede con los tel%fonos, para diferenciar todos los ordenadores y dispositivos conectados a una red, se les asigna a cada uno un n0mero diferente, y basta con XmarcarX ese n0mero para acceder a %l. 3ctualmente esos n0meros van desde el F al @?D@DTW?DT, pero en lugar de utili'ar simplemente el n0mero, se emplea una notacin m&s sencilla, separando el n0mero en @ d4gitos del F al ?<<, por e"emplo1 A?L.?@@.C@.A? AD?.ATL.F.A. En un futuro no muy le"ano, las direcciones (+ cambiar&n su formato, ya que el espacio de direcciones que ofrece la versin actual de (+ *(+v@, se est& agotando, por lo que habr& que ampliar su rango *al igual que ocurre en ciudades o provincias con mucha demanda de n0meros de tel%fono, que ampl4an la longitud de sus n0meros de tel%fono en una o varias cifras,. Siempre que queramos acceder a una red =C+#(+, deberemos tener una direccin (+ que nos identifique. Est& prohibido via"ar sin matr4cula por estas carreteras. En nuestras redes privadas, nuestras intranets o peque2as L3 s, la manera de establecer esas direcciones (+ la marcamos nosotros mismos *o el administrador de red, en su caso,. Es decir, dentro de nuestras organi'aciones, somos nosotros los que ponemos los nombres. Esto es lo mismo que lo que sucede en una organi'acin grande, con muchos tel%fonos internos y una centralita. El n0mero de extensin de cada tel%fono, lo inventamos nosotros mismos, no la compa24a telefnica. Cuando queremos salir a una red p0blica como pueda ser (nternet, no podemos inventarnos nuestra direccin (+ , deberemos seguir unas normas externas para poder circular por all4. Siguiendo el s4mil telefnico, si queremos un tel%fono

Programacin de Sistemas

.+

accesible por todo el mundo, deberemos solicitar un n0mero v&lido a la empresa telefnica. Easta aqu4 todo claro1 los ordenadores tienen unos n0meros similares a los n0meros de tel%fono para identificarse, y cuando queremos comunicarnos con un destino en concreto, slo tenemos que XmarcarX su n0mero, pero... 6cu&ndo pedimos una p&gina 5eb a 555.linux.org cmo sabe nuestra m&quina qu% n0mero XmarcarX7 8uena pregunta, tiene que haber un Xlist4n telefnicoX (+ , que nos diga que (+ corresponde con una direccin espec4fica. Estas Xp&ginas amarillasX de las redes (+ se denominan $ S *$omain ame System,. Husto antes de hacer la conexin a 555.linux.org, nuestro navegador le pregunta la direccin (+ al $ S, y luego conecta v4a direccin (+ con el servidor 5eb 555.linux.org. !na conexin de un ordenador a otro precisa, adem&s de una direccin (+ de destino, un n0mero de puerto. Si llamamos por tel%fono a un n0mero normalmente preguntamos por alguien en concreto. Llamas a casa de tus padres y preguntas por tu hermano peque2o. Con las comunicaciones telem&ticas sucede algo parecido1 llamas a una determinada (+ y preguntas por un servicio en concreto, por e"emplo el Servicio Ueb, que tiene reservado el puerto LF. Los servicios reservan puertos y se quedan a la escucha de peticiones para esos puertos. Existen un montn de puertos que t4picamente se usan para servicios habituales1 el LF para 5eb, el ?F y ?A para -=+, el ?C para telnet, etc. Son los puertos 9bien conocidos: *9well known ports:, y suelen ser puertos reservados, por deba"o de AF?@. +ara aplicaciones extra2as o de &mbito personal se suelen utili'ar puertos 9altos:, por encima de AF?@. El n0mero de puerto lo define un entero de AT bits, es decir, hay T<<C< puertos disponibles. !n servidor o servicio no es m&s que un programa a la escucha de peticiones en un puerto. 3s4 pues, cuando queramos conectarnos a un ordenador, tendremos que especificar el par 9&i recc in "P :Puer :. to Con esta breve introduccin hemos repasado nociones b&sicas de lo que son protocolos de comunicaciones, la pila de protocolos =C+#(+ , el direccionamiento (+ , y la resolucin de nombres o $ S. *.+./., Soc8ets !n soc.et es, como su propio nombre indica, un conector o enchufe. Con %l podremos conectarnos a ordenadores remotos o permitir que %stos se conecten al nuestro a trav%s de la red. En realidad un soc.et no es m&s que un descriptor de fichero un tanto especial. ;ecordemos que en ! () todo es un fichero, as4 que para enviar y recibir datos por la red, slo tendremos que escribir y leer en un fichero un poco especial. Ra hemos visto que para crear un nuevo fichero se usan las llamadas o enA B o c rea t A,B sin embargo, este nuevo tipo de ficheros se crea de una forma un poco distinta, con la funcin soc8etAB1
int soc8etAint domain- int t? e- int rotocolBI

Programacin de Sistemas

..

!na ve' creado un soc.et, se nos devuelve un descriptor de fichero, al igual que ocurr4a con o enA B o c reat A,B y a partir de ah4 ya podr4amos tratarlo, si quisi%ramos, como un fichero normal. Se pueden hacer readAB y writeAB sobre un soc.et, ya que es un fichero, pero no es lo habitual. Existen funciones espec4ficamente dise2adas para el mane"o de soc.ets, como sendAB o rec'AB, que ya iremos viendo m&s adelante. 3s4 pues, un soc.et es un fichero un poco especial, que nos va a servir para reali'ar una comunicacin entre dos procesos. Los soc.ets que trataremos nosotros son los de la 3+( *(nterfa' de +rogramacin de 3plicaciones, de soc.ets 8er.eley, dise2ados en la universidad del mismo nombre, y nos centraremos exclusivamente en la programacin de clientes y servidores =C+#(+. $entro de este tipo de soc.ets, veremos dos tipos1 Soc.ets de flu"o *=C+,. Soc.ets de datagramas *!$+,. Los primeros utili'an el protocolo de transporte =C+, definiendo una comunicacin bidireccional, confiable y orientada a la conexin1 todo lo que se env4e por un extremo de la comunicacin, llegar& al otro extremo en el mismo orden y sin errores *existe correccin de errores y retransmisin,. Los soc.ets de datagramas, en cambio, utili'an el protocolo !$+ que no est& orientado a la conexin, y es no confiable1 si env4as un datagrama, puede que llegue en orden o puede que llegue fuera de secuencia. o precisan mantener una conexin abierta, si el destino no recibe el paquete, no ocurre nada especial. 3lguien podr4a pensar que este tipo de soc.ets no tiene ninguna utilidad, ya que nadie nos asegura que nuestro tr&fico llegue a buen puerto, es decir, podr4a haber p%rdidas de informacin. 8ien, imaginemos el siguiente escenario1 el partido del siglo *todos los a2os hay dos o m&s, por eso es el partido del siglo,, una 0nica televisin en todo el edificio, pero una potente red interna que permite retransmitir el partido digitali'ado en cada uno de los ordenadores. Cientos de empleados poniendo cara de contables, pero siguiendo cada lance del encuentro... 6Qu% pasar4a si se usaran soc.ets de flu"o7 +ues que la calidad de la imagen ser4a perfecta, con una nitide' asombrosa, pero es posible que para mantener intacta la calidad original haya que retransmitir fotogramas semidefectuosos, reordenar los fotogramas, etc. Existen much4simas posibilidades de que no se pueda mantener una visibilidad en tiempo real con esa calidad de imagen. 6Qu% pasar4a si us&ramos soc.ets de datagramas7 +robablemente algunos fotogramas tendr4an alg0n defecto o se perder4an, pero todo fluir4a en tiempo real, a gran velocidad. +erder un fotograma no es grave *recordemos que cada segundo se suelen emitir ?@ fotogramas,, pero esperar a un fotograma incorrecto de hace dos minutos que tiene que ser retransmitido, puede ser desesperante *qui'& tu veas la secuencia del gol con m&s nitide', pero en el ordenador de enfrente hace minutos que lo han feste"ado,. +or esto mismo, es muy normal que las retransmisiones de eventos deportivos o musicales en tiempo real usen soc.ets de datagramas, donde no se asegura

Programacin de Sistemas

./

una calidad perfecta, pero la imagen llegar& sin grandes saltos y sin demoras por retransmisiones de datos imperfectos o en desorden. *.+./.1 )i os de datos Es muy importante conocer las estructuras de datos necesarias a la hora de programar aplicaciones en red. Qui'& al principio pueda parecer un tanto catica la definicin de estas estructuras, es f&cil pensar en implementaciones m&s eficientes y m&s sencillas de comprender, pero debemos darnos cuenta de que la mayor4a de estas estructuras son modificaciones de otras existentes, m&s generales y, sobre todo, que se han convertido en un est&ndar en la programacin en C para ! (). +or lo tanto, no nos queda m&s remedio que tratar con ellas. La siguiente estructura es una s t ruc tderivada del tipo soc8addr, pero espec4fica para (nternet1
struct soc8addrCin _ short int sinC2amil?I // Y G!C"N%) unsigned short int sinC ortI struct inCaddr sinCaddrI unisgned char sinCzeroD0EI c

3 simple vista parece monstruosamente fea. ecesit&bamos una estructura que almacenase una direccin (+ y un puerto, 6y alguien dise2 eso7 6En qu% estaba pensando7 o desesperemos, ya hemos dicho que esto viene de m&s atr&s. Comentemos poco a poco la estructura1 s inC 2ami l1?es un entero corto que indica la 9familia de direcciones:, en nuestro caso siempre tendr& el valor 9G!C "N%):. s inC or1t entero corto sin signo que indica el n0mero de puerto. s inCaddr 1 estructura de tipo i n Caddrque indica la direccin (+ . s inCze ro 1 array de L bytes rellenados a cero. Simplemente tiene
sentido para que el tama2o de esta estructura coincida con el de soc.addr.

La estructura i n Caddrutili'ada en s inCaddrtiene la siguiente definicin1


struct inCaddr _ unisgned long sCaddrI c

Es decir, un entero largo sin signo. 3dem&s de utili'ar las estructuras necesarias, tambi%n deberemos emplear los formatos necesarios. En comunicaciones telem&ticas entran en "uego ordenadores de muy diversas naturale'as, con representaciones diferentes de los datos en memoria. La familia de microprocesadores xLT, por e"emplo, guarda los valores num%ricos en memoria utili'ando la

Programacin de Sistemas

.0

representacin 9LittleJEndian:, es decir, para guardar 9*,14+./0 :, se almacena as41 9/0+.14*, :, es decir, el byte de menos peso *9/0:, al principio, luego el siguiente *9+.:,, el siguiente *914:, y por 0ltimo, el byte de m&s peso *9*,:,. La representacin 98igJEndian:, empleada por los microprocesadores Botorola, por e"emplo, guardar4a 9*,14+./0: as41 9*,14+./0:. Si dos ordenadores de estas caracter4sticas compartieran informacin sin ser unificada, el resultado ser4a ininteligible para ambas partes. +or ello disponemos de un con"unto de funciones que traducen de el formato local *9host:, al formato de la red *9net5or.:, y viceversa1
uint1,Ct uint*.Ct uint1,Ct uint*.Ct c htonlAuint1,Ct hostlongBI htonsAuint*.Ct hostshortBI ntohlAuint1,Ct netlongBI ntohsAuint*.Ct netshortBI

Qui'& pare'ca complicado, pero sus nombres son muy representativos1 9h: significa 9host:[y 9n: significa 9net5or.:. Las funciones que acaban en 9l: son para enteros largos *9long int:, como los usados en las direcciones (+, y las que acaban en 9s: son para enteros cortos *9short int:, como los usados al especificar un n0mero de puerto,. +or lo tanto para indicar un n0mero de puerto, por e"emplo, podr4amos hacerlo as41
sinC ort Y htonsA 0: BI

Es decir, convertimos 90:: del formato de nuestro host, al formato de red *9h: to 9n:,, y como es un 9short: usamos htonsAB. Ra para terminar con los formatos veremos dos funciones m&s. ormalmente la gente no utili'a enteros largos para representar sus direcciones (+, sino que usan la notacin decimal, por e"emplo1 ACF.?FT.AFF.<D. +ero como ya hemos visto, inCaddr necesita un entero largo para representar la direccin (+. +ara poder pasar de una notacin a otra tenemos dos funciones a nuestra disposicin1
int inetCatonAconst char Fc - struct inCaddr Fin BI char FinetCntoaAstruct inCaddr inBI

Los nombres de las funciones tambi%n ayudan1 inetCatonAB traduce de un array *9a:, de chars, es decir, un string, a una direccin de red *9n:, de 9net5or.:,, mientras que inetCntoaAB traduce de una direccin de red *9n:, a un array de chars *9a:,. >eamos un e"emplo de su uso1
struct inCaddr miCaddrI inetCatonA `*1:.,:..*::.+3b- sAmiCaddrB BI rint2A `&ireccion "P: ]sanb- inetCntoaA miCaddr B BI

+ara terminar este apartado, vamos a ver cmo rellenar una estructura soc8addrCin desde el principio. (maginemos que queremos preparar la

Programacin de Sistemas

.3

estructura 9miCes t ruc tu ra : para conectarnos al puerto LF del host 9ACF.?FT.AFF.<D:. $eber4amos hacer los siguiente1
struct soc8addrCin miCestructuraI miCestructura.sinC2amil? Y G!C"N%)I miCestructura.sinC ort Y htonsA 0: BI inetCatonA `*1:.,:..*::.+3b- sAmiCestructura.sinCaddrB BI memsetA sAmiCestructura.sinCzeroB- ua:v- 0 BI

Como ya sabemos, 9s inC 2ami l: ? siempre va a ser 9G!C "N%):. +ara definir 9sinC ort:, utili'amos htonsAB con el ob"eto de poner el puerto en formato de red. La direccin (+ la definimos desde el formato decimal a la estructura 9sinCaddr: con la funcin inetCatonAB, como sabemos. R por 0ltimo necesitamos L bytes a F *9a::, en 9sinCzero:, cosa que conseguimos utili'ando la funcin memsetAB. ;ealmente podr4a copiarse este fragmento de cdigo y utili'arlo siempre as4 sin variacin1
Jde2ine PU%L)7 0: Jde2ine &"L%55"7N `*1:.,:..*::.+3b struct soc8addrCin miCestructuraI miCestructura.sinC2amil? Y G!C"N%)I miCestructura.sinC ort Y htonsA PU%L)7 BI inetCatonA &"L%55"7N- sAmiCestructura.sinCaddrB BI memsetA sAmiCestructura.sinCzeroB- ua:v- 0 BI

*.+./.4 !unciones necesarias !na ve' conocidos los tipos de datos que emplearemos a la hora de programar nuestras aplicaciones de red, es hora de ver las llamadas que nos proporcionar&n la creacin de conexiones, env4o y recepcin de datos, etc. Lo primero que debemos obtener a la hora de programar una aplicacin de red es un soc.et. Ra hemos explicado que un soc.et es un conector o enchufe para poder reali'ar intercomunicaciones entre procesos, y sirve tanto para crear un programa que pone un puerto a la escucha, como para conectarse a un determinado puerto. !n soc.et es un fichero, como todo en ! (), por lo que la llamada a la funcin soc8etAB crear& el soc.et y nos devolver& un descriptor de fichero1
int soc8etAint domain- int t? e- int rotocolBI

$e los tres par&metros que recibe, slo nos interesa fi"ar uno de ellos1 9t? e:. $eberemos decidir si queremos que sea un soc.et de flu"o *9S75MCS)L%G9:,o un soc.et de datagramas *9S75MC&GLG9:,. El resto de par&metros se pueden fi"ar a 9G!C"N%): para el dominio de direcciones, y a 9::, para el protocolo *de esta manera se selecciona el protocolo autom&ticamente,1
miCsoc8et Y soc8etA G!C"N%)- S75MC&GLG9- : BI

Programacin de Sistemas

/:

Ra sabemos crear soc.ets, utilic%moslos para conectarnos a un servidor remoto. +ara conectarnos a otro ordenador deberemos utili'ar la funcin connec t A , B que recibe tres par&metros1
int connectAint soc82d- const struct soc8addr Fser'Caddr- soc8lenCt addrlenBI

El primero de ellos es el soc.et *9soc8 2d :, que acabamos de crear, el segundo es un puntero a una estructura de tipo soc8addr *9ser'Caddr:, recientemente explicada *recordemos que soc8addrCin era una estructura soc8addr especialmente dise2ada para protocolos de (nternet, as4 que nos sirve aqu4,, y por 0ltimo es preciso indicar el tama2o de dicha estructura *9addrlen:,. >eamos un fragmento de cdigo que ponga todo esto en "uego1
Jde2ine PU%L)7 0: Jde2ine &"L%55"7N `*1:.,:..*::.+3b int miCsoc8et- tamI struct soc8addrCin miCestructuraI miCestructura.sinC2amil? Y G!C"N%)I miCestructura.sinC ort Y htonsA PU%L)7 BI inetCatonA &"L%55"7N- sAmiCestructura.sinCaddrB BI memsetA sAmiCestructura.sinCzeroB- ua:v- 0 BI miCsoc8et Y soc8etA G!C"N%)- S75MCS)L%G9- : BI tam Y sizeo2A struct soc8addr BI connectA miCsoc8et- Astruct soc8addr FBsmiCestructura- tam BI

Como vemos, lo 0nico que hemos hecho es "untar las pocas cosas vistas hasta ahora. Con ello ya conseguimos establecer una conexin remota con el servidor especificado en las constantes &"L%55"7N y PU%L)7. Ser4a conveniente comprobar todos los errores posibles que podr4a provocar este cdigo, como que connectAB no logre conectar con el host remoto, que la creacin del soc.et falle, etc. +ara poder enviar y recibir datos existen varias funciones. Ra avan'amos anteriormente que un soc.et es un descriptor de fichero, as4 que en teor4a ser4a posible escribir con writeAB y leer con readAB, pero hay funciones mucho m&s cmodas para hacer esto. $ependiendo si el soc.et que utilicemos es de tipo soc.et de flu"o o soc.et de datagramas emplearemos unas funciones u otras1 +ara soc.ets de flu"o1 sendAB y rec'AB. +ara soc.ets de datagramas1 sendtoAB y rec'2romAB. Los prototipos de estas funciones son los siguientes1
int sendAint s- const 'oid Fmsg- sizeCt len- int 2lagsBI int sendtoAint s- const 'oid Fmsg- sizeCt len- int 2lags- const struct soc8addr Fto- soc8lenCt tolenBI

Programacin de Sistemas

/*

int rec'Aint s- 'oid Fbu2- sizeCt len- int 2lagsBI int rec'2romAint s- 'oid Fbu2- sizeCt len- int 2lags- struct soc8addr F2rom- soc8lenCt F2romlenBI

El par&metro 9s: es el soc.et que emplearemos. =anto 9msg : como 9bu2: son los buffers que utili'aremos para almacenar el mensa"e que queremos enviar o recibir. +osteriormente tenemos que indicar el tama2o de ese buffer, con 9len:. En el campo 92lags: se pueden indicar varias opciones "unt&ndolas mediante el operador /;, pero funcionar& perfectamente si ponemos un F *significa no elegir ninguna de esas opciones m0ltiples,. Las funciones para soc.ets de datagramas incluyen adem&s el puntero a la estructura soc8addr y un puntero a su tama2o, tal y como ocurr4a con connectAB. Esto es as4 porque una conexin mediante este tipo de soc.et no requiere hacer un connectAB previo, por lo que es necesario indicar la direccin (+ y el n0mero de puerto para enviar o recibir los datos. >eamos unos fragmentos de cdigo de e"emplo1
char bu2Cen'ioDE Y `;ola mundo telematico<aranbI char bu2Crece cionD,++EI int tam- numb?tesI // aSu# creamos el soc8et miCsoc8et ? // la estructura miCestructura- como hemos hecho antes tam Y sizeo2A struct soc8addr BI connectA miCsoc8et- Astruct soc8addr FBsmiCestructura- tam BI numb?tes Y sendA miCsoc8et- bu2Cen'io- strlenA bu2Cen'io B- : BI rint2A `]d b?tes en'iadosanb- numb?tes BI numb?tes Y rec'A miCsoc8et- bu2Crece cion- ,++$*- : BI rint2A `]d b?tes recibidosanb- numb?tes BI

Creamos dos buffers, uno para contener el mensa"e que queremos enviar, y otro para guardar el mensa"e que hemos recibido *de ?<< bytes,. En la variable 9numb?tes: guardamos el n0mero de bytes que se han enviado o recibido por el soc.et. 3 pesar de que en la llamada a rec'AB pidamos recibir ?<@ bytes *el tama2o del buffer menos un byte, para indicar con un 9a:: el fin del string,, es posible que recibamos menos, por lo que es muy recomendable guardar el n0mero de bytes en dicha variable. El siguiente cdigo es similar pero para para soc.ets de datagramas1
char bu2Cen'ioDE Y `;ola mundo telematico<aranbI char bu2Crece cionD,++EI int tam- numb?tesI // aSu# creamos el soc8et miCsoc8et ? // la estructura miCestructura- como hemos hecho antes tam Y sizeo2A struct soc8addr BI

Programacin de Sistemas

/,

// no es reciso hacer connectAB numb?tes Y sendtoA miCsoc8et- bu2Cen'io- strlenA bu2Cen'io B- :Astruct soc8addr FBsmiCestructura- tam BI rint2A `]d b?tes en'iadosanb- numb?tes BI numb?tes Y rec'2romA miCsoc8et- bu2Crece cion- ,++$*- :Astruct soc8addr FBsmiCestructura- stam BI rint2A `]d b?tes recibidosanb- numb?tes BI

Las diferencias con el cdigo anterior son bastante f&ciles de ver1 no hay necesidad de hacer connec t A , B porque la direccin y puerto los incluimos en la llamada a sendto A By rec'2romAB. El puntero a la estructura 9miCestructura: tiene que ser de tipo 9soc8addr:, as4 que hacemos un cast, y el tama2o tiene que indicarse en rec'2romAB como un puntero al entero que contiene el tama2o, as4 que referenciamos la variable 9tam:. Con todo lo visto hasta ahora ya sabemos hacer clientes de red, que se conecten a hosts remotos tanto mediante protocolos orientados a la conexin, como telnet o http, como mediante protocolos no orientados a la conexin ,como tftp o dhcp. El proceso para crear aplicaciones que escuchen un puerto y acepten conexiones requiere comprender el uso de unas cuantas funciones m&s. +ara crear un servidor de soc.ets de flu"o es necesario reali'ar una serie de pasos1 A. Crear un soc.et para aceptar las conexiones, mediante soc8etAB. ?. 3sociar ese soc.et a un puerto local, mediante bindAB. C. +oner dicho puerto a la escucha, mediante listenAB. @. 3ceptar las conexiones de los clientes, mediante acce tAB. <. +rocesar dichas conexiones. La llamada a bindAB asocia el soc.et que acabamos de crear con un puerto local. Cuando un paquete de red llegue a nuestro ordenador, el n0cleo del Sistema /perativo ver& a qu% puerto se dirige y utili'ar& el soc.et asociado para encaminar los datos. La llamada a bindAB tiene unos par&metros muy poco sorprendentes1
int bindAint soc82d- struct soc8addr Fm?Caddr- soc8lenCt addrlenBI

Es decir, el soc.et, la estructura que indica la direccin (+ y el puerto, y el tama2o de dicha estructura. !n e"emplo del uso de bindAB1
Jde2ine PU%L)7 0: Jde2ine &"L%55"7N `*1:.,:..*::.+3b int miCsoc8et- tamI struct soc8addrCin miCestructuraI miCestructura.sinC2amil? Y G!C"N%)I

Programacin de Sistemas

/1

miCestructura.sinC ort Y htonsA PU%L)7 BI inetCatonA &"L%55"7N- sAmiCestructura.sinCaddrB BI memsetA sAmiCestructura.sinCzeroB- ua:v- 0 BI miCsoc8et Y soc8etA G!C"N%)- S75MCS)L%G9- : BI tam Y sizeo2A struct soc8addr BI bindA miCsoc8et- Astruct soc8addr FBsmiCestructura- tam BI

Si quisi%ramos poner a la escucha nuestra propia direccin, sin tener que saber cu&l es en concreto, podr4amos haber empleado 9"NG&&LCGNP :, y si el puerto que ponemos a la escucha nos da igual, podr4amos haber puesto el n0mero de puerto 9::, para que el propio .ernel decida cu&l darnos1
miCestructura.sinC2amil? Y G!C"N%)I miCestructura.sinC ort Y :I miCestructura.sinCaddr.sCaddr Y "NG&&LCGNPI memsetA sAmiCestructura.sinCzeroB- ua:v- 0 BI

Esta es la 0nica llamada que se requiere para crear un servidor de soc.ets de datagramas, ya que no est&n orientados a la conexin. +ara crear servidores de soc.ets de flu"o, una ve' hayamos asociado el soc.et al puerto con b indA , B deberemos ponerlo a la escucha de peticiones mediante la funcin l i s ten.A B Esta funcin recibe slo dos par&metros1
int listenAint s- int bac8logBI

El primero de ellos es el soc.et, y el segundo es el n0mero m&ximo de conexiones a la espera que puede contener la cola de peticiones. $espu%s de poner el puerto a la escucha, aceptamos conexiones pendientes mediante la llamada a la funcin acce t A . B Esta funcin saca de la cola de peticiones una conexin y crea un nuevo soc.et para tratarla. ;ecordemos qu% sucede cuando llamamos al n0mero de informacin de =elefnica1 marcamos el n0mero *connec t A , B y como hay alguien atento a coger el tel%fono *listenAB,, se acepta nuestra llamada que pasa a la espera *en funcin del bac8log de listenAB puede que nos informen de que todas las l4neas est&n ocupadas,. $espu%s de tener que escuchar una horrible sinton4a, se nos asigna un telefonista *acce tAB, que atender& nuestra llamada. Cuando se nos ha asignado ese telefonista, en realidad estamos hablando ya por otra l4nea, porque cualquier otra persona puede llamar al n0mero de informacin de =elefnica y la llamada no dar4a la se2al de comunicando. uestro servidor puede aceptar varias peticiones *tantas como indiquemos en el bac8log de listenAB, y luego cuando aceptemos cada una de ellas, se crear& una l4nea dedicada para esa peticin *un nuevo soc.et por el que hablar,. >eamos un e"emplo con todo esto1
Jde2ine PU%L)7 0: int miCsoc8et- nue'o- tamI struct soc8addrCin miCestructuraI miCestructura.sinC2amil? Y G!C"N%)I

Programacin de Sistemas

/4

miCestructura.sinC ort Y htonsA PU%L)7 BI miCestructura.sinCaddr.sCaddr Y "NG&&LCGNPI memsetA sAmiCestructura.sinCzeroB- ua:v- 0 BI miCsoc8et Y soc8etA G!C"N%)- S75MCS)L%G9- : BI tam Y sizeo2A struct soc8addr BI bindA miCsoc8et- Astruct soc8addr FBsmiCestructura- tam BI listenA miCsoc8et- + BI whileA * B // bucle in2inito ara tratar conexiones _ nue'o Y acce tA miCsoc8et- Astruct soc8addr FBsmiCestructurastam BI i2A 2or8AB YY : B _ // hi=o closeA miCsoc8et BI // %l roceso hi=o no lo necesita sendA nue'o- h,:: (ien'enidoanh- *+- :BI closeA nue'o BI exitA : BI c else _ // adre closeA nue'o BI // %l roceso adre no lo necesita c c

Lo m&s extra2o de este e"emplo puede ser el bucle 9whi le :, todo lo dem&s es exactamente igual que en anteriores e"emplos. >eamos ese bucle1 lo primero de todo es aceptar una conexin de las que est%n pendientes en el bac8 log de conexiones. La llamada a acce tAB nos devolver& el nuevo soc.et creado para atender dicha peticin. Creamos un proceso hi"o que se encargue de gestionar esa peticin mediante 2or8AB. $entro del hi"o cerramos el soc.et inicial, ya que no lo necesitamos, y enviamos 9,:: (ien'enidoan: por el soc.et nuevo. Cuando hayamos terminado de atender al cliente, cerramos el soc.et con closeAB y salimos. En el proceso padre cerramos el soc.et 9nue'o:, ya que no lo utili'aremos desde este proceso. Este bucle se e"ecuta indefinidamente, ya que nuestro servidor deber& atender las peticiones de conexin indefinidamente. Ra hemos visto cmo cerrar un soc.et, utili'ando la llamada est&ndar como con cualquier fichero. Esta funcin cierra el descriptor de fichero del soc.et, liberando el soc.et y denegando cualquier env4o o recepcin a trav%s del mismo. Si quisi%ramos tener m&s control sobre el cierre del soc.et podr4amos usar la funcin shutdownAB1
closeAB, int shutdownAint s- int howBI

En el par&metro 9how: indicamos cmo queremos cerrar el soc.et1 :1 o se permite recibir m&s datos. *1 o se permite enviar m&s datos. ,1 o se permite enviar ni recibir m&s datos *lo mismo que closeAB,.

Programacin de Sistemas

/+

Esta funcin no cierra realmente el descriptor de fichero del soc.et, sino que modifica sus condiciones de uso, es decir, no libera el recurso. +ara liberar el soc.et despu%s de usarlo, deberemos usar siempre c lose A.B *.+./.+ %=em los con soc8ets de ti o stream Ser'idor Ee aqu4 el cdigo de e"emplo de un servidor sencillo =C+1
Jinclude Jinclude Jinclude Jinclude Jinclude Zunistd.hK Znetinet/in.hK Zar a/inet.hK Zs?s/t? es.hK Zs?s/soc8et.hK

int mainA int argc- char Farg'DE B _ int miCsoc8et- nue'o- tamI struct soc8addrCin miCestructuraI miCestructura.sinC2amil? Y G!C"N%)I miCestructura.sinC ort Y :I miCestructura.sinCaddr.sCaddr Y "NG&&LCGNPI memsetA sAmiCestructura.sinCzeroB- \a:\- 0 BI miCsoc8et Y soc8etA G!C"N%)- S75MCS)L%G9- : BI tam Y sizeo2A struct soc8addr BI bindA miCsoc8et- Astruct soc8addr FBsmiCestructura- tam BI listenA miCsoc8et- + BI whileA * B // bucle in2inito ara tratar conexiones _ nue'o Y acce tA miCsoc8etAstruct soc8addr FBsmiCestructura- stamBI i2A 2or8AB YY : B _ // hi=o closeA miCsoc8et BI // %l roceso hi=o no lo necesita sendA nue'o- h,:: (ien'enidoanh- *+- : BI closeA nue'o BI exitA : BI c else _ // adre closeA nue'o BI // %l roceso adre no lo necesita c c return :I c

!n e"emplo de su e"ecucin1
txi i@neon:NO gcc ser'ertc .c $o ser'ertc

Programacin de Sistemas

/.

txi i@neon:NO ./ser'ertc s D*E 4*3 txi i@neon:NO netstat $ ta ANot all rocesses could be identi2ied- non$owned rocess in2o will not be shown- ?ou would ha'e to be root to see it all.B Gcti'e "nternet connections Aser'ers and establishedB Proto Lec'$d Send$d Local Gddress !oreign Gddress State P"&/Program name tc : : F:www $ tc : : F:4.*:* 4*3/ser'ertc F:F F:F L"S)%N L"S)%N

txi i@neon:NO telnet localhost 4.*:* )r?ing *,/.:.:.*... 5onnected to localhost. %sca e character is \[E\. ,:: (ien'enido 5onnection closed b? 2oreign host.

3l haber indicado como n0mero de puerto el F, ha elegido un n0mero de puerto aleatorio de entre los posibles *de AF?@ a T<<C<, ya que slo root puede hacer b indA Bsobre los puertos 9ba"os:, del A al AF?@,. 5liente Ee aqu4 el cdigo de e"emplo de un simple cliente =C+1
Jinclude Jinclude Jinclude Jinclude Jinclude Zunistd.hK Znetinet/in.hK Zar a/inet.hK Zs?s/t? es.hK Zs?s/soc8et.hK

Jde2ine S"H% ,++ int mainAint argc- char Farg'DEB _ int miCsoc8et- tam- numb?tesI char bu22erDS"H%EI struct soc8addrCin miCestructuraI i2A argc <Y 1 B _ rint2A herror: modo de em leo: clienttc i exitA $* BI c

uertoanh BI

miCestructura.sinC2amil? Y G!C"N%)I miCestructura.sinC ort Y htonsA atoiA arg'D,E B BI inetCatonA arg'D*E- sAmiCestructura.sinCaddrB BI memsetA sAmiCestructura.sinCzeroB- \a:\- 0 BI miCsoc8et Y soc8etA G!C"N%)- S75MCS)L%G9- :BI tam Y sizeo2A struct soc8addr BI connectA miCsoc8et- Astruct soc8addr FBsmiCestructura- tam BI

Programacin de Sistemas

//

numb?tes Y rec'A miCsoc8et- bu22er- S"H%$*- : BI bu22erDnumb?tesE Y \a:\I rint2A h]d b?tes recibidosanh- numb?tes BI rint2A hrecibido: ]sanh- bu22er BI closeA miCsoc8et BI return :I c

>e&moslo en funcionamiento1
txi i@neon:N/ rogramacionO gcc clienttc .c $o clienttc txi i@neon:N/ rogramacionO ./clienttc *,/.:.:.* 4.*:+ *+ b?tes recibidos recibido: ,:: (ien'enido

*.+./.. %=em los con soc8ets de ti o datagrama Ser'idor Ee aqu4 el cdigo de e"emplo de un servidor sencillo !$+1
Jinclude Jinclude Jinclude Jinclude Jinclude Zunistd.hK Znetinet/in.hK Zar a/inet.hK Zs?s/t? es.hK Zs?s/soc8et.hK

Jde2ine PU%L)7 +::: Jde2ine S"H% ,++ int mainAint argc- char Farg'DEB _ int miCsoc8et- tam- numb?tesI char bu22erDS"H%EI struct soc8addrCin miCestructuraI miCestructura.sinC2amil? Y G!C"N%)I miCestructura.sinC ort Y htonsA PU%L)7 BI miCestructura.sinCaddr.sCaddr Y "NG&&LCGNPI memsetA sAmiCestructura.sinCzeroB- \a:\- 0BI miCsoc8et Y soc8etA G!C"N%)- S75MC&GLG9- :BI tam Y sizeo2A struct soc8addr BI bindA miCsoc8et- Astruct soc8addr FBsmiCestructura- tam BI whileA * B // bucle in2inito ara tratar conexiones _ numb?tes Y rec'2romA miCsoc8et- bu22er- S"H%$*- :- Astruct soc8addr FBsmiCestructurastam BI bu22erDnumb?tesE Y \a:\I rint2A hser'erud : ]d b?tes recibidosanh- numb?tes BI rint2A hser'erud : recibido: ]sanh- bu22er BI

Programacin de Sistemas

/0

c closeA miCsoc8et BI return :I c

El puerto a la escucha se define con la constante PU%L)7 , en este caso tiene el valor +::: . Con 9netstat Wu a: podemos ver que realmente est& a la escucha1
txi i@neon:NO netstat $u a ANot all rocesses could be identi2ied- non$owned rocess in2o will not be shown- ?ou would ha'e to be root to see it all.B Gcti'e "nternet connections Aser'ers and establishedB Proto Lec'$d Send$d Local Gddress !oreign Gddress State ud : : F:tal8 F:F $ ud : : F:ntal8 F:F $ ud : : F:+::: F:F /,0/ser'erud

P"&/Program name

5liente Ee aqu4 el cdigo de e"emplo de un simple cliente !$+1


Jinclude Jinclude Jinclude Jinclude Jinclude Zunistd.hK Znetinet/in.hK Zar a/inet.hK Zs?s/t? es.hK Zs?s/soc8et.hK

Jde2ine S"H% ,++ int mainAint argc- char Farg'DEB _ int miCsoc8et- tam- numb?tesI char bu22erDS"H%EI struct soc8addrCin miCestructuraI i2A argc <Y 1 B _ rint2A herror: modo de em leo: clienttc i exitA $* BI c

uertoanh BI

miCestructura.sinC2amil? Y G!C"N%)I miCestructura.sinC ort Y htonsA atoiA arg'D,E B BI inetCatonA arg'D*E- sAmiCestructura.sinCaddrB BI memsetA sAmiCestructura.sinCzeroB- \a:\- 0 BI miCsoc8et Y soc8etA G!C"N%)- S75MC&GLG9- :BI tam Y sizeo2A struct soc8addr BI strc ?A bu22er- h;ola mundo telematico<anh BI numb?tes Y sendtoA miCsoc8et- bu22er- strlenAbu22erB- :- Astruct soc8addr FBsmiCestructura- tam BI rint2A hclientud : ]d b?tes en'iadosanh- numb?tes BI

Programacin de Sistemas

/3

rint2A hclientud : en'iado: ]sanh- bu22er BI strc ?A bu22er- h%ste es otro aSuete.anh BI numb?tes Y sendtoA miCsoc8et- bu22er- strlenAbu22erB- :- Astruct soc8addr FBsmiCestructura- tam BI rint2A hclientud : ]d b?tes en'iadosanh- numb?tes BI rint2A hclientud : en'iado: ]sanh- bu22er BI closeA miCsoc8et BI return :I c

>e&moslo en funcionamiento1
txi i@neon:NO gcc clientud .c $o clientud txi i@neon:NO ./clientud *,/.:.:.* +::: clientud : ,1 b?tes en'iados clientud : en'iado: ;ola mundo telematico< clientud : ,, b?tes en'iados clientud : en'iado: %ste es otro aSuete. ser'erud : ,1 b?tes recibidos ser'erud : recibido: ;ola mundo telematico< ser'erud : ,, b?tes recibidos ser'erud : recibido: %ste es otro aSuete.

Programacin de Sistemas

0:

,.Licencia

R3456547873695:C58;<=97=I>?<@ 2.5 E2;<A<

U293B 32 @7C=3 B3D co iar- distribuir ? comunicar ^blicamente la obra hacer obras deri'adas hacer un uso comercial de esta obra

$<E5 @<2 456B7475632 27>?736932D R3456547873695. &ebe reconocer los crkditos de la obra de la manera es eci2icada or el autor o el licenciador.

C58;<=97= C<E5 @< 8728< @743647<. Si altera o trans2orma esta obra- o genera una obra deri'ada- slo uede distribuir la obra generada ba=o una licencia idkntica a ksta. Gl reutilizar o distribuir la obra- tiene Sue de=ar bien claro los tkrminos de la licencia de esta obra. Glguna de estas condiciones uede no a licarse si se obtiene el ermiso del titular de los derechos de autor L52 B3=34F52 B3=7G<B52 B3 ?252 @3>H97852 ? 59=<2 @7879<475632 =3456547B<2 ;5= @3I 65 23 G36 <J349<B52 ;5= @5 <693=75=.

También podría gustarte