SEMAPH.C

/*

* Provee un interface mas sencillo de entender que las llamadas a sistema

* de semaforos de System V. Hay 7 rutinas disponibles:

*

* id = semCreate(key, initval); # Crear con un valor inicial o abrir.

* id = semOpen(key); # Abrir (debe existir ya)

* semWait(id); # espera = P = down en 1

* semSignal(id); # senal = V = up en 1

* semOp(id, cantidad); # espera si (cantidad < 0)

* # senal si (cantidad > 0)

* semClose(id); # cierra

* semRm(id); # destruye (borra)

*

* Se disegna un semaforo soportado por un conjunto de tres, dos de ellos

* auxiliares. (Los semaforos se crean por arrays)

* - El primer miembro, [0], es el valor real del semaforo.

* - El segundo miembro, [1], es un contador utilizado para conocer

* si todos los procesos han acabado con el semaforo. El contador

* se inicializa con un numero grande (BIGCOUNT) y se decrementa cada

* vez que se crea o abre, y se incrementa en cada cierre.

*

* De esta forma se puede "ajustar" la caracteristica de System V

* de forma que se tenga en cuenta cualquier proceso que salga

* sin llamar a semClose(). A pesar de ello, no ahuda mucho si el

* ultimo proceso sale sin cerrar el semaforo, ya que no hay forma

* de destruir el semaforo, pero puede ayudar si acaba (intencional

* o no intencionalmente) cualquier otro proceso diferente del ultimo.

* - El tercer miembro, [2], del conjunto de semaforos se utiliza para

* bloquear las secciones criticas en semCreate() y semClose().

*/

#include <stdio.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

#include <errno.h>

void semOp(int, int);

int semCreate(key_t, int);

int semOpen(key_t);

void semRm(int);

void semClose(int);

void semWait(int);

void semSignal(int);

#define BIGCOUNT 10000 /* Valor inicial para el contador de procesos */

/* Define los arrays de operaciones del semaforo para llamadas a

* semop().

*/

static struct sembuf op_lock[2] = {

2, 0, 0, /* espera para [2] (bloqueo) sea igual 0

* entonces incrementa [2] en 1 - esto lo bloquea */

2, 1, SEM_UNDO /* UNDO para liberar el bloqueo si el proceso sale

* antes de desbloquear explicitamente */

};

static struct sembuf op_endcreate[2] = {

1, -1, SEM_UNDO, /* decrementa [1] (contador de procesos) con undo en

* caso de finalizar */

/* UNDO para ajustar el contador de procesos en caso de

* acabar antes de llamar explicitamente a semClose() */

2, -1, SEM_UNDO /* entonces decrementa [2] (bloqueo) de vuelta a 0 */

};

static struct sembuf op_open[1] = {

1, -1, SEM_UNDO /* decrementa [1] (contador de proceso) con undo en

* caso de finalizar */

};

static struct sembuf op_close[3] = {

2, 0, 0, /* espera hasta que [2] (bloqueo) sea igual a 0 */

2, 1, SEM_UNDO, /* entonces incrementa [2] en 1 - esto lo bloquea */

1, 1, SEM_UNDO /* entonces incrementa [1] (contador de procesos) */

};

static struct sembuf op_unlock[1] = {

2, -1, SEM_UNDO /* decrementa [2] (bloqueo) de vuelta a 0 */

};

static struct sembuf op_op[1] = {

0, 99, SEM_UNDO /* decrementa o incrementa [0] con undo en caso de

* finalizar */

/* El 99 se substituye con la cantidad real que hay

* que substraer (positiva o negativa) */

};

/****************************************************************************

* Crea un semaforo con un valor inicial especificado.

* Si el semaforo existe, no se inicializa (por su puesto).

* Se devuelve la identidad del semaforo si todo va bien, si no -1.

*/

