You are on page 1of 11

UNIX Semforos POSIX.

Utilizaremos dos tipos de semforos POSIX: los semforos binarios y los semforos generales.

Semforos POSIX binarios.


Los semforos binarios slo pueden estar en uno de dos estados posibles. Se declaran como variables de tipo pthread_mutex_t y las funciones que los manejan son: pthread_mutex_lock() Operacin P

pthread_mutex_destroy() Libera el semforo pthread_mutex_unlock() Operacin V pthread_mutex_init() Inicializacin

Si son declarados como variables externas, se pueden inicializar de forma esttica sin necesidad de invocar a pthread_mutex_init(). Por ejemplo:

// // Semaforo binario pthread_mutex_t buffer_lock = PTHREAD_MUTEX_INITIALIZER; Como las funciones lock() y unlock() modifican el semforo recibirn como argumento de entrada su direccin; por ejemplo:

pthread_mutex_lock(&buffer_lock); *itemp = buffer[bufout]; bufout = (bufout + 1) % TAMBUF; pthread_mutex_unlock(&buffer_lock); La funcin pthread_mutex_destroy() libera la memoria asociada a dicho semforo. Normalmente no existe tal estructura de memoria y en la mayor parte de los sistemas esta primitiva no hace nada. Puede obviarse.

Semforos POSIX genricos.


Se trata de semforos diseados para sincronizar hilos. El estndar tiene en cuenta la posibilidad de su utilizacin para la sincronizacin de procesos (adems de hilos) pero esta posibilidad no est soportada en todas las implementaciones y por ello, en esta asignatura, slo sern utilizados entre hilos de un mismo proceso. Se declaran como variables de tipo sem_t. Se manejan con las siguientes funciones:

int sem_init(sem_t *sem, int pshared, unsigned int value); int sem_wait(sem_t * sem); int sem_post(sem_t * sem); int sem_destroy(sem_t * sem); La funcin de inicializacin recibe como segundo argumento un valor indicativo de si el semforo sincronizar hilos del mismo o diferente proceso. Por defecto se pone un valor 0 a dicho valor ya que slo sern usados dentro del mismo proceso. Anlogamente al semforo binario, la funcin sem_destroy() puede obviarse. Veamos cmo se implementa un sistema productor-consumidor con bfer circular utilizando hilos, semforos POSIX binarios y semforos POSIX genricos:

Ejercicio completo
#include <stdio.h> #include <string.h> #include <errno.h> #include <semaphore.h> #include <pthread.h> #define TAMBUF 8 // Tamao del bfer circular

#define NUMDATOS 100 // Nmero de datos a enviar // // El buffer circular y los correspondientes punteros int buffer[TAMBUF]; int bufin = 0;

int bufout = 0; // // Semaforo binario pthread_mutex_t buffer_lock = PTHREAD_MUTEX_INITIALIZER; // // Variable suma unsigned long sum = 0; // // Semaforos generales sem_t hay_datos; sem_t hay_sitio; // // Funciones de escritura y lectura del buffer circular void obten_dato(int *itemp) { pthread_mutex_lock(&buffer_lock); *itemp = buffer[bufout]; bufout = (bufout + 1) % TAMBUF; pthread_mutex_unlock(&buffer_lock); return; } void pon_dato(int item) { pthread_mutex_lock(&buffer_lock); buffer[bufin] = item;

bufin = (bufin + 1) % TAMBUF; pthread_mutex_unlock(&buffer_lock); return; } // // Funciones productor-consumidor void *productor(void *arg1) { int i; for (i = 1; i <= NUMDATOS; i++) { sem_wait(&hay_sitio); pon_dato(i*i); sem_post(&hay_datos); } pthread_exit( NULL ); } void *consumidor(void *arg2) { int i, midato; for (i = 1; i<= NUMDATOS; i++) { sem_wait(&hay_datos); obten_dato(&midato); sem_post(&hay_sitio); sum += midato; }

pthread_exit( NULL ); } // // Funcion principal main() { pthread_t tidprod, tidcons; unsigned long i, total; total = 0; for (i = 1; i <= NUMDATOS; i++) total += i*i; printf("El resultado deberia ser %u\n", total); // // Inicializacion de semaforos sem_init(&hay_datos, 0, 0); sem_init(&hay_sitio, 0, TAMBUF); // // Se crean los hilos pthread_create(&tidprod, NULL, productor, NULL); pthread_create(&tidcons, NULL, consumidor, NULL); // // Se espera a que los hilos terminen pthread_join(tidprod, NULL); pthread_join(tidcons, NULL); printf("Los hilos produjeron el valor %u\n", sum);

EJERCICIO #2
Para todo programa en C para UNIX se hace uso de operaciones con semforos debe incluirse los siguientes ficheros de cabecera:

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

La creacin e inicializacin de un semforo se puede llevar a cabo con la siguiente funcin:

inicia (valor) int valor; { int semval; int id; union semun { int val; struct semid_ds *buf; ushort *array; } arg; if ((id=semget(IPC_PRIVATE, 1, (IPC_CREAT|0666))) == -1) { perror("Error al crear el semforo."); return(-1); } arg.val = valor; if (semctl(id, 0, SETVAL, arg) == -1) { perror("Error al inicializar el semforo."); return (-1); /*error en inicializacion*/ } return(id); }

Las operaciones elementales sobre semforos (esperar y sealar) se pueden implantar con el siguiente cdigo:

/*Rutina P */ P (semaforo)

int semaforo; { if ( semcall(semaforo, -1) == -1 ) perror("Error en operacin P."); }

/*Rutina V */ V (semaforo) int semaforo; { if ( semcall(semaforo, 1) == -1 ) perror("Error en operacin V."); }

semcall (semaforo, operacion) int semaforo, operacion; { struct sembuf sb; sb.sem_num = 0; sb.sem_op = operacion; sb.sem_flg = 0; return ( semop(semaforo, &sb, 1) ); /*devuelve -1 si error */ }

Todo recurso de un sistema informtico que ya no se va a necesitar ha de ser liberado. Si se trata de semforos UNIX una posibilidad para hacer esto es:

borra_s (semaforo) int semaforo; { if ( semctl(semaforo, 0, IPC_RMID, 0) == -1) { perror("Error al eliminar el semforo."); return(-1); } }

Ejercicio completo
#include #include #include #include #include #include #include <stdio.h> <sys/types.h> <sys/ipc.h> <sys/sem.h> <sys/time.h> <unistd.h> <errno.h>

#define ESPERA 1000 // Son los microsegundos de espera usados para asegurar // la finalizacin del quantum. /********************************************************************** * * PROBAR EL SISTEMA CON Y SIN LAS OPERACIONES P Y V * **********************************************************************/ main() { int pid; /* identifica el proceso hijo */ int mutex; /* semaforo binario */ mutex=inicia(1); if (0==(pid=fork())) proceso_hijo(mutex); else proceso_padre(mutex); borra_s(mutex); } /********************************************************************** * * Tanto el proceso hijo como el padre escriben 30 secuencias de 80 caracteres * **********************************************************************/ proceso_hijo(critica) int critica; { /* escribe 30 ristras de 80 caracteres '+' */ int i,j; for (i=0;i< 30; i++) { P(critica); for (j=0; j<80; j++) {

printf("+"); fflush(stdout); // Provocamos la finalizacin del quantum de tiempo retardo (); } printf("\n"); V(critica); } exit(); }

proceso_padre(critica) int critica; { /* escribe 30 ristras de 80 caracteres '-' */ int i,j; for (i=0;i< 30; i++) { P(critica); for (j=0; j<80; j++) { printf("-"); fflush (stdout); // Provocamos la finalizacin del quantum de tiempo retardo (); } printf("\n"); V(critica); } wait(0); /* espera a que finalice el hijo */ } /********************************************************************** * * Provocamos la espera durante ESPERA microsegundos * **********************************************************************/ retardo() { struct timeval tiempo; struct timezone tz; unsigned long inicio, ahora; gettimeofday(&tiempo, &tz); ahora = inicio = tiempo.tv_sec * 1000000 + tiempo.tv_usec; // ESPERA microsegs while (ahora < inicio + ESPERA) { gettimeofday(&tiempo, &tz); ahora = tiempo.tv_sec * 1000000 + tiempo.tv_usec; }

} /*********************************************************************** * * Rutinas de manejo de semforos * ***********************************************************************/ inicia(valor) int valor; { int semval; int id; union semun { int val; struct semid_ds *buf; ushort *array; } arg; if ((id=semget(IPC_PRIVATE, 1, (IPC_CREAT|0666))) == -1) { perror("Error al crear el semforo."); return(-1); } arg.val = valor; if (semctl(id, 0, SETVAL, arg) == -1) { perror("Error al inicializar el semforo."); return (-1); /*error en inicializacion*/ } return(id); } /*Rutina P */ P(semaforo) int semaforo; { if ( semcall(semaforo, -1) == -1 ) perror("Error en operacin P."); } /*Rutina V */ V(semaforo) int semaforo; { if ( semcall(semaforo, 1) == -1 ) perror("Error en operacin V."); } semcall(semaforo, operacion) int semaforo, operacion; { struct sembuf sb; sb.sem_num = 0;

sb.sem_op = operacion; sb.sem_flg = 0; return ( semop(semaforo, &sb, 1) ); /*devuelve -1 si error */ } borra_s(semaforo) int semaforo; { if ( semctl(semaforo, 0, IPC_RMID, 0) == -1) { perror("Error al eliminar el semforo."); return(-1); } }