/* * 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 #include #include #include #include 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, "< 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"); }