1. INSTITUTO TECNOLOGICO de Morelia Sistemas Operativos
M. en C. Felipe Morales López 1 de 6
UNIDAD 5
MONITORES
5.1 Introducción
Los semáforos son primitivas con las cuales es difícil expresar una solución a grandes problemas de
concurrencia y su presencia en programas concurrentes incrementa la ya existente dificultad para
probar que los programas son correctos.
Los algoritmos para la implementación de la exclusión mutua basada en semáforos tienen algunas
debilidades:
• La omisión de una de estas primitivas puede corromper la operación de un sistema
concurrente.
• El control de la concurrencia es responsabilidad del programador.
• Las primitivas de control se encuentran esparcidas por todo el sistema.
La noción de un monitor fue presentada inicialmente por Dijkstra (1971), posteriormente por
Brinch Hansen (1973) y después fue refinada por Hoare (1974).
5. 2 Definición
Un monitor es un mecanismo de software para control de concurrencia que contiene los datos y los
procedimientos necesarios para realizar la asignación de un determinado recurso o grupo de
recursos compartidos reutilizables en serie.
• Un monitor se usa para manejar todas las funciones de concurrencia, comunicación entre
procesos y localización física de recursos en una región crítica.
• Para llevar a cabo la asignación de un recurso un proceso debe llamar a una función particular
del monitor. Pueden existir varios procesos que deseen entrar al monitor, pero la exclusión
mutua queda definida por la frontera del monitor: en un tiempo dado sólo un proceso se
encuentra dentro del monitor.
• Los datos contenidos en el monitor pueden ser globales (accesibles a todos los procedimientos
dentro del monitor), o locales (accesibles a un procedimiento específico). Estos datos sólo son
accesibles dentro del monitor; no hay forma de que un proceso fuera del monitor pueda acceder
a dichos datos.
• El monitor consta de varios procedimientos que manipulan datos internos y existe una parte de
inicialización (figura 5.1). El monitor puede ser visto como una aduana en donde se permite o
no el acceso a un recurso compartido.
• El código del monitor consta de 2 partes lógicas:
• El algoritmo para la manipulación del recurso.
• El mecanismo para la asignación del orden en el cual los procesos asociados pueden
compartir el recurso.
2. INSTITUTO TECNOLOGICO de Morelia Sistemas Operativos
M. en C. Felipe Morales López 2 de 6
Figura 5.1 Estructura básica de un monitor.
Para asegurar que un proceso obtenga el recurso que espera, el monitor (la lógica del monitor) debe
darle prioridad sobre los nuevos procesos que solicitan entrar al monitor. De otra manera, los
nuevos procesos tomaran el recurso antes de que el proceso que espera lo haga, esto puede llevar al
proceso que espera a la postergación indefinida.
• Dado que un proceso puede necesitar esperar fuera del monitor por varias razones se creo un
mecanismo llamado variable de condición. Una variable de condición se controla con solo dos
operaciones:
• wait(variable) Forma el proceso en la lista de espera de la variable de condición.
• signal(variable) Libera al primer proceso de la lista de la variable de decisión. Esta
operación implica la salida del proceso actual del monitor.
• Normalmente se usa una variable de condición por cada situación por la cual un proceso debe
de esperar.
Si el proceso que se encuentra dentro del monitor encuentra que el recurso que ocupa no está
disponible, el procedimiento del monitor debe sacar momentáneamente al proceso y hacerlo esperar
fuera del monitor (wait). El proceso NO puede permanecer en el monitor ya que esto viola la
condición de exclusión mutua y debe esperar fuera del monitor la liberación del recurso que
necesita. Cuando el recurso se libera se debe indicar que está libre para que otro proceso pueda
entrar.
Procedimiento UNO
Variables Locales
Procedimiento DOS
Declaración Variables Globales
Procedimiento de
inicialización
3. INSTITUTO TECNOLOGICO de Morelia Sistemas Operativos
M. en C. Felipe Morales López 3 de 6
5.3 Diagrama de estados del Monitor
En el estado activo existen tres opciones para que un proceso abandone el monitor:
1- SALIR: en este caso un proceso llega al final del procedimiento de monitor que está
ejecutando. Por ejemplo, en la salida normal de una región crítica
2- CONTINUAR: Esta operación se realiza cuando el proceso activo completó una acción
que otro proceso pudiera estar esperando (en el estado bloqueado) y entonces le envía la
señal (signal) de que puede continuar. Al proceso bloqueado se le permite regresar a activo.
Si no hay procesos bloqueados se abre la entrada del monitor.
3- RETARDAR: El retardo se presenta cuando un proceso necesita el resultado de una acción
que otro proceso aún no realiza y entonces espera (wait).
El proceso que desbloquea a otro a través de la operación continuar debe dejar el monitor.
5.4 Ejemplos de aplicación de monitores
Productor/consumidor con un buffer circular. Los sistemas operativos conceden algunas veces
una cantidad fija de memoria para las comunicaciones a través del buffer entre los procesos
consumidor y productor. Esto se puede simular por un arreglo de tamaño fijo. El productor deposita
los datos en los elementos sucesivos del arreglo. El consumidor los quita en el orden que fueron
depositados.
El productor puede ir varios pasos adelante del consumidor. El productor llenará el último elemento
del arreglo. Cuando produce más datos, tiene que regresar y continuar a depositar de nuevo datos en
el primer elemento del arreglo (suponiendo que el consumidor haya retirado los datos previamente
depositados allí por el productor), de esta forma, el arreglo se cierra en circulo. El buffer circular es
apropiado para implementar el control del spool en los sistemas operativos.
Salir o
Retardar o
Continuar (2)
Continuar (1)
SALIR
CONTINUAR
RETARDAR
BLOQUEADO
ACTIVOESPERA
Transiciones propias
Transiciones forzadas
4. INSTITUTO TECNOLOGICO de Morelia Sistemas Operativos
M. en C. Felipe Morales López 4 de 6
monitor ProductorConsumidor
condition full, empty;
integer count;
procedure enter;
begin
if count = N then wait(full);
enter_item;
count:= count + 1;
if count = 1 then signal(empty);
end;
procedure remove;
begin
if count = 0 then wait(empty);
remove_item;
count:= count - 1;
if count = N - 1 then signal(full);
end;
count:= 0;
end monitor;
procedure productor;
begin
while true do
begin
produce_item;
ProductorConsumidor.enter;
end
end;
procedure consumidor;
begin
while true do
begin
ProductorConsumidor.remove;
consume_item;
end
end;
begin
parbegin
productor;
consumidor;
parend
end
Algoritmo 5.1 Productor/Consumidor implementado con un monitor
module MonitorBufferCircular;
var bufferCircular: array[0..Ranuras-1] of cosas;
ranuraEnUso : [0..Ranuras];
RanuraAllenar: [0..Ranuras-1];
RanuraAvaciar: [0..Ranuras-1];
BCircularTieneDatos,
BCircularTieneEspacio:condition;
procedure llenarRanura(datosRanura:cosas);
begin
if ranuraEnUso = ranuras then
wait(BCircularTieneEspacio);
end;
bufferCircular[RanuraAllenar]:=datosRanura;
inc(ranuraEnUso);
RanuraAllenar:=
(RanuraAllenar+1) mod ranuras;
signal(BCircularTieneDatos);
end llenaRanura;
procedure vaciaRanura;
begin
if ranuraEnUso = 0 then
wait(BCircularTieneDatos);
end;
datosRanura:=bufferCircular[RanuraAvaciar];
dec(ranuraEnUso);
RanuraAvaciar:=(RanuraAvaciar+1) mod ranuras;
signal(BCircularTieneEspacio);
end vaciaRanura;
begin
ranuraEnUso:= 0;
RanuraAllenar:= 0;
RanuraAvaciar:= 0;
end monitorBufferCircular
Algoritmo 5.2 Buffer circular implementado con un monitor.
5. INSTITUTO TECNOLOGICO de Morelia Sistemas Operativos
M. en C. Felipe Morales López 5 de 6
Problema de los Lectores y Escritores. La solución de este problema se puede usar como un
modelo para implementar un mecanismo de acceso a información compartida.
Problema: Existen varios procesos lectores y escritores que acceden una base de datos común, las
reglas de acceso son:
1- Cualquier número de procesos lectores puede acceder la base de datos en forma
concurrente.
2- Cuando un proceso escritor está accediendo la base de datos ningún otro proceso, ni
lector, ni escritor podrá accederla.
3- Cuando están accediendo procesos lectores y se tenga por lo menos a un escritor
esperando acceso, los lectores que llegaron después del escritor no acceden directamente
sino esperan.
4- Los procesos lectores esperando que un escritor termine su acceso tienen prioridad sobre
el siguiente escritor.
5- Un escritor esperando que los procesos lectores terminen su acceso tiene prioridad sobre
los lectores que están esperando.
module MLecEsc;
var lectores: integer;
AlguienEstaEscribiendo : boolean;
LecturaPermitida,
EscrituraPermitida: condition;
procedure ComienzaLectura;
begin
if AlguienEstaEscribiendo or
queue(EscrituraPermitida) then
wait(LecturaPermitida);
end:
inc(lectores);
signal(LecturaPermitida);
end ComienzaLectura;
procedure TerminaLectura;
begin
dec(lectores);
if lectores = 0 then
signal(EscrituraPermitida);
end;
end TerminaLectura;
procedure ComienzaEscritura;
begin
if (lectores > 0) or (AlguienEstaEscribiendo) then
wait(EscrituraPermitida);
end;
AlguienEstaEscribiendo:=true;
end ComienzaEscritura;
procedure TerminaEscritura;
begin
AlguienEstaEscribiendo:=false;
if queue(LecturaPermitida) then
signal(LecturaPermitida);
else
signal(EscrituraPermitida);
end;
end TerminaEscritura;
begin
lectores:=0;
AlguienEstaEscribiendo:=false;
end.
Algoritmo 5.3 Problema de los Lectores y Escritores.
Problema de los Filósofos. Existen n filósofos que pasan su vida pensando y comiendo.
• Cada filósofo tiene su lugar en una mesa circular en cuyo centro está un plato con espagueti.
• Para comer espagueti se requieren dos tenedores, pero sólo hay n tenedores, uno entre cada par
de filósofos.
• Los únicos tenedores que puede tomar un filósofo son los inmediatos a su izquierda y derecha.
6. INSTITUTO TECNOLOGICO de Morelia Sistemas Operativos
M. en C. Felipe Morales López 6 de 6
El problema es simular el comportamiento de un filósofo igual para todos, de tal forma que:
• Se evite el interbloqueo, por ejemplo, cuando simultáneamente cada filósofo toma su tenedor
izquierdo y se queda esperando por su tenedor derecho.
• Se evite la inanición esto es, que un filósofo no pueda comer y se muera de hambre.
5.5 Implementación de monitores con Java
El siguiente ejemplo es una implementación del problema productor/consumidor con java. Se
compone de cuatro clases simples: Q, la cola cuyo acceso se trata de sincronizar; Producer, el
objeto hilo que genera datos para la cola; Consumer, el objeto hilo que consume datos de la cola; y,
PC, la miniclase que crea Q, Producer y Consumer.
class Q
{
int n;
boolean valueSet = false;
synchronized int get( )
{
if (!valueSet)
try wait( ); catch(InterruptedException e);
System.out.println (“Obtenido: “ + n);
valueSet = false;
notify( );
return n;
}
synchronized int put(int n)
{
if (valueSet)
try wait( ); catch(InterruptedException e);
this.n = n;
valueSet = true;
System.out.println (“Colocado: “ + n);
notify( );
}
}
class Producer implements Runnable
{
Q q;
Producer (Q q)
{
this.q = q;
new Thread(this, “Producer”).start( );
}
public void run( )
{
int i = 0;
while(true)
{ q.put( i++ ); }
}
}
class Consumer implements Runnable
{
Q q;
Producer (Q q)
{
this.q = q;
new Thread(this, “Consumer”).start( );
}
public void run( )
{
while(true)
{ q.get( ); }
}
}
class PC
{
public static void main(String args[ ] )
{
Q q = new Q( );
new Producer (q);
new Consumer(q);
}
}