int semCreate(key_t key, int initval) {

register int id, semval;

union semun {

int val;

struct semid_ds *buf;

ushort *array;

} semctl_arg;

if (key == IPC_PRIVATE)

return(-1); /* no utilizable para semaforos privados */

else if (key == (key_t) -1)

return(-1); /* probablemente una llamada erronea anterior a ftok() */

deNuevo:

if ( (id = semget(key, 3, 0666 | IPC_CREAT)) < 0)

return(-1); /* problemas de permisos o tablas llenas */

/* Cuando se crea el semaforo, sabemos que el valor de todos los

* miembros es 0.

*

* Bloquear el semaforo esperando a que [2] sea 0, e incrementarlo.

*

* Hay una condicion de carrera: Cabe la posibilidad de que entre el

* semget() de arriba y el semop() de abajo, otro proceso pueda llamar

* a semClose() que puede borrar el semaforo si el ultimo lo esta

* usando.

*

* Ademas, se maneja la condicion de error sobre el identificador.

* Si esto ocurre, se vuelve atras y se intenta crear de nuevo.

*/

if (semop(id, &op_lock[0], 2) < 0) {

if (errno == EINVAL)

goto deNuevo;

fprintf(stderr, "semCreate: no puedo bloquear\n");

}

/* Obtener el valor del contador de procesos. Si es igual a 0,

* entonces ninguno ha inicializado el semaforo aun.

*/

if ( (semval = semctl(id, 1, GETVAL, 0)) < 0)

fprintf(stderr, "semCreate: no puedo realizar GETVAL\n");

if (semval == 0) {

/* Podriamos inicializar mediante SETALL, pero podria borrar el

* ajuste del valor que se realizo cuando se bloqueo el semaforo antes.

* En su lugar, se hacen dos llamadas al sistema para inicializar

* [0] y [1].

*/

semctl_arg.val = initval;

if (semctl(id, 0, SETVAL, semctl_arg) < 0)

fprintf(stderr, "semCreate: puedo SETVAL[0]\n");

semctl_arg.val = BIGCOUNT;

if (semctl(id, 1, SETVAL, semctl_arg) < 0)

fprintf(stderr, "semCreate: puedo SETVAL[1]\n");

}

/* Decrementar el contador de procesos y desbloquear.

*/

if (semop(id, &op_endcreate[0], 2) < 0)

fprintf(stderr, "semCreate: no puedo acabar semCreate()\n");

return(id);

}

/****************************************************************************

* Abre un semaforo que debe existir ya.

* Esta funcion deberia de usarse, en vez de semCreate(), si en la llamada

* se sabe que el semaforo deberia ya existir. Por ejemplo un cliente

* de un par cliente-servidor podria utilizarla, si es responsabilidad del

* servidor crear el semaforo.

* Se vuelve la identidad del semaforo si va bien, si no -1.

*/

int semOpen(key_t key) {

register int id;

if (key == IPC_PRIVATE)

return(-1); /* no utilizable para semaforos privados */

else if (key == (key_t) -1)

return(-1); /* probablemente una llamada erronea anterior a ftok() */

if ( (id = semget(key, 3, 0)) < 0)

return(-1); /* no existe o las tablas estan llenas */

/* Decrementa el contador de procesos. No necesitamos un bloqueo

* para hacer esto.

*/

if (semop(id, &op_open[0], 1) < 0)

fprintf(stderr, "semOpen: no puedo abrir\n");

return(id);

}

/****************************************************************************

* Borrar un semaforo.

* Se supone que esta llamada se realiza desde un servidor en operaciones como

* apagarServidor ... No importa si los otros procesos estan usandolo o no.

* El resto de los procesos deberian emplear semClose().

*/

void semRm(int id)

{

if (semctl(id, 0, IPC_RMID, 0) < 0)

fprintf(stderr, "semRm: no puedo borrar semaforo (IPC_RMID)\n");

}

/****************************************************************************

* Cerrar el semaforo.

* Funcion por proceso que decrementa el numero de procesos activos en el

* semaforo. Se emplea al salir. Si el proceso es el ultimo destruye el

* semaforo.

*/

void semClose(int id) {

register int semval;

/* En primer lugar bloquear el recurso semaforo e incrementar el contador

* de procesos [1].

*/

if (semop(id, &op_close[0], 3) < 0)

fprintf(stderr, "semClose: no puedo bloquer en semClosep\n");

/* Comprobar si el valor leido es la ultima referencia al semaforo.

*/

if ( (semval = semctl(id, 1, GETVAL, 0)) < 0)

fprintf(stderr, "semClose: no puedo realizar GETVAL\n");

if (semval > BIGCOUNT)

fprintf(stderr, "<<sem[1] > BIGCOUNT>>\n");

else if (semval == BIGCOUNT)

semRm(id);

else

if (semop(id, &op_unlock[0], 1) < 0)

fprintf(stderr, "semClose: no puedo desbloquear\n"); /* desbloqueo */

}

/****************************************************************************

* Espera hasta que el valor del semaforo sea mayor que 0, entonces

* decrementa en 1 y vuelve. Operador wait, DOWN (Tanenbaum) o P (Dijkstra).

*/

void semWait(int id) {

semOp(id, -1);

}

/****************************************************************************

* Incrementar el semaforo en 1. Operador segnal, UP (Tanenbaum) o

* V (Dijkstra).

*/

void semSignal(int id) {

semOp(id, 1);

}

/****************************************************************************

* Operacion generica de semaforo:

* incrementar o decrementar cierta cantidad positiva o negativa, distinta

* de cero.

*/

void semOp(int id, int value) {

if ( (op_op[0].sem_op = value) == 0)

(void) fprintf(stderr, "semOp: 'valor' no puede ser 0\n");

if (semop(id, &op_op[0], 1) < 0)

(void) fprintf(stderr, "semOp: error\n");

}

Volver a la documentación