CONCEPTO DE SEMAFORO

Un semáforo es una estructura diseñada para sincronizar dos o más threads o procesos, de modo que su ejecución se realice de forma ordenada y sin conflictos entre ellos.

El por qué no se pueden usar directamente otras estructuras mas clásicas, como por ejemplo usar una variable común para decidir si se puede o no acceder a un recurso, se debe a que estamos en un sistema multitarea: hacer esto implicaría realizar una espera activa (un bucle, comprobando constantemente si la variable está o no a 0, y así saber si podemos seguir ejecutando o no). Por otro lado, puede ocurrir algo mucho peor: supongamos que un proceso comprueba la variable, y ve que el recurso está libre, por lo que procedería a cambiar dicha variable de valor y seguir. Pues bien, si justo después de la comprobacion pero antes de que cambie el valor se conmuta de tarea (puede pasar, pues el sistema operativo puede hacerlo en cualquier momento), y el nuevo proceso comprueba la variable, como todavía no se ha actualizado, creerá que el recurso está libre, e intentará tomarlo, haciendo que ambos programas fallen. Lo peor del caso es que se tratará de un error aleatorio: unas veces fallará (cuando se produzca cambio de tarea en ese punto) y otras no.

Para evitarlo, se idearon los semáforos. Un semáforo básico es una estructura formada por una posición de memoria y dos instrucciones, una para reservarlo y otra para liberarlo. A esto se le puede añadir una cola de threads para recordar el orden en que se hicieron las peticiones.

Se empieza por inicializar la posición de memoria a 1 (o al valor correspondiente si ese recurso concreto admite más de un acceso simultáneo). Esto se hace en el inicio del programa principal.

A continuación, cada vez que un thread o un proceso quiera acceder a dicho recurso (por ejemplo, un fichero), hará primero una petición con la primera de las llamadas disponibles. Cuando el S.O. ejecuta esa llamada, comprueba el valor que hay en la posición de memoria del semáforo, y si es distinta de cero, se limita a restarle 1 y devolver el control al programa; sin embargo, si ya es cero, duerme al proceso que hizo la petición y lo mete en la cola de procesos, en espera de que el semáforo se ponga a un valor distinto de cero.

Por último, cuando el proceso ha terminado el acceso al recurso, usa la segunda llamada para liberar el semáforo. Cuando el S.O. la ejecuta, comprueba si la cola del semáforo está vacia, en cuyo caso se limita a incrementar el valor del semáforo, mientras que si tiene algún proceso, lo despierta, de modo que vuelve a recibir ciclos de CPU y sigue su ejecución. Si había varios procesos en espera, se irán poniendo en marcha uno tras otro a medida que el anterior va liberando el semáforo. Cuando termina el último, el semáforo se vuelve a poner a 1. Se trata, por tanto, del mismo proceso que seguiríamos con la variable, pero con la ventaja de que es un mecanismo estandar para todos los procesos, y como es una operacion atómica (esto es, que durante su ejecución no se admiten cambios de tarea), no surje el problema de que una conmutación pueda producir errores aleatorios.

Vemos que la primera vez que un proceso usa el semáforo, este tiene valor 1, por lo que pasa a cero y el proceso puede acceder al recurso. Si durante ese tiempo otro proceso quiere acceder también, al usar el semáforo, este tiene valor cero, por lo que el S.O. deja de darle ciclos de CPU. Cuando el primer proceso ha terminado, libera el recurso, con lo que el S.O. puede comprobar que el segundo proceso está esperando, por lo que le vuelve a dar ciclos. En este punto, el proceso sigue como si nunca hubiese sido detenido. Este tipo de semáforos son los de Exclusión mútua, o Mutex.

DosCreateMutexSem
DosOpenMutexSem
DosCloseMutexSem
DosQueryMutexSem
DosReleaseMutexSem
DosRequestMutexSem

Otra utilización de los semáforos es cuando uno o más procesos tienen que esperar a que otro halla terminado una tarea. Para ello, el primer proceso borra el semáforo y con una primitiva adecuada se pone a esperar a que el semáforo se active (posted). Mientras, el segundo proceso va trabajando, y cuando termina lo que tiene que hacer, activa el semáforo, con lo que el primer proceso vuelve a ponerse en marcha, sin haber desperdiciado ciclos de CPU. Son semáforos evento. Vemos que puede haber varios procesos esperando por el mismo semáforo, y el thread que lo activa no tiene por qué saber cuantos son. Cobran su importáncia cuando el evento no es producido por otro thread, sino por otras funciones del S.O., como las de sincronización, que veremos más adelante.

DosCreateEventSem
DosOpenEventSem
DosCloseEventSem
DosPostEventSem
DosQueryEventSem
DosResetEventSem
DosWaitEventSem

Los semáforos se identifican con un nombre, el cual tiene la forma \SEM32\un_nombre. Un_nombre es el identificador del semáforo. Existe la posibilidad de crear un semáforo sin nombre, en cuyo caso se puede especificar si es un semáforo compartido o no (por otros procesos).

Pagina anterior  Indice  Pagina siguiente