Professional Documents
Culture Documents
El compilador de C Llamadas al sistema Llamadas al sistema para uso de ficheros Flujos estndares y redireccin Manejo de procesos Herramientas IPC y semforos Memoria compartida Colas de mensajes
Pgina 1 de 32
El compilador de C
El compilador de C o El programa cc. Pasos de compilacin o Convenciones en los nombres de ficheros o Uso del compilador o Resumen de opciones para el compilador o El compilador de C++
El compilador de C
Este apartado es suficiente para dominar el uso bsico del compilador de lenguaje C, a travs del programa cc. Para comprender este texto no tienen por qu estar familiarizados con el C, aunque se precisan unas mnimas nociones de este lenguaje. Adems, deberan conocer conceptos bsicos relacionados con el desarrollo de programas, como programa fuente, objeto, enlace (linking), etc.
Pgina 2 de 32
fichero fuente en C fichero cabecera fuente en C (slo til para los #include) fichero fuente en ensamblador (tambin reconocido) fichero fuente tras ser preprocesado (raramente empleado por el usuario) fichero objeto fichero de biblioteca
El compilador de C slo genera un ejecutable, de nombre a.out, aunque se le puede indicar que tenga otro nombre con la opcin -onombre_de_ejecutable.
Si pepe.c es un fichero fuente en C, se compila y se enlaza con las bibliotecas del sistema. Si no haba errores sintcticos ni referencias a funciones o variables inexistentes, se genera el ejecutable a.out. Ejemplo 2:
cc -o pepe main.c utilidades.c pepe.o -lm
Pgina 3 de 32
ste es un ejemplo ms complejo donde anticipamos el uso de un par de opciones de compilacin. La opcin -o pepe sirve para que, en lugar de generar el fichero ejecutable pepe, en lugar de a.out. Para construir el ejecutable se hace uso explcito de cuatro elementos: * Un fuente main.c (quizs con el programa principal) * Otro fuente, utilidades.c (quizs con funciones adicionales) * Un objeto pepe.o (tal vez con otras utilidades ya compiladas) * La biblioteca matemtica estndar (con la opcin -lm) Con este ejemplo hacemos ver que un programa en C puede estar compuesto de varios mdulos en forma de ficheros fuentes. Y que si un mdulo ya est compilado, podemos pasar como argumento al compilador el correspondiente .o para ahorrar tiempo.
compila en C ANSI compila en C de K&R slo compila, no hace el montaje monta la biblioteca lib establece el nombre del ejecutable optimiza el cdigo optimiza el cdigo hasta el nivel n genera un fuente .s en ensamblador; no compila modo locuaz: imprime todos los pasos
Las opciones del compilador de C pueden variar segn la versin de UNIX que se utilice. Laboratorio de operativos: El compilador de C est preparado para compilar en modo ANSI, con lo que no hace falta que escriban la opcin -Aa.
Pgina 4 de 32
biblioteca estndar del C. No hace falta incluirla explcitamente biblioteca estndar matemtica contiene rutinas de visualizacin con ventanas, etc. biblioteca para uso del lex y el yacc
El compilador de C++
Es muy probable que en la mquina en la que trabajen exista un compilador de C++. Describiremos brevemente las caractersticas de uno de los compiladores de C++ ms difundidos, el de la casa GNU (software de distribucin gratuita). El compilador de la GNU se llama gcc, aunque se le puede invocar como gcc o c++. Es capaz de reconocer tanto fuentes en C como en C++. El gcc reconoce como fuentes en C++ a los ficheros con extensin .cxx o .C (letra "C" mayscula). Las opciones admitidas por el gcc son ms o menos las mismas que acepta el compilador convencional de UNIX; aunque siempre es recomendable consultar el manual en lnea. [ ] 1 De hecho el paso de compilacin a objeto suele atravesar una fase intermedia en que se genera un fichero en lenguaje ensamblador y se invoca al programa ensamblador del sistema.
Llamadas al sistema
Llamadas al sistema
El sistema operativo UNIX ofrece un conjunto de llamadas al sistema, en forma de funciones en C, que sirven de interfaz con los servicios provistos por el ncleo. En los siguientes apartados describiremos algunas de estas funciones, en concreto aqullas que consideramos de uso ms frecuente en esta asignatura.
Pgina 5 de 32
Este texto no ha de considerarse un manual de programacin, sino una simple gua. Lo que viene a continuacin es poco ms que un catlogo de funciones con algunos ejemplos de uso, pero no es sustituto de un buen manual de programacin en C bajo UNIX, cuya lectura les recomendamos encarecidamente.
Interfaz de uso
Las llamadas al sistema en UNIX suelen acogerse a este prototipo:
int funcin ( arg1, arg2, ... );
Todas las llamadas al sistema son funciones C que retornan un entero. Habitualmente un valor negativo (especialmente un -1) indica algn tipo de error. Estas funciones siempre van en minsculas y hay una tendencia a la parquedad en los nombres (suelen ser cortos). El nmero de argumentos aceptado en una llamada al sistema UNIX no suele ir ms all de tres.
Cdigos de error
En general, las llamadas al sistema del UNIX devuelven un valor convencional en caso de error (casi siempre un -1). Esta informacin no basta casi nunca para explicar el cariz de la anomala producida. Por suerte, todo programa puede acceder a una variable entera llamada errno, donde se deposita un cdigo de error cada vez que sucede algo incorrecto. Para acceder a errno han de incluir el fichero <errno.h>, que tambin define constantes simblicas para los cdigos de error. Por ejemplo, ENOMEM significa memoria insuficiente, EPERM es para "tipo de acceso denegado" (como cuando queremos abrir en modo escritura un fichero para el que no tenemos permiso de escritura), etc., etc. En los manuales encontrarn el listado completo de los cdigos de error, muchos de los cuales casi nunca se les manifestarn, por fortuna para ustedes.
Pgina 6 de 32
Llamadas al sistema para manejo de ficheros o Manejo de ficheros en UNIX o Apertura, creacin y cierre de ficheros Funcin open Funcin creat Funcin close o Borrado de ficheros o Lectura y escritura o Movimiento del puntero del fichero o Ejemplos Primer programa: creacin y escritura Segundo programa: lectura
Pgina 7 de 32
Laboratorio de Operativos: Las llamadas al sistema del HP-UX se encuentran en los manuales "C Programming Routines (seccin 2)".
usuario grupo otros Se toman los nueve permisos como tres nmeros consecutivos de 3 bits cada uno. Un bit vale 1 si su permiso correspondiente est activado y 0 en caso contrario. Cada nmero se expresa en decimal, del 0 al 7, y los permisos quedan definidos como un nmero octal de tres dgitos. Para poner un nmero en octal en el lenguaje C, se escribe con un cero a la izquierda. Por ejemplo, los permisos rw-r--r-x son el nmero octal 0645.
/* Crea un fichero con permisos RW-R--R-- */ int fd = creat ( "mi_fichero", 0644);
Pgina 8 de 32
El parmetro nombre es la cadena conteniendo el nombre del fichero que se quiere abrir. El parmetro modo establece la forma en que se va a trabajar con el fichero. Algunas constantes que definen los modos bsicos son:
O_RDONLY O_WRONLY O_RDWR O_APPEND O_CREAT O_EXCL O_TRUNC
abre en modo lectura abre en modo escritura abre en modo lectura-escritura abre en modo apndice (escritura desde el final) crea el fichero y lo abre (si exista, se lo machaca) usado con O_CREAT. Si el fichero existe, se retorna un error abre el fichero y trunca su longitud a 0
Para usar estas constantes, han de incluir la cabecera <fcntl.h>. Los modos pueden combinarse, simplemente sumando las constantes, o haciendo un "or" lgico, como en este ejemplo:
O_CREAT | O_WRONLY
El parmetro acceso slo se ha de emplear cuando se incluya la opcin O_CREAT, y es un entero que define los permisos de acceso al fichero creado. Consulten en la bibliografa cmo se codifican los permisos. La funcin open retorna un descriptor vlido si el fichero se ha podido abrir, y el valor -1 en caso de error. .
Pgina 9 de 32
Funcin creat
Si desean expresarmente crear un fichero, disponen de la llamada creat. Su prototipo es
int creat ( char* nombre, int acceso );
Equivale (ms o menos) a llamar a open (nombre, O_RDWR|O_CREAT, acceso). Es decir, devuelve un descriptor si el fichero se ha creado y abierto, y -1 en caso contrario.
Funcin close
Para cerrar un fichero ya abierto est la funcin close:
int close ( int fichero );
donde fichero es el descriptor de un fichero ya abierto. Retorna un 0 si todo ha ido bien y -1 si hubo problemas.
Borrado de ficheros
La funcin
int unlink ( char* nombre );
Lectura y escritura
Para leer y escribir informacin en ficheros, han de abrirlos primero con open o creat. Las funciones read y write se encargan de leer y de escribir, respectivamente:
int read ( int fichero, void* buffer, unsigned bytes ); int write( int fichero, void* buffer, unsigned bytes );
Ambas funciones toman un primer parmetro, fichero, que es el descriptor del fichero sobre el que se pretende actuar. El parmetro buffer es un apuntador al rea de memoria donde se va a efectuar la transferencia. O sea, de donde se van a leer los datos en la funcin read, o donde se van a depositar en el caso de write. El parmetro bytes especifica el nmero de bytes que se van a transferir. Las dos funciones devuelven el nmero de bytes que realmente se han transferido. Este dato es particularmente til en la funcin read, pues es una pista para saber si se ha llegado al final del fichero. En caso de error, retornan un -1.
Pgina 10 de 32
Hay que tener especial cautela con estas funciones, pues el programa no se va a detener en caso de error, ni hay control sobre si el puntero buffer apunta a un rea con capacidad suficiente (en el caso de la escritura), etc., etc. La primera vez que se lee o escribe en un fichero recin abierto, se hace desde el principio del fichero (desde el final si se incluy la opcin O_APPEND). El puntero del fichero se mueve al byte siguiente al ltimo byte ledo o escrito en el fichero. Es decir, UNIX trabaja con ficheros secuenciales.
Como siempre, fichero es el descriptor de un fichero y abierto. El parmetro desp junto con origen sirven para determinar el punto del fichero donde va a acabar el puntero. desp es un entero largo que expresa cuntos bytes hay que moverse a partir del punto indicado en origen, parmetro que podr adoptar estos valores:
0 SEEK_SET 1 SEEK_CUR 2 SEEK_END
inicio del fichero relativo a la posicin actual relativo al final del fichero
Las constantes simblicas se encuentran en <stdlib.h> y <unistd.h>. El parmetro desp puede adoptar valores negativos, siempre que tengan sentido. Si el resultado final da una posicin mayor que el tamao del fichero, ste crece automticamente hasta esa posicin. La funcin lseek devuelve un entero largo que es la posicin absoluta donde se ha posicionado el puntero; o un -1 si hubo error. Obsrvese que la funcin lseek puede utilizarse tambin para leer la posicin actual del puntero.
Pgina 11 de 32
Ejemplos
En las siguientes lneas se muestran dos programas que emplean las llamadas al sistema.
main ( int argc, char* argv[] ) { /* Cadena que se va a escribir */ const char* cadena = "Hola, mundo"; /* Creacin y apertura del fichero */ int fichero = open ("mi_fichero", O_CREAT|O_WRONLY,0644); /* Comprobacin de errores */ if (fichero==-1) { perror("Error al abrir fichero:"); exit(1); } /* Escritura de la cadena */ write(fichero, cadena, strlen(cadena)); close(fichero); return 0; }
main ( int argc, char* argv[] ) { char cadena[11]; /* Depsito de los caracteres */ int leidos; /* Apertura del fichero */ int fichero = open ("mi_fichero", O_RDONLY); /* Comprobacin */ if (fichero==-1) { perror("Error al abrir fichero:"); exit(1); }
Pgina 12 de 32
/* Coloca el puntero en la posicin 400 */ lseek(fichero,400,SEEK_SET); /* Lee diez bytes */ leidos = read(fichero, cadena, 10); close(fichero); cadena[10]=0; /* Mensaje para ver qu se ley */ printf ( "Se leyeron %d bytes. La cadena leda es %s\n", return 0; }
leidos,
Flujos estndares y redireccin o Entrada, salida y error estndares o Utilizacin en un programa en C o Redireccin o Preservar el antiguo flujo estndar: funcin dup
Pgina 13 de 32
Existe tambin el llamado error estndar, flujo donde se vierten los mensajes de error. Habitualmente coincide con la salida estndar, pero se considera un flujo diferente.
Utilizacin en un programa en C
La entrada y la salida estndares en UNIX son utilizables en forma de sendos ficheros que estn abiertos desde el inicio de la ejecucin del programa. Como ya sabrn, cuando se abre un fichero con open se devuelve un descriptor de fichero (file descriptor), que es un nmero entero que se emplear en posteriores llamadas a las funciones de manejo de ficheros como read o write. En UNIX siempre se cumple que la entrada estndar tiene el descriptor de valor 0, y la salida estndar el de valor 1.
Redireccin
Para redirigir la entrada o la salida hay que conseguir abrir el fichero de redireccin de forma que el sistema le asigne el handle 0 1. Cmo lograr esto? Pues teniendo en cuenta que el algoritmo de la llamada open reserva el primer descriptor disponible, empezando a explorar desde el nmero cero. Esto significa que si cerramos la salida estndar (descriptor nmero 1), el siguiente open que se realice asignar al fichero abierto el descriptor 1, quedando automticamente reasignada la salida estndar. Sirva de ejemplo este cdigo:
close (1); open ("pepe.txt",O_CREAT,0777); printf("Estoy escribiendo en pepe.txt\n");
Cualquier nueva operacin sobre la salida estndar se dirigir al fichero "pepe.txt". (Suponiendo que el descriptor 0 no est libre).
Pgina 14 de 32
Ejecucin de procesos
Ejecucin de procesos o La funcin main y los argumentos o Funciones para ejecucin de programas La funcin system() Las llamadas exec... o Procesos concurrentes: llamadas fork y wait
Pgina 15 de 32
Ejecucin de procesos
La funcin main y los argumentos
Habitualmente, cuando se lanza un programa a ejecucin desde el shell, se le aaden parmetros o argumentos para definir exactamente qu queremos de l. Por ejemplo:
vi pepe.txt ls -l /usr/include cp pepe.c ../pipo.c
En el primer caso se invoca al editor vi especificndose que se desea trabajar con el fichero "pepe.txt". El fichero es un parmetro pasado al shell. El segundo caso es una llamada al programa ls que incorpora dos parmetros, como ocurre en el ltimo ejemplo. Qu son los parmetros o argumentos? En principio, puede afirmarse que son conjuntos de caracteres separados por blancos (espacios o tabuladores). Ahora bien, no se consideran parmetros los redireccionamientos, y caracteres como el ampersand "&" o el punto y coma ";" actan de separadores (en general, eso ocurre con todos los caracteres que tengan un significado para el intrprete de rdenes). Ejemplo:En la orden
ls -l /usr/include & >pepe.txt
los parmetros son "ls", "-l" y "/usr/include" ('&' y la redireccin no cuentan). Cada programa recibe los parmetros a travs de su punto de entrada, que en el caso del lenguaje C es la funcin main. El formato mnimo que acepta esta funcin en UNIX es
main ( int argc, char* argv[] ); donde argc expresa cuntos parmetros se han reconocido, y argv es un vector de cadenas de caracteres que precisamente contienen los parmetros, siendo argv[i] el
parmetro i. Los parmetros se empiezan a numerar en 0. El parmetro 0 es el nombre del programa invocado tal y como se pas en la lnea de rdenes. En el ejemplo de vi pepe.txt, los valores de argc y argv seran
argc = 2 argv[0] = "vi" argv[1] = "pepe.txt"
Pgina 16 de 32
ejecuta la cadena pasada como parmetro tal y como si la hubiramos tecleado desde la consola. La funcin system se limita a lanzar un shell hijo pasndole como parmetro de entrada la cadena suministrada en la funcin. La forma de ms bajo nivel para ejecutar una orden consiste en lanzar a ejecucin el programa deseado mediante alguna de las llamadas al sistema que empiezan por exec. Existen varias modalidades que difieren en la forma de pasar los parmetros al programa (aunque realmente se trata de una sola llamada al sistema UNIX).
Es decir, el nombre del fichero y luego todos los argumentos consecutivamente, terminando con un puntero nulo (vale con un cero). Sirva este ejemplo: Para ejecutar
/bin/ls -l /usr/include
escribiramos
execl ( "/bin/ls", "ls", "-l", "/usr/include", 0 );
Obsrvese que el primer argumento coincide con el nombre del programa. En caso de desconocer con anticipacin el nmero de argumentos, habr que emplear la funcin execv, que tiene este prototipo:
execv ( char* fichero, char* argv [] );
Pgina 17 de 32
El parmetro argv es una tira de cadenas que representan los argumentos del programa lanzado, siendo la ltima cadena un nulo (un cero). El ejemplo anterior se resolvera as:
char* tira [] = { "ls", "-l", "/usr/include", 0 }; ... execv ( "/bin/ls", tira );
En los anteriores ejemplos se ha escrito el nombre completo del fichero para ejecutar ("/bin/ls" en vez de "ls" a secas). Esto es porque tanto execl como execv ignoran la variable PATH, que contiene las rutas de bsqueda. Para tener en cuenta esta variable pueden usarse las versiones execlp o execvp. Por ejemplo:
execvp ( "ls", tira );
ejecutara el programa "/bin/ls", si es que la ruta "/bin" est definida en la variable PATH. Todas las llamadas exec... retornan un valor no nulo si el programa no se ha podido ejecutar. En caso de que s se pueda ejecutar el programa, se transfiere el control a ste y la llamada exec... nunca retorna. En cierta forma el programa que invoca un exec desaparece del mapa.
Al llamar a esta funcin se crea un nuevo proceso (proceso hijo), idntico en cdigo y datos al proceso que ha realizado la llamada (proceso padre). Los espacios de memoria del padre y el hijo son disjuntos, por lo que el proceso hijo es una copia idntica del padre que a partir de ese momento sigue su vida separada, sin afectar a la memoria del padre; y viceversa. Siendo ms concretos, las variables del proceso padre son inicialmente las mismas que las del hijo. Pero si cualquiera de los dos procesos altera una variable, el cambio slo repercute en su copia local. Padre e hijo no comparten memoria. El punto del programa donde el proceso hijo comienza su ejecucin es justo en el retorno de la funcin fork, al igual que ocurre con el padre. Si el proceso hijo fuera un mero clon del padre, ambos ejecutaran las mismas instrucciones, lo que en la mayora de los casos no tiene mucha utilidad. El UNIX permite distinguir si se es el proceso padre o el hijo por medio del valor de retorno de fork. Esta funcin devuelve un cero al proceso hijo, y el identificador de proceso (PID) del hijo al proceso padre. Como se garantiza que el PID siempre es no nulo, basta aplicar un if para determinar quin es el padre y quin el hijo para as ejecutar distinto cdigo.
Pgina 18 de 32
Este programa mostrar por la salida estndar las cadenas "Soy el hijo, x=1" y "Soy el padre, x=33", en un orden que depender del compilador y del planificador de procesos de la mquina donde se ejecute. Como aplicacin de fork a la ejecucin de programas, vase este otro pequeo ejemplo, que adems nos introducir en nuevas herramientas:
1 2 3 4 4b 5 6 7 8 9 10 11 if ( fork()==0 ) { execlp ("ls","ls","-l","/usr/include",0); printf ("Si ves esto, no se pudo ejecutar el \ programa\n"); exit(1); } else { int tonta; wait(&tonta); }
Este fragmento de cdigo lanza a ejecucin la orden ls -l /usr/include, y espera por su terminacin. En la lnea 1 se verifica si se es padre o hijo. Generalmente es el hijo el que toma la iniciativa de lanzar un fichero a ejecucin, y en las lneas 2 a la 6 se invoca a un programa con execlp. La lnea 4 slo se ejecutar si la llamada execlp no se ha podido cumplir. La llamada a exit garantiza que el hijo no se dedicar a hacer ms tareas. Las lneas de la 8 a la 11 forman el cdigo que ejecutar el proceso padre, mientras el hijo anda a ejecutar sus cosas. Aparece una nueva llamada el sistema, la funcin wait. Esta funcin bloquea al proceso llamador hasta que alguno de sus hijos termina. Para nuestro ejemplo, dejar al padre bloqueado hasta que se ejecute el programa lanzado o se ejecute la lnea 5, terminando en ambos casos el discurrir de su nico hijo. Es decir, la funcin wait es un mecanismo de sincronizacin entre un proceso padre y sus hijos.
Pgina 19 de 32
La llamada wait recibe como parmetro un puntero a entero donde se deposita el valor devuelto por el proceso hijo al terminar; y retorna el PID del hijo. El PID del hijo es una informacin que puede ser til cuando se han lanzado varios procesos hijos y se desea discriminar quin exactamente ha terminado. En nustro ejemplo no hace falta este valor, porque slo hay un hijo por el que esperar. Como ejemplo final, esta funcin en C es una implementacin sencilla de la funcin system, haciendo uso de fork, wait y una funcin exec.
int system ( const char* str ) { int ret; if (!fork()) { execlp ( "sh", "sh", "-c", str, 0 ); return -1; } wait (&ret); return ret; }
Comunicacin entre procesos (IPC) o Semforos o Llamadas al sistema para semforos Apertura o creacin de un semforo Operaciones de control sobre semforos Operaciones sobre semforos o Ejemplos de uso
Pgina 20 de 32
Semforos
Qu es un semforo para el UNIX? Formalmente es muy similar a la definicin clsica de Dijkstra, en el sentido de que es una variable entera con operaciones atmicas de inicializacin, incremento y decremento con bloqueo. El UNIX define tres operaciones fundamentales sobre semforos: * semget Crea o toma el control de un semforo * semctl Operaciones de lectura y escritura del estado del semforo. Destruccin del semforo * semop Operaciones de incremento o decremento con bloqueo Como el lenguaje C no tiene un tipo "semforo" predefinido, si queremos usar semforos tenemos que crearlos mediante una llamada al sistema (semget). Esta llamada permite crear un conjunto de semforos, en lugar de uno solo. Las operaciones se realizan atmicamente sobre todo el conjunto; esto evita interbloqueos y oscuras programaciones en muchos casos, pero para esta prctica es ms bien un engorro. Al crear un semforo se nos devuelve un nmero identificador, que va a funcionar casi igual que los identificadores de fichero de las llamadas open, creat, etc. La funcin semget nos permite adems "abrir" un semforo que ya est creado. As, por ejemplo, si un proceso crea un semforo, otros procesos pueden sincronizarse con aqul (con ciertas restricciones) disponiendo del semforo con semget. Para darle un valor inicial a un semforo, se utiliza la funcin semctl. El UNIX no ofrece las funciones clsicas P y V o equivalentes, sino que dispone de una funcin general llamada semop que permite realizar una gama de operaciones que incluyen las P y V.
semctl tambin se emplea para destruir un semforo.
Las tres funciones devuelven -1 si algo ha ido mal y en tal caso la variable errno informa del tipo de error.
Pgina 21 de 32
a) key vale IPC_PRIVATE. Este valor especial obliga a semget a crear un nuevo y nico identificador, nunca devuelto por ulteriores llamadas a semget hasta que sea liberado con semctl. b) key no est asociada a ningn semforo existente, y se cumple que (semflg & IPC_CREAT) es cierto. A un semforo puede accederse siempre que se tengan los permisos adecuados. Si se crea un nuevo semforo, el parmetro nsems indica cuntos semforos contiene el conjunto creado; los 9 bits inferiores de semflg contienen los permisos estilo UNIX de acceso al semforo (usuario, grupo, otros). semflg es una mscara que puede contener IPC_CREAT, que ya hemos visto, o IPC_EXCL, que hace crear el semforo, pero fracasando si ya exista. Ejemplo:
int semid = semget ( IPC_PRIVATE, 1, IPC_CREAT | 0744 );
Esta es una funcin compleja (y de interfaz poco elegante) para realizar ciertas operaciones con semforos. semid es un identificador de semforo (devuelto previamente por semget) y semnum, el semforo del conjunto sobre el que quieren trabajar. cmd es la operacin aplicada; a continuacin puede aparecer un parmetro opcional segn la operacin definida por cmd. Las operaciones que les interesan a ustedes son
GETVAL semctl retorna el valor actual del semforo SETVAL se modifica el valor del semforo (un cuarto parmetro entero da el nuevo
valor)
IPC_RMID destruye el semforo
Pgina 22 de 32
Ejemplos:
valor = semctl (semid,semnum,GETVAL); semctl (semid,semnum,SETVAL,nuevo_valor);
Esta funcin realiza atmicamente un conjunto de operaciones sobre semforos, pudiendo bloquear al proceso llamador. semid es el identificador del semforo y sops es un apuntador a un vector de operaciones. nsops indica el nmero de operaciones solicitadas. La estructura sembuf tiene estos campos:
struct sembuf { unsigned short sem_num; // nmero del semforo dentro del conjunto short sem_op; // clase de operacin // segn sea >0, <0 o ==0 short sem_flg; // modificadores de operacin };
Cada elemento de sops es una operacin sobre algn semforo del conjunto de semid. El algoritmo simplificado de la operacin realizada es ste (semval es el valor entero contenido en el semforo donde se aplica la operacin).
si semop<0 si semval >= |semop| semval -= |semop| si semval < |semop| si (semflag & IPC_NOWAIT)!=0 la funcin semop() retorna si no bloquearse hasta que semval >= |semop| semval -= |semop| si semop>0 semval += semop si semop==0 si semval = 0 SKIP si semval != 0 si (semflag & IPC_NOWAIT)!=0 la funcin semop() retorna si no bloquearse hasta que semval == 0
Pgina 23 de 32
Resumiendo un poco, si el campo semop de una operacin es positivo, se incrementa el valor del semforo. Asimismo, si semop es negativo, se decrementa el valor del semforo si el resultado no es negativo. En caso contrario el proceso espera a que se d esa circunstancia. Es decir, semop==1 produce una operacin V y semop==-1, una operacin P.
Ejemplos de uso
Para ilustrar de forma concreta el empleo de semforos bajo UNIX, les mostramos unos ejemplos de subrutinas en C que les pueden servir como modelos para elaborar sus rutinas de sincronizacin en las prcticas de la asignatura. En concreto, son unas funciones que implementan las operaciones P y V de un semforo clsico (inicializacin, incremento y decremento con posible bloqueo del proceso llamador). As definidas, o con pocas modificaciones, les pueden servir como la interfaz para uso de semforos en sus aplicaciones.
#include <sys/types.h> /* para key_t */ /* Crea un semforo con un valor inicial, dada una clave */ /* Devuelve el identificador (vlido o no) del semforo */ int crea_sem ( key_t clave, int valor_inicial ); /* Operaciones P y V sobre un semforo */ void sem_P ( int semid ); void sem_V ( int semid );
/***********************************/ /******** IMPLEMENTACIN *******/ /***********************************/ #include <sys/ipc.h> #include <sys/sem.h> #define PERMISOS 0644 /* crea_sem: abre o crea un semforo */ int crea_sem ( key_t clave, int valor_inicial { int semid = semget( /* Abre o crea clave, */ 1, elemento */ IPC_CREAT|PERMISOS con */ ); if ( semid==-1 ) return -1; /* Da el valor inicial al semforo */ semctl ( semid, 0, SETVAL, valor_inicial ); return semid; } ) un semforo... */ /* con una cierta clave /* /* lo con un solo
crea
(IPC_CREAT) PERMISOS
unos
Pgina 24 de 32
/* abre_sem: Abrir un semforo que otro proceso ya cre */ int abre_sem (key_t clave) { return semget(clave,1,0); }
/* Operaciones P y V */ void sem_P ( int semid ) /* Operacin P */ { struct sembuf op_P [] = { 0, -1, 0 /* Decrementa semval o bloquea si cero */ }; semop ( semid, op_P, 1 ); } void sem_V ( int semid ) /* Operacin V */ { struct sembuf op_V [] = { 0, 1, 0 /* Incrementa en 1 el semforo */ }; semop ( semid, op_V, 1 ); }
Memoria compartida
Memoria compartida o Obtener un sgmento de memoria Sintaxis Descripcin o Vincular un rea de memoria compartida Sintaxis Descripcin o Operaciones de control Sintaxis Descripcin
Pgina 25 de 32
Memoria compartida
Las utilidades de memoria compartida permiten crear segmentos de memoria a los que pueden acceder mltiples procesos, pudiendo definirse restricciones de acceso (slo lectura). Para trabajar con un segmento de memoria compartida, es necesario crear un vnculo (attachment) entre la memoria local del proceso interesado y el segmento compartido. Esto se realiza con la funcin shmat. El proceso que vincula un segmento de memoria compartida cree estar trabajando con ella como si fuera cierta rea de memoria local. Para deshacer el vnculo est la funcin shmdt.
Sintaxis
#include <sys/shm.h> int shmget( key_t key, size_t size, int shmflg);
Descripcin
La funcin shmget retorna el identificador de memoria compartida asociada a key. Un identificador de memoria compartida y la estructura de datos asociada se crearn para key si una de las siguientes condiciones se cumple: - Si key es igual a IPC_PRIVATE. Cuando esto ocurre se crear un nuevo identificador, si existen disponibilidades, este identificador no ser devuelto por posteriores invocaciones a shmget mientras no se libere mediante la funcin shmctl. El identificador creado podr ser utilizado por el proceso invocador y sus descendientes; sin embargo esto no es un requerimiento. El segmento podr ser accedido por cualquier proceso que posea los permisos adecuados. - Si key an no tiene asociado un identificador de memoria compartida y adems shmflg & IPC_CREAT es verdadero. Como consecuencia de la creacin, la estructura de datos asociada al nuevo identificador se inicializa de la siguientes manera: shm_perm.cuid y shm_perm.uid al identificador de usuario efectivo del proceso invocador, shm_perm.gcuid y shm_perm.guid al identificador de grupo efectivo del proceso invocador, los 9 bits menos significativos de shm_perm.mode se inicializan a los 9 bits menos significativos del parmetro shmflg, shm_segsz al valor especificado por size, shm_ctime a la fecha y hora que el sistema posea en el momento de la invocacin y por ltimo se ponen a cero shm_lpid, shm_nattach, shm_atime y shm_dtime.
Pgina 26 de 32
Si la ejecucin se realiza con xito, entonces retornar un valor no negativo denominado identificador de segmento compartido. En caso contrario, retornar -1 y la variable global errno tomar en cdigo del error producido.
Sintaxis
#include <sys/shm.h> char *shmat( int shmid, void *shmaddr, int shmflg ); int shmdt( void *shmaddr )
Descripcin
shmat asocia el segmento de memoria compartida especificado por shmid al segmento
de datos del proceso invocador. Si el segmento de memoria compartida an no haba sido asociado al proceso invocador, entonces shmaddr debe tener el valor de cero y el segmento se asocia a una posicin en memoria seleccionado por el sistema operativo. Dicha localizacin ser la misma en todos los procesos que acceden al objeto de memoria compartida. Si el segmento de memoria compartida ya haba sido asociado por el proceso invocador, shmaddr podr tener un valor distinto de cero, en ese caso deber tomar la direccin asociada actual del segmento referenciado por shmid. Un segmento se asocia en modo slo lectura si shmflg & SHM_RDONLY es verdadero; si no entonces se podr acceder en modo lectura y escritura.. No es posible la asociacin en modo slo escritura. Si la funcin se ejecuta con xito, entonces retornar la direccin de comienzo del segmento compartido, si ocurre un error devolver -1 y la variable global errno tomar el cdigo del error producido.
shmdt desasocia del segmento de datos del proceso invocador el segmento de memoria compartida ubicado en la localizacin de memoria especificada por shmaddr. Si la
funcin se ejecuta sin error, entonces devolver 0, en caso contrario retornar -1 y errno tomar el cdigo del error producido.
Operaciones de control
shmctl - Realiza operaciones de control en una regin de memoria compartida.
Sintaxis
#include <sys/shm.h> int smctl(int shmid, int cmd, struct shmid_ds *buff);
Pgina 27 de 32
Descripcin
La funcin shmctl permite realizar un conjunto de operaciones de control sobre una zona de memoria compartida identificada por shmid. El argumento cmd se usa para codificar la operacin solicitada. Los valores admisibles para este parmetro son:
IPC_STAT: lee la estructura de control asociada a shmid y la deposita en la estructura
estructura de control asociada a shmid tomando los valores de la estructura apuntada por buff.
IPC_RMID: elimina el identificador de memoria compartida especificado por shmid del
sistema, destruyendo el segmento de memoria compartida y las estructuras de control asociadas. Si el segmento est siendo utilizado por ms de un proceso, entonces la clave asociada toma el valor IPC_PRIVATE y el segmento de memoria es eliminado, el segmento desaparecer cuando el ltimo proceso que lo utiliza notifique su desconexin del segmento. Esta operacin slo la podrn utilizar aquellos procesos que posean privilegios de acceso a recurso apropiados, para llevar a cabo esta comprobacin el sistema considerar el identificador de usuario efectivo del proceso y lo comparar con los campos shm_perm_id y shm_perm_cuid de la estructura de control asociada a la regin de memoria compartida.
SHM_LOCK: bloquea la zona de memoria compartida especificada por shmid. Este
comando slo puede ejecutado por procesos con privilegios de acceso apropiados.
SHM_UNLOCK: desbloquea la regin de memoria compartida especificada por shmid. Esta
operacin, al igual que la anterior, slo la podrn ejecutar aquellos procesos con privilegios de acceso apropiados. La funcin shmctl retornar el valor 0 si se ejecuta con xito, o -1 si se produce un error, tomando adems la variable global errno el valor del cdigo del error producido.
Colas de mensajes
Colas de mensajes o Creacin y obtencin de una cola Sintaxis Descripcin o Envo y recepcin de mensajes Envo: sintaxis Envo: descripcin Recepcin: sintaxis Recepcin: descripcin o Operaciones de control (ej. borrar cola) Sintaxis Descripcin
Pgina 28 de 32
Colas de mensajes
Creacin y obtencin de una cola
msgget - Obtiene una cola de mensajes.
Sintaxis
#include <sys/msg.h> int msgget( key_t key, int msgflg);
Descripcin
La funcin msgget se utiliza para solicitar al sistema una cola de mensajes referenciada por el parmetro key. Una cola de mensajes se crear mediante esta funcin si se cumple alguna de las siguientes condiciones: 1. El argumento key toma el valor IPC_PRIVATE. 2. No existe cola de mensajes en el sistema con clave identificadora igual al valor de key y adems se ha especificado el flag IPC_CREAT en el parmetro msgflg. La creacin de una cola de mensajes en el sistema implicar que la estructura de control asociada a la cola se inicialice de la siguiente forma: - msg_perm.cuid, msg_perm.uid, msg_perm.cgid y msg_perm.gid se ponen al valor del identificador de usuario y de grupo efectivo respectivamente del proceso invocador. - Los 9 bits menos significativos de msg_perm.mode se igualan a los 9 bits menos significativos del parmetro msgflg. - msg_qnum, msg_lspid, msg_lrpid, msg_stime y msg_rtime se ponen a 0. - msg_ctime toma el valor de la hora/fecha actual. - msg_qbytes se pone al lmite del sistema. El retorno de la funcin msgget ser un entero no negativo si sta se ejecuta con xito. Dicho valor ser el identificador del sistema asociado a la cola referenciada por key. En caso de error, la funcin retornar -1 y la variable errno tomar el valor del cdigo del error producido.
Pgina 29 de 32
Envo: sintaxis
#include <sys/msg.h> int msgsnd( int msqid, const void *msgp, void *msgp, size_t msgsz, int msgflg);
Envo: descripcin
La funcin msgsnd enva un mensaje a la cola asociada al identificador del sistema referenciado por el parmetro msqid. El argumento msgp es un puntero al comienzo de la zona de memoria de usuario donde est almacenado el mensaje a enviar, el mensaje debe poseer un campo de comienzo del tipo long en el que se indica el tipo del mensaje, el siguiente campo deber contener el mensaje prpiamente dicho, por ejemplo:
long mi_tipo; char texto[];
El campo tipo de mensaje, mi_tipo, se podr utilizar un proceso receptor para seleccionar el tipo de mensajes que desea recibir. El campo del mensaje en s, texto, deber poseer una longitud igual al valor especificado en el parmetro msgsz. El ltimo parmetro de msgsnd, msgflg, especifica la accin a realizar si al menos una de las siguientes condiciones se cumple: 1. En la cola de mensajes ya existe uno del tipo especificado, esto se controla mediante el campo msg_qbytes de la estructura de control asociada a la cola de mensajes referenciada. 2. Se ha alcanzado el nmero mximo de mensajes en las colas del sistema. Si (msgflg & IPC_NOWAIT) es verdadero, entonces el mensaje no se envia y se produce un retorno inmediato. En caso contrario, es decir, si (msgflg & IPC_NOWAIT) es falso, entonces el proceso invocador suspende la ejecucin hasta que alguna de las siguientes condiciones ocurra: 1. La condicin responsable para la suspensin ya no se da. 2. La cola referenciada por msqid es eliminada del sistema. Cuando esto ocurre msgsnd retorna con -1 y la variable global errno toma el valor EIDRM. 3. El proceso invocador recibe una seal del sistema. Cuando esto ocurre, el proceso continua su ejecucin tal y como establece el tratamiento de seales (ver funcin signal).
Pgina 30 de 32
Una vez finalizada con xito la ejecucin de la funcin msgsnd, se actualizan los siguientes campos de control: - msg_qnum se incrementa en 1. - msg_lspid se iguala al identificador del proceso invocador. - msg_stime toma el valor de la fecha/hora actual.
Recepcin: sintaxis
#include <sys/msg.h> int msgrcv(( int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
Recepcin: descripcin
La funcin msgrcv toma un mensaje de la cola especificada por el parmetro msqid y lo copia en la zona de memoria apuntada por msgp. El mensaje recibido tiene una estrura anloga a la del mensaje enviado por emisor. El argumento msgtyp se utiliza para especificar el tipo de mensaje a recibir, concretamente: - Si msgtyp es igual a 0, entonces se tomar el primer mensaje recibido en la cola. - Si msgtyp es mayor o igual que 1, entonces el primer mensaje del tipo igual al valor especificado se tomar de la cola. - Si msgtyp es negativo, entonces el primer mensaje cuyo tipo sea menor o igual que el valor absoluto de msgtyp se tomar de la cola. El parmetro msgflg especifica la accin que se realizar en el caso que no exista en la cola mensaje alguno del tipo requerido, especficamente: - Si (msgflg & IPC_NOWAIT) es verdadero, entonces el proceso invocador retornar inmediatamente con un valor de -1 y la variable errno tomar el valor ENOMSG. - Si (msgflg & IPC_NOWAIT) es falso, entonces el proceso invocador suspender la ejecucin hasta que se cumpla alguna de las siguientes condiciones: - Se recibe un mensaje del tipo deseado. - La cola de mensajes es eliminada del sistema, en este caso se retornar -1 y la variable errno tomar el valor EIDRM. - Se produce una seal a tratar por el proceso invocador, en este caso se continuar la ejecucin de la forma definida en el tratamiento de seales. Una vez ejecutada con xito la funcin msgrcv, alguno campos de control asociados a la cola se actualizan de la siguiente manera:
Gua de Programacin en C y UNIX Pgina 31 de 32
- msg_qnum se decrementa en 1. - msg_lrpid se pone al valor del identificador del proceso invocador. - msg_setime se actualiza actualiza a la fecha/hora actual.
Sintaxis
#define <sys/msg.h> int msgctl(int msqid, int cmd, struct msqid_ds *buf);
Descripcin
La funcin msgctl permite realizar operaciones de control en la cola de mensajes especificada por el parmetro msqid. La operacin se especifica mediante el argumento cmd, cuyos valores posibles son: - IPC_STAT: lee la estructura de control asociada a la cola y los deposita en la estructura apuntada por buf. - IPC_SET: actualiza los campos de control siguientes: msg_perm.uid, msg_perm.gid, msg_perm.mode (slo los 9 bits menos significativos) y msg_qbytes a los valores que poseen los campos homnimos en la estructura apuntada por buf. Para poder realizar esta operacin el proceso invocador debe poseer un identificador igual al usuario "root" o bien coincidir con el valor almacenado en el campo msg_perm.uid o en el campo msg_perm.cuid de la estructura asociada a la cola de mensajes. - IPC_RMID: elimina la cola de menajes identificada por msqid. Esta orden slo puede ejecutarla un proceso con identificador de usuario igual al "root" , o igual al campo msg_perm.uid o al campo msg_perm.cuid. Si la funcin msgctl se ejecuta con xito, entonces retornar 0. En caso contrario retornar -1 y la variable global errno tomar un valor igual al cdigo del error producido.
Pgina 32 de 32