Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Semaforos
Semaforos
Dijkstra dio en 1968 una solucin al problema de la exclusin mutua con la introduccin del
concepto de semforo binario. Est tcnica permite resolver la mayora de los problemas de
sincronizacin entre procesos y forma parte del diseo de muchos sistemas operativos y de
lenguajes de programacin concurrentes.
Un semforo binario es un indicador (S) de condicin que registra si un recurso est disponible o
no. Un semforo binario slo puede tomar dos valores: 0 y 1. Si, para un semforo binario, S = 1
entonces el recurso est disponible y la tarea lo puede utilizar; si
S = 0 el recurso no est disponible y el proceso debe esperar.
Los semforos se implementan con una cola de tareas o de condicin a la cual se aaden los
procesos que estn en espera del recurso.
Slo se permiten tres operaciones sobre un semforo
- Inicializar
- Espera (wait)
- Seal (signal)
En algunos textos, se utilizan las notaciones P y V para las operaciones de espera y seal
respectivamente, ya que sta fue la notacin empleada originalmente por Dijkstra para referirse a
las operaciones.
As pues, un semforo binario se puede definir como un tipo de datos especial que slo puede tomar
los valores 0 y 1, con una cola de tareas asociada y con slo tres operaciones para actuar sobre l.
Las operaciones pueden describirse como sigue:
Las operaciones son procedimientos que se implementan como acciones indivisibles y por ello la
comprobacin y cambio de valor del indicador se efecta de manera real como una sola operacin,
lo cual hay que tener presente a la hora de disear el planificador de tareas. En sistemas con un
nico procesador bastar simplemente con inhibir las interrupciones durante la ejecucin de las
operaciones del semforo. En los sistemas multiprocesador, sin embargo, este mtodo no resulta ya
que las instrucciones de los procesadores se pueden entrelazar de cualquier forma. La solucin est
en utilizar instrucciones hardware especiales, si se dispone de ellas, o en introducir soluciones
software como las vistas anteriormente, que ya indicamos, que servan tanto para sistemas
uniprocesador como para sistemas multiprocesador.
La operacin inicializa se debe llevar a cabo antes de que comience la ejecucin concurrente de los
procesos ya que su funcin exclusiva es dar un valor inicial al semforo.
Un proceso que corre la operacin espera y encuentra el semforo a 1, lo pone a 0 y prosigue su
ejecucin. Si el semforo est a 0 el proceso queda en estado de espera hasta que el semforo se
libera. Dicho estado se debe considerar como uno ms de los posibles de un proceso. Esto es as
debido a que un proceso en espera de un semforo no est en ejecucin ni listo para pasar a dicho
estado puesto que no tiene la CPU ni puede pasar a tenerla mientras que no se lo indique el
semforo. Tampoco es vlido el estado suspendido, ya que este estado est pensado para que lo
utilicen llamadas al sistema operativo para suspender o reactivar un proceso que no tiene por qu
tener una conexin con los semforos. El diagrama de transicin de estados de la figura se puede
ampliar con un nuevo estado que denominamos de espera.
Cuando se ejecuta la operacin seal puede haber varios procesos en la lista o cola, el proceso que
la dejar para pasar al estado listo depender del esquema de gestin de la cola de tareas
suspendidas que se haya implementado en el diseo del semforo, por ejemplo: prioridades, FIFO,
etc. Si no hay ningn proceso en espera del semforo este se deja libre (S := 1) para el primero que
lo requiera.
package Semaforo;
public class SemaforoBinario {
protected int contador = 0;
public SemaforoBinario (int valorInicial) {
contador = valorInicial;
}
synchronized public void WAIT () {
while (contador == 0)
try {
wait();
}
catch (Exception e) {}
contador--;
}
synchronized public void SIGNAL () {
contador = 1;
notify(); /* aqu con despertar a uno es suficiente, pues todos esperan
por la misma condicin */
}
}
package Semaforo;
public class SemaforoGeneral extends SemaforoBinario {
public SemaforoGeneral (int valorInicial) {
super (valorInicial);
}
synchronized public void SIGNAL () {
contador++;
notify();
}
}
SINCRONIZACIN
El uso de semforos hace que se pueda programar fcilmente la sincronizacin entre dos tareas. En
este caso las operaciones espera y seal no se utilizan dentro de un mismo proceso sino que se dan
en dos procesos separados; el que ejecuta la operacin de espera queda bloqueado hasta que el otro
proceso ejecuta la operacin de seal.
A veces se emplea la palabra seal para denominar un semforo que se usa para sincronizar
procesos. En este caso una seal tiene dos operaciones: espera y seal que utilizan para
sincronizarse dos procesos distintos.
Supongamos que un proceso quiere que se le notifique que ha tenido lugar un suceso determinado y
que otro proceso es capaz de detectar que ha ocurrido dicho suceso; el ejemplo siguiente muestra
como se puede implementar una sincronizacin entre los dos procesos utilizando un semforo.
Ejemplo
(* Sincronizacin con semforo*)
module Sincronizacin;
var sincro: semaforo;
begin (* Sincronizacin*)
inicializa(sincro, 0);
cobegin
P1;
P2;
coend
end Sincronizacion.
El semforo se inicializa a cero de modo que cuando el proceso P1 ejecuta la operacin de espera se
suspende hasta que el proceso P2 ejecuta la operacin seal. La sincronizacin se realiza
perfectamente incluso si el proceso P2 ejecuta la operacin seal antes de que el proceso P1 ejecute
la operacin de espera, ya que en este caso el proceso P2 incrementa el semforo y permite que P1
decremente el semforo y siga su ejecucin cuando alcanza la operacin espera.
El ejemplo que sigue muestra la solucin que el uso de semforos ofrece al problema clsico del
productor-consumidor. Este problema aparece en distintos lugares de un sistema operativo y
caracteriza a aquellos problemas en los que existe un conjunto de procesos que producen
informacin que otros procesos consumen, siendo diferentes las velocidades de produccin y
consumo de la informacin. Este desajuste en las velocidades, hace necesario que se establezca una
sincronizacin entre los procesos de manera que la informacin no se pierda ni se duplique,
consumindose en el orden en que es producida.
Ejemplo
Suponemos que se tienen dos procesos P1 y P2; P1 produce unos datos que consume P2. P1
almacena datos en algn sitio hasta que P2 est listo para usarlos. Por ejemplo, P1 genera
informacin con destino a una impresora y P2 es el proceso gestor de la impresora que se encarga
de imprimirlos. Para almacenar los datos se dispone de un buffer o zona de memoria comn al
productor y al consumidor. Se supone que para almacenar y tomar datos se dispone de las funciones
Poner(x) y Tomar(x). Para saber el estado del buffer se usan las funciones Lleno, que devuelve el
valor TRUE si el buffer est lleno, y Vacio, que devuelve el valor TRUE si el buffer est vaco. El
productor y el consumidor corren a velocidades distintas, de modo que si el consumidor opera
lentamente y el buffer est lleno, el productor deber bloquearse en espera de que haya sitio para
dejar ms datos; por el contrario si es el productor el que acta lentamente, el consumidor deber
bloquearse si el buffer est vaco en espera de nuevos datos. El programa que sigue muestra un
intento de solucin sin utilizar semforos.
(* Problema del Productor-Consumidor: Solucin 1*)
module Productor_Consumidor;
var BufferComun: buffer;
process Productor;
var x: dato;
begin
loop
produce(x);
while Lleno do
(* espera *)
end;
Poner(x);
end
end Productor;
process Consumidor;
var x: dato;
begin
loop
while Vacio do
(* espera *)
end;
Tomar(x);
Consume(x)
end
end Consumidor;
begin
cobegin
Productor;
Consumidor;
coend
end Productor_Consumidor;
El productor espera en un bucle hasta que el buffer no est lleno y pueda poner el dato x; el
consumidor tambin espera en un bucle a que el buffer no est vaco y pueda tomar un dato. La
solucin no es satisfactoria porque:
1.- Las funciones Poner(x) y Tomar(x) utilizan el mismo buffer, lo que plantea el problema de la
exclusin mutua.
2.- Ambos procesos utilizan una espera ocupada cuando no pueden acceder al buffer porque este
est lleno o vaco.
El primer problema puede resolverse utilizando un semforo para proteger el acceso al buffer y el
segundo sincronizando la actividad de los dos procesos mediante el uso de semforos.
En la solucin que sigue se utiliza el semforo AccesoBuffer para resolver el problema de la
exclusin mtua en el acceso al buffer y los semforos Nolleno y Novacio para la sincronizacin
entre los dos procesos.
process Consumidor;
var x: dato;
begin
loop
espera(AccesoBuffer);
if Vacio then
seal(AccesoBuffer);
espera(Novacio);
espera(AccesoBuffer)
end;
Tomar(x);
seal(AccesoBuffer);
seal(Nolleno);
Consume(x)
end
end Consumidor;
begin
inicializa(AccesoBuffer, 1);
inicializa(Nolleno, 1);
inicializa(Novacio, 0);
cobegin
Productor;
Consumidor;
coend
end Productor_Consumidor;
En la solucin presentada, el acceso al buffer para tomar o poner un dato est protegido por las
operaciones espera y seal del semforo AccesoBuffer. Antes de ejecutar las operaciones
espera(Nolleno) y espera(Novacio) se libera el semforo AccesoBuffer para evitar que el sistema se
bloquee. Esto puede ocurrir si, por ejemplo, el productor gana el acceso al buffer y permanece en
espera de que ste no est lleno para poner un dato, a la vez que el consumidor no puede acceder al
buffer por tenerlo el productor y, por lo tanto, tambin queda en espera de ganar el acceso no
puediendo sacar del estado de lleno al buffer.
El monitor no tiene por qu exportar todos sus procedimientos sino slo aquellos que sean pblicos
(puertas de entrada de las figuras *** y ***), manteniendo como privados aquellos a los que slo
pueden acceder otros procedimientos definidos dentro del monitor. Para indicar los procedimientos
del monitor que se exportan y actuan por lo tantos como puertas de entrada al monitor usaremos la
sentencia export Prc1, Prc2,....,Prcn
Utilizaremos la palabra condicin para definir las variables de condicin del monitor.
El cuerpo del monitor contiene una secuencia de instrucciones de inicializacin que se ejecutan una
sola vez y antes de que se realice alguna llamada a los procedimientos del monitor.
Se denomina condiciones a la biblioteca donde se encuentra la definicin de las variables de
condicin y de los procedimientos espera y seal de los monitores. La variable ocupado indica el
estado del semforo. Puesto que est inicialmente en falso, el primer proceso que realiza una
invocacin a la operacin sespera puede acceder al recurso controlado por el monitor y como la
variable ocupado se pone a verdadero, cualquier otro proceso que ejecute la operacin sespera debe
esperar a que se ejecute una operacin sseal para poder acceder al recurso. Cuando esto ocurre la
variable ocupado pasa a tener el valor falso y la condicin libre recibe una seal, lo que hace que
uno de los procesos que esperan en su cola pase a completar la ejecucin de sespera y pueda
acceder al recurso. Ejecuciones sucesivas de sseal van despertando a procesos en espera hasta que
no haya ms procesos en la cola de la condicin libre. Cuando no hay ningn proceso en espera de
la variable de condicin la ejecucin de sseal slo tiene por efecto poner la variable ocupado a
false, lo que permite que el primer proceso que ejecute sespera pueda acceder al recurso.
Un monitor es un objeto que implementa acceso bajo e.m a todos sus mtodos.
En Java, son objetos de una clase cuyos mtodos pblicos son todos synchronized .
Un objeto con mtodos synchronized proporciona un cerrojo que permite implantar
monitores con comodidad.
Los mtodos wait, notify, notifyAll permiten sincronizar el monitor, de acuerdo a la
semntica de los mismos ya conocida.