2.2.2. Utilización de los semáforos
Texto íntegro de la conversión. Las figuras y la disposición de las notas se pueden consultar en el PDF.
2.2.2. Utilización de los semáforos
A continuación, presentamos tres posibles usos de los semáforos: exclusión mutua, contador de recursos y sincronización.
Acceso a la sección crítica en exclusión mutua
Para asegurar el acceso a la sección crítica en exclusión mutua, hay que utilizar los semáforos binarios o n-arios de la manera siguiente:
-
Se inicializa el semáforo. El valor inicial del semáforo ha de ser 1 para indicar que no hay ningún proceso dentro de la sección crítica y dejar paso al primer proceso que pida permiso para entrar mediante la operación sem_wait.
-
Se ejecuta la operación sem_wait. El proceso que quiere acceder a una zona compartida en exclusión mutua ejecuta la función sem_wait sobre el semáforo asociado a la sección.
-
Se ejecuta la operación sem_signal. El proceso que ha finalizado la ejecución de la zona crítica libera la exclusión y permite el paso a otros procesos.
Como ejemplo, veamos cuál sería el código de los procesos usuario y del proceso gestor de la impresora en caso de que utilicen semáforos para actualizar la variable compartida trabajo_pendiente.
En un principio, el sistema inicializa la variable semáforo exclusion, utilizando el código de la figura 13. Consideremos el código de un proceso usuario (figura 14) y el código del proceso gestor de la impresora (figura 15).
semaphore exclusion;
... sem_init(exclusion, 1);
Figura 13. Definición de la variable de tipo semáforo e inicialización
... /Generar fichero/ ...
sem_wait(exclusion); trabajo_pendiente = trabajo_pendiente + 1; sem_signal(exclusion);
...
Figura 14. Código de los procesos usuario
...
while (cierto){ /* Espera activa */
Página 18
while (trabajo_pendiente == 0); sem_wait(exclusion);
trabajo_pendiente = trabajo_pendiente - 1; sem_signal(exclusion); /Enviar trabajo a la impresora/
... }
Figura 15. Código del proceso gestor
Para ilustrar el comportamiento y el funcionamiento de los semáforos, mostramos a continuación un posible escenario de ejecución de tres procesos usuario (P1, P2, P3) y el proceso gestor (G1). Las diferentes columnas indican qué acciones llevan a cabo cada uno de los procesos en cada unidad de tiempo, qué procesos están esperando el semáforo, qué proceso está dentro de la sección crítica y cuál es el valor del semáforo en cada instante concreto (figura 16).
Supondremos que el semáforo, exclusion, se ha inicializado en 1 y que los procesos usuario y el proceso gestor están dentro de un bucle infinito. Los procesos usuario generan continuamente trabajos que se envían a la impresora. El proceso gestor imprime continuamente los trabajos que han generado los procesos usuario.
En la tabla se ve que en el tiempo T0 se inicializa el semáforo y todavía no se ha creado ningún proceso. No hay ningún trabajo pendiente de ser imprimido. En T1, el semáforo exclusion ha decrementado su valor hasta 0. Esto indica que un proceso ha recibido el permiso para entrar en la sección crítica. En este caso, ha sido el proceso usuario P1. El resto de los procesos, el gestor y los usuarios están intentando entrar en la sección, pero el mecanismo del semáforo asegura que ningún otro proceso pueda entrar hasta que P1 lo libere mediante la ejecución de la operación sem_signal(exclusion).
Figura 16. Evolución del estado de los tres procesos usuario, del proceso gestor y del semáforo
Supongamos que los tres procesos usuario generan los ficheros correspondientes y, tal como se ha indicado en el código del proceso usuario, a continuación examinan el valor del semáforo exclusion con la intención de decre-
Notas y pies de figura de la fuente
(9)Con esta utilización de los semáforos, el orden de acceso de los procesos a la sección crítica es totalmente aleatorio.
Página 19
mentarle el valor y entrar en la sección crítica (modificar la variable). Uno de los tres tiene éxito, aunque no podemos asegurar cuál acabará entrando9 en la sección crítica. En el ejemplo, la operación sem_wait que ejecuta P1, después de haber leído el valor del semáforo a 1, debe apropiarse la variable semáforo y evitar que el resto de los procesos concurrentes (P2 y P3) puedan acceder a ella antes de que sea modificada (decrementada a 0). Éste es el motivo por el cual el código de la operación sem_wait debe ser indivisible. Más adelante veremos cómo podemos conseguirlo.
Una vez dentro de la sección crítica, el proceso P1 actualiza la variable. A continuación, libera la sección crítica ejecutando la operación sem_signal(exclusion). En este instante, P2, P3 y el proceso gestor de la impresora tienen la misma probabilidad de entrar en la sección crítica. En el ejemplo, es el proceso P3 el que lo consigue. Se vuelven a repetir las mismas operaciones y cuando P3 libera la sección entra de nuevo P1, mientras que los procesos P2 y el proceso gestor de la impresora continúan esperándose. Esta situación se conoce como inanición y no es deseable; más adelante, al hablar de la implementación de los semáforos, veremos cómo se puede evitar.
Observación: la granularidad de un semáforo indica el número de zonas críticas a las que está asociado. Distinguimos dos grados de granularidad:
-
Granularidad baja: implica tener semáforos diferentes para zonas críticas diferentes. Esto aumenta el grado de concurrencia entre los diferentes procesos concurrentes. En general, interesa tener este grado de granularidad.
-
Granularidad alta: es cuando se utiliza un mismo semáforo para asegurar la exclusión mutua de diferentes zonas críticas (independientes entre ellas).
Contador de recursos disponibles
Muchas aplicaciones deben controlar la cantidad de recursos disponibles de un determinado tipo pero no necesitan saber exactamente cuántos recursos disponibles hay, sólo necesitan saber si hay alguno; si no hay ninguno, han de esperar a que haya alguno libre.
Es posible utilizar semáforos n-arios en este entorno. El contador asociado al semáforo reflejará el número de recursos disponibles.
- Se inicializa el semáforo. El valor inicial del semáforo debe ser el número inicial de recursos disponibles (será un número entero mayor o igual que cero).
Página 20
-
Se ejecuta la operación sem_wait. Cuando un proceso necesite un recurso, invocará esta operación. Si hay algún recurso disponible, se decrementará el contador asociado al semáforo; de lo contrario, se esperará hasta que haya algún recurso disponible.
-
Se ejecuta la operación sem_signal. El proceso que ha finalizado en la utilización de un recurso incrementa el contador y permite que otros procesos lo utilicen.
El ejemplo anterior de los procesos usuario y el proceso gestor de la impresora se podría haber resuelto utilizando un semáforo n-ario y aprovechando el contador del semáforo para acumular el número de trabajos pendientes (figura 17). El proceso usuario incrementará este contador haciendo sem_signal (figura 18) y el proceso gestor esperará la aparición de trabajos efectuando sem_wait (figura 19). El contador asociado al semáforo indicará el número de trabajos pendientes.
semaphore trabajos_pendientes; ...
sem_init(trabajos_pendientes, 0);
Figura 17. Definición de la variable de tipo semáforo e inicialización
...
while (cierto){ /Generar fichero/ ...
sem_signal(trabajos_pendientes); }
Figura 18. Código de los procesos usuario
... while (cierto){ sem_wait(trabajos_pendientes);
/Enviar trabajo a la impresora/ ... }
Figura 19. Código del proceso gestor de la impresora
En el apartado 2 del anexo, tenéis un ejemplo más completo de la utilización de este tipo de semáforos. Fijaos que en la figura 19 el gestor se bloquea si no hay trabajos pendientes. En cambio, el código de la figura 15 hacía una espera activa.
Página 21
Sincronización entre procesos
Un semáforo binario o n-ario puede implementar una sincronización entre dos procesos en la que un proceso espera una autorización por parte del otro con el fin de poder continuar su ejecución.
-
Se inicializa el semáforo. El valor inicial del semáforo debe ser 0.
-
Se ejecuta la operación sem_wait. El proceso que espera la autorización por parte del otro proceso debe invocar esta operación.
-
Se ejecuta la operación sem_signal. El proceso que debe conceder la autorización ha de invocar esta operación.
El proceso que espera la autorización (figura 21) ejecutará el código posterior al sem_wait únicamente cuando el proceso que autoriza (figura 22) haya ejecutado el sem_signal.
semaphore sync;
... sem_init(sync, 0);
Figura 20. Definición de la variable de tipo semáforo e inicialización
... sem_wait(sync); ...
Figura 21. Código del proceso que espera autorización
... sem_signal(sync);
...
Figura 22. Código del proceso que autoriza
Ver fragmento extraído sin normalizar
2.2.2. Utilización de los semáforos
A continuación, presentamos tres posibles usos de los semáforos: exclusión
mutua, contador de recursos y sincronización.
Acceso a la sección crítica en exclusión mutua
Para asegurar el acceso a la sección crítica en exclusión mutua, hay que utilizar
los semáforos binarios o n-arios de la manera siguiente:
1)Seinicializaelsemáforo. El valor inicial del semáforo ha de ser 1 para indi-
car que no hay ningún proceso dentro de la sección crítica y dejar paso al pri-
mer proceso que pida permiso para entrar mediante la operación sem_wait.
2)Seejecutalaoperaciónsem_wait. El proceso que quiere acceder a una
zona compartida en exclusión mutua ejecuta la función sem_wait sobre el
semáforo asociado a la sección.
3)Seejecutalaoperaciónsem_signal. El proceso que ha finalizado la eje-
cución de la zona crítica libera la exclusión y permite el paso a otros procesos.
Como ejemplo, veamos cuál sería el código de los procesos usuario y del pro-
ceso gestor de la impresora en caso de que utilicen semáforos para actualizar
la variable compartida trabajo_pendiente.
En un principio, el sistema inicializa la variable semáforo exclusion, utilizando
el código de la figura 13. Consideremos el códigodeunprocesousuario
(figura 14) y el códigodelprocesogestordelaimpresora (figura 15).
semaphore exclusion;
...
sem_init(exclusion, 1);
Figura 13. Definición de la variable de tipo semáforo e inicialización
...
/*Generar fichero*/
...
sem_wait(exclusion);
trabajo_pendiente = trabajo_pendiente + 1;
sem_signal(exclusion);
...
Figura 14. Código de los procesos usuario
...
while (cierto){
/* Espera activa */
## Página 18
<!-- source-page: 18 -->
GNUFDL • PID_00214803 18 Comunicación y sincronización
while (trabajo_pendiente == 0);
sem_wait(exclusion);
trabajo_pendiente = trabajo_pendiente - 1;
sem_signal(exclusion);
/*Enviar trabajo a la impresora*/
...
}
Figura 15. Código del proceso gestor
Para ilustrar el comportamiento y el funcionamiento de los semáforos, mostra-
mos a continuación un posible escenario de ejecución de tres procesos usuario
(P1, P2, P3) y el proceso gestor (G1). Las diferentes columnas indican qué ac-
ciones llevan a cabo cada uno de los procesos en cada unidad de tiempo, qué
procesos están esperando el semáforo, qué proceso está dentro de la sección
crítica y cuál es el valor del semáforo en cada instante concreto (figura 16).
Supondremos que el semáforo, exclusion, se ha inicializado en 1 y que los
procesos usuario y el proceso gestor están dentro de un bucle infinito. Los pro-
cesos usuario generan continuamente trabajos que se envían a la impresora.
El proceso gestor imprime continuamente los trabajos que han generado los
procesos usuario.
En la tabla se ve que en el tiempo T0 se inicializa el semáforo y todavía no se ha
creado ningún proceso. No hay ningún trabajo pendiente de ser imprimido.
En T1, el semáforo exclusion ha decrementado su valor hasta 0. Esto indica
que un proceso ha recibido el permiso para entrar en la sección crítica. En
este caso, ha sido el proceso usuario P1. El resto de los procesos, el gestor
y los usuarios están intentando entrar en la sección, pero el mecanismo del
semáforo asegura que ningún otro proceso pueda entrar hasta que P1 lo libere
mediante la ejecución de la operación sem_signal(exclusion).
Figura 16. Evolución del estado de los tres procesos usuario, del proceso gestor y del semáforo
Supongamos que los tres procesos usuario generan los ficheros correspondien- (9)Con esta utilización de los semá-
tes y, tal como se ha indicado en el código del proceso usuario, a continua- foros, el orden de acceso de los
procesos a la sección crítica es to-
ción examinan el valor del semáforo exclusion con la intención de decre- talmente aleatorio.
## Página 19
<!-- source-page: 19 -->
GNUFDL • PID_00214803 19 Comunicación y sincronización
mentarle el valor y entrar en la sección crítica (modificar la variable). Uno de
los tres tiene éxito, aunque no podemos asegurar cuál acabará entrando9 en la
sección crítica. En el ejemplo, la operación sem_wait que ejecuta P1, después
de haber leído el valor del semáforo a 1, debe apropiarse la variable semáforo
y evitar que el resto de los procesos concurrentes (P2 y P3) puedan acceder
a ella antes de que sea modificada (decrementada a 0). Éste es el motivo por
el cual el código de la operación sem_wait debe ser indivisible. Más adelante
veremos cómo podemos conseguirlo.
Una vez dentro de la sección crítica, el proceso P1 actualiza la va-
riable. A continuación, libera la sección crítica ejecutando la operación
sem_signal(exclusion). En este instante, P2, P3 y el proceso gestor de la
impresora tienen la misma probabilidad de entrar en la sección crítica. En el
ejemplo, es el proceso P3 el que lo consigue. Se vuelven a repetir las mismas
operaciones y cuando P3 libera la sección entra de nuevo P1, mientras que los
procesos P2 y el proceso gestor de la impresora continúan esperándose. Esta
situación se conoce como inanición y no es deseable; más adelante, al hablar
de la implementación de los semáforos, veremos cómo se puede evitar.
Observación: la granularidaddeunsemáforo indica el número de zonas crí-
ticas a las que está asociado. Distinguimos dos grados de granularidad:
• Granularidadbaja: implica tener semáforos diferentes para zonas críticas
diferentes. Esto aumenta el grado de concurrencia entre los diferentes pro-
cesos concurrentes. En general, interesa tener este grado de granularidad.
• Granularidadalta: es cuando se utiliza un mismo semáforo para asegu-
rar la exclusión mutua de diferentes zonas críticas (independientes entre
ellas).
Contador de recursos disponibles
Muchas aplicaciones deben controlar la cantidad de recursos disponibles de
un determinado tipo pero no necesitan saber exactamente cuántos recursos
disponibles hay, sólo necesitan saber si hay alguno; si no hay ninguno, han
de esperar a que haya alguno libre.
Es posible utilizar semáforos n-arios en este entorno. El contador asociado al
semáforo reflejará el número de recursos disponibles.
1)Seinicializaelsemáforo. El valor inicial del semáforo debe ser el número
inicial de recursos disponibles (será un número entero mayor o igual que cero).
## Página 20
<!-- source-page: 20 -->
GNUFDL • PID_00214803 20 Comunicación y sincronización
2)Seejecutalaoperaciónsem_wait. Cuando un proceso necesite un recurso,
invocará esta operación. Si hay algún recurso disponible, se decrementará el
contador asociado al semáforo; de lo contrario, se esperará hasta que haya
algún recurso disponible.
3)Seejecutalaoperaciónsem_signal. El proceso que ha finalizado en la
utilización de un recurso incrementa el contador y permite que otros procesos
lo utilicen.
El ejemplo anterior de los procesos usuario y el proceso gestor de la impreso-
ra se podría haber resuelto utilizando un semáforo n-ario y aprovechando el
contador del semáforo para acumular el número de trabajos pendientes (figu-
ra 17). El proceso usuario incrementará este contador haciendo sem_signal
(figura 18) y el proceso gestor esperará la aparición de trabajos efectuando
sem_wait (figura 19). El contador asociado al semáforo indicará el número
de trabajos pendientes.
semaphore trabajos_pendientes;
...
sem_init(trabajos_pendientes, 0);
Figura 17. Definición de la variable de tipo semáforo e inicialización
...
while (cierto){
/*Generar fichero*/
...
sem_signal(trabajos_pendientes);
}
Figura 18. Código de los procesos usuario
...
while (cierto){
sem_wait(trabajos_pendientes);
/*Enviar trabajo a la impresora*/
...
}
Figura 19. Código del proceso gestor de la impresora
En el apartado 2 del anexo, tenéis un ejemplo más completo de la utilización
de este tipo de semáforos. Fijaos que en la figura 19 el gestor se bloquea si
no hay trabajos pendientes. En cambio, el código de la figura 15 hacía una
espera activa.
## Página 21
<!-- source-page: 21 -->
GNUFDL • PID_00214803 21 Comunicación y sincronización
Sincronización entre procesos
Un semáforo binario o n-ario puede implementar una sincronización entre
dos procesos en la que un proceso espera una autorización por parte del otro
con el fin de poder continuar su ejecución.
1)Seinicializaelsemáforo. El valor inicial del semáforo debe ser 0.
2)Seejecutalaoperaciónsem_wait. El proceso que espera la autorización
por parte del otro proceso debe invocar esta operación.
3)Seejecutalaoperaciónsem_signal. El proceso que debe conceder la au-
torización ha de invocar esta operación.
El proceso que espera la autorización (figura 21) ejecutará el código posterior
al sem_wait únicamente cuando el proceso que autoriza (figura 22) haya eje-
cutado el sem_signal.
semaphore sync;
...
sem_init(sync, 0);
Figura 20. Definición de la variable de tipo semáforo e inicialización
...
sem_wait(sync);
...
Figura 21. Código del proceso que espera autorización
...
sem_signal(sync);
...
Figura 22. Código del proceso que autoriza