Академический Документы
Профессиональный Документы
Культура Документы
Utilizaremos dos tipos de semforos POSIX: los semforos binarios y los semforos generales.
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.
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:
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)
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); } }