...

Il monitor

by user

on
Category: Documents
32

views

Report

Comments

Transcript

Il monitor
Il monitor
1
Costrutti linguistici per la sincronizzazione
è
I semafori costituiscono un meccanismo molto potente per la
sincronizzazione dei processi. Tuttavia, il suo uso può risultare
troppo “a basso livello”.
Possibilità di errori
Esempio: mutua esclusione
• scambio tra wait e signal:
signal(mutex);
<A>;
wait(mutex);
più processi possono operare nella sezione critica.
• utilizzo erroneo di wait e signal:
• etc.
wait(mutex);
<A>;
wait(mutex); -> deadlock.
2
1
Costrutti linguistici per la
sincronizzazione
èPer ovviare a problemi di questa natura si sono
introdotti costrutti linguistici “a più alto livello” :
•regioni critiche semplici e condizionali
•monitor
3
Il costrutto monitor [Hoare 74]
Definizione: Costrutto sintattico che associa un
insieme di operazioni (public o entry) ad una
struttura dati comune a più processi, tale che:
• Le operazioni entry sono le sole operazioni
permesse su quella struttura.
• Le operazioni entry sono mutuamente esclusive: un
solo processo per volta può essere attivo nel
monitor.
4
2
monitor tipo_risorsa {
<dichiarazioni variabili locali>;
<inizializzazione variabili locali>;
public void op1 ( ) {
<corpo della operazione op1 >;
}
---------public void opn ( ) {
<corpo della operazione opn>;
}
<eventuali operazioni non public>
}
5
• Le variabili locali sono accessibili solo entro il monitor.
• Le operazioni public (o entry) sono le sole operazioni che
possono essere utilizzate dai processi per accedere alle
variabili locali. L'accesso avviene in modo mutuamente
esclusivo.
• Le variabili locali mantengono il loro valore tra successive
esecuzioni delle operazioni del monitor (variabili
permanenti).
• Le operazioni non dichiarate public non sono accessibili
dall’esterno. Sono usabili solo all'interno del monitor (dalle
funzioni public e da quelle non public).
6
3
Uso del monitor
tipo_risorsa ris;
ècrea una istanza del monitor, cioè una struttura dati
organizzata secondo quanto indicato nella
dichiarazione dei dati locali.
ris.opi(...);
èchiamata di una generica operazione dell’oggetto ris.
7
Uso del monitor
Scopo del monitor è controllare l’accesso a una risorsa da
parte processi concorrenti in accordo a determinate
politiche. Le variabili locali definiscono lo stato della
risorsa associata al monitor.
L’accesso alla risorsa avviene secondo due livelli di
controllo:
1. Il primo garantisce che un solo processo alla volta possa aver
accesso alle variabili comuni del monitor. Ciò è ottenuto
garantendo che le operazioni public siano eseguite in modo
mutuamente esclusivo (eventuale sospensione dei processi
nella entry queue).
2. Il secondo controlla l’ordine con il quale i processi hanno
accesso alla risorsa. La procedura chiamata verifica il
soddisfacimento di una condizione logica (condizione di
sincronizzazione) che assicura l’ordinamento (eventuale
sospensione del processo in una coda associata alla
8
condizione e liberazione del monitor).
4
• Nel caso in cui la condizione non sia verificata, la
sospensione del processo avviene utilizzando
variabili di un nuovo tipo, detto condition
(condizione).
• La condizione di sincronizzazione è costituita da
variabili locali al monitor e da variabili proprie del
processo passate come parametri.
9
Variabili tipo condizione
La dichiarazione di una variabile cond di tipo
condizione ha la forma:
condition cond;
• Ogni variabile di tipo condizione rappresenta una coda
nella quale i processi si sospendono.
Operazioni sulle variabili condition:
• Le operazioni del monitor agiscono su tali variabili
mediante le operazioni:
wait(cond);
signal(cond);
10
5
wait:
• L’esecuzione dell’operazione wait(cond) sospende il
processo, introducendolo nella coda individuata dalla
variabile cond, e il monitor viene liberato.
signal:
• L’esecuzione dell’operazione signal(cond) rende attivo
un processo in attesa nella coda individuata dalla
variabile cond.
11
Semantiche dell’operazione signal
Q:
signal(cond);
signal(cond);
P:
wait(cond);
Come conseguenza della signal entrambi i processi, quello
segnalante Q e quello segnalato P, possono concettualmente
proseguire la loro esecuzione.
Possibili strategie:
signal_ and_ wait. P riprende immediatamente l’esecuzione ed
il processo Q viene sospeso.
signal_ and_ continue. Q prosegue la sua esecuzione
mantenendo l’accesso esclusivo al monitor, dopo aver
risvegliato il processo .
12
6
• Con la signal_ and_ wait si evita la possibilità che
Q, proseguendo, possa modificare la condizione di
sincronizzazione rendendola non più vera per P.
• Q si sospende nella coda dei processi che
attendono di usare il monitor (entry queue).
13
Semantiche della signal
condition
queue
Signal&Continue
Signal&Wait
wait
monitor libero
entry
queue
esecuzione
Signal&Continue
no
call
monitor
libero
Signal&Wait
si
14
7
Signal_and_urgent_wait
E` una variante della signal_and_wait.
signal_and_urgent_wait. Q ha la priorità rispetto agli altri processi
che aspettano di entrare nel monitor. Viene quindi sospeso in una
coda interna al monitor (urgent queue). Quando P ha terminato la
sua esecuzione (o si è nuovamente sospeso), trasferisce il controllo
a Q senza liberare il monitor.
condition
queue
Signal&UrgentWait
wait
Signal&UrgentWait
monitor libero
entry queue
urgent
queue
esecuzione
no
call
monitor
libero
P termina o si sospende
si
15
• Un caso particolare della signal _and_urgent_wait (e
della signal_and_wait) si ha quando essa corrisponde
ad una istruzione return: signal_and_return.
• Il processo completa cioè la sua operazione con il
risveglio del processo segnalato. Cede ad esso il
controllo del monitor senza rilasciare la mutua
esclusione.
16
8
signal_and_continue
• Il processo segnalato P viene trasferito dalla coda
associata alla variabile condizione alla entry_queue e
potrà rientrare nel monitor una volta che Q l’abbia
rilasciato.
• Poiché altri processi possono entrare nel monitor prima di
P, questi potrebbero modificare la condizione di
sincronizzazione (lo stesso potrebbe fare Q).
• E’ pertanto necessario che quando P rientra nel monitor
ritesti la condizione:
while(!B) wait (cond);
<accesso alla risorsa>
17
• E’ possibile anche risvegliare tutti i processi sospesi
sulla variabile condizione utilizzando la :
signal_all
che è una variante della signal_and_continue.
èTutti i processi risvegliati vengono messi nella
entry_queue dalla quale, uno alla volta potranno
rientrare nel monitor.
18
9
Esempio: monitor come gestore di
risorse (mailbox)
Utilizziamo il monitor per risolvere il problema della
comunicazione tra processi (“produttori e
consumatori”):
r il monitor rappresenta il buffer dei messaggi
(gestito in modo circolare)
r i processi Produttori (o Consumatori)
inseriranno (o preleveranno) i messaggi
mediante le funzioni entry Send (o Receive)
definite nel monitor.
r la struttura dati che rappresenta il buffer fa
parte delle variabili locali al monitor e quindi
le operazioni Send e Receive possono accedere
solo in modo mutuamente esclusivo a tale
struttura.
19
monitor buffer_circolare{
messaggio buffer[N];
int contatore=0; int testa=0; int coda=0;
condition non_pieno;
condition non_vuoto;
/* procedure e funzioni entry: */
public void send(messaggio m){ /*proc. entry -> mutua esclusione*/
if (contatore==N) non_pieno.wait;
buffer[coda]=m;
coda=(coda + 1)%N;
++contatore;
non_vuoto.signal;
}
public messaggio receive(){ /*proc. entry -> mutua esclusione*/
messaggio m;
if (contatore == 0) non_vuoto.wait;
m=buffer[testa];
testa=(testa + 1)%N;
--contatore;
non_pieno.signal;
return m;}
}/* fine monitor */
20
10
Esempio: monitor come allocatore
di risorse
Utilizziamo il monitor per garantire l’accesso esclusivo
ad una risorsa comune da parte dei processi.
r La struttura dati gestita dal monitor rappresenta lo
stato (libero,occupato) della risorsa.
r Le operazioni Richiesta e Rilascio del monitor sono
utilizzate solo per garantire l’accesso esclusivo alla
risorsa da parte dei processi.
r La mutua esclusione tra le operazioni Richiesta e
Rilascio garantisce che lo stato della risorsa venga
esaminato in modo mutuamente esclusivo dai
processi.
r Una volta guadagnato l’accesso alla risorsa i singoli
processi potranno accedere direttamente ad essa
21
all’esterno del monitor.
monitor allocatore
{ boolean occupato = false;
condition libero;
public void Richiesta()
{
if (occupato) libero.wait;
occupato = true;
}
public void Rilascio()
{
occupato = false;
libero.signal;
}
}
allocatore A; /* istanza del tipo monitor*/
void processo() /*codice di un generico processo */
{
A.Richiesta;
<uso della risorsa>;
A.Rilascio;
}
22
11
Realizzazione del costrutto monitor tramite
semafori
Il compilatore assegna ad ogni istanza di un monitor:
• un semaforo mutex inizializzato a 1 per la mutua
esclusione delle operazioni del monitor:
• la richiesta di un processo di eseguire
un’operazione public equivale all’esecuzione di una
P(mutex).
Il compilatore assegna a ogni variabile cond di tipo
condition:
• un semaforo condsem inizializzato a 0 sul quale il
processo si può sospendere tramite una
wait(condsem).
• un contatore condcount inizializzato a 0 per tenere
conto dei processi sospesi su condsem.
23
Signal_and_ continue
Prologo di ogni operazione public: P(mutex);
Epilogo di ogni operazione public: V(mutex);
wait(cond):
{
condcount++;
V(mutex);
P(condsem);
P(mutex);
}
Signal&Continue
call
signal(cond):
{
if (condcount>0)
{
condcount--;
V(condsem);
}
}
condition
queue
entry queue
wait
esecuzione
monitor libero
Signal&Continue
24
12
Signal_and_ wait
Prologo di ogni operazione public P(mutex);
Epilogo di ogni operazione public V(mutex);
wait(cond):
{
condcount++;
V(mutex);
P(condsem);
}
condition
queue
signal(cond):
{
if (condcount>0)
{
condcount-- ;
V(condsem);
entry
P(mutex);
queue
}
call
}
Signal&Wait
wait
monitor libero
esecuzione
Signal&Wait
25
Signal_and_urgent_wait
urgent: semaforo per la sospensione del processo segnalante (v.iniz. 0)
urgentcount: contatore dei processi sospesi su urgent
Prologo di ogni operazione: P(mutex);
Epilogo di ogni operazione: if(urgentcount>0)
wait(cond):
{
condcount++;
if (urgentcount>0)
V(urgent);
else V(mutex);
P(condsem);
condcount-- ; }
call
V(urgent) else V(mutex);
condition
queue
Signal&UrgentWait
wait
entry
queue
signal(cond):
{
if (condcount>0
{ urgentcount++;
V(condsem);
P(urgent);
urgentcondcount-- ;
}
}
monitor libero
esecuzione
Signal&UrgentWait
P termina o si sospende
urgent
queue
26
13
Signal_and_return
Prologo di ogni operazione: P(mutex);
Epilogo di ogni operazione:
•se la funzione non contiene signal allora : V(mutex)
•altrimenti signal(cond) (vedi sotto).
wait(cond):
{
condcount++;
V(mutex);
P(condsem);
condcount-- ;
}
signal(cond):
{
if (condcount>0)
V(condsem);
else V(mutex);
}
27
Ulteriori operazioni sulle variabili
condizione
Sospensione con indicazione della priorità:
wait(cond, p);
èi processi sono accodati rispettando il valore (crescente o
decrescente) di p e vengono risvegliati nello stesso
ordine.
Verifica dello stato della coda:
queue(cond);
èfornisce il valore vero se esistono processi sospesi nella
coda associata a cond , true altrimenti.
28
14
Esempio: allocazione di risorse in uso esclusivo
Si vuole che la risorsa venga assegnata a quello tra tutti i processi sospesi che
la userà per il periodo di tempo inferiore :
monitor allocatore
{ boolean occupato = false;
condition libero;
public void Richiesta(int tempo)
{
if (occupato) libero.wait(tempo);
occupato = true;
}
public void Rilascio()
{
occupato = false;
libero.signal;
}
}
I processi sono inseriti nella coda secondo l’ordine crescente di p e quindi il
primo processo risvegliato è quello che richiede meno tempo.
29
Esempio: lettori e scrittori
Si supponga di voler realizzare la seguente politica di
allocazione della risorsa:
1. un nuovo lettore non può acquisire la risorsa se c’e’ uno
scrittore in attesa
2. tutti i lettori sospesi al termine di una scrittura hanno
priorità sul successivo scrittore
Soluzione: uso del monitor con le seguenti variabili locali:
•
•
•
num-lettori: il numero di processi lettori attivi sulla risorsa
occupato: una variabile logica che indica se la risorsa è
occupata da uno scrittore (occupato=true)
ok-lettura , ok-scrittura : due variabili condizione sulle
quali si sospendono rispettivamente i processi lettori e
scrittori
30
15
monitor lettori_scrittori
{ int num-lettori=0,occupato=0;
condition ok_lettura,ok_scrittura;
public void inizio_lettura()
{
if (occupato || ok_scrittura.queue)
ok_lettura.wait;
num_lettori++;
ok_lettura.signal;
}
public void fine_lettura()
{
num_lettori-- ;
if (num_lettori==0)
ok_scrittura.signal;
}
public void inizio_scrittura()
{
if ((num_lettori!=0)|| occupato)
ok_scrittura.wait;
occupato=1;
}
/* continua...*/
31
/* ...continua */
public void fine_scrittura()
{
occupato=0;
if (ok-lettura.queue)
ok-lettura.signal;
else
ok-scrittura.signal;
}
}/* fine monitor */
lettori_scrittori LS; /* istanza del monitor*/
void lettore() /*codice di un generico lettore */
{
LS.inizio_lettura();
<lettura>;
LS.fine_lettura()
}
void scrittore() /*codice di un generico scrittore */
{
LS.inizio_scrittura();
<scrittura>;
LS.fine_scrittura()
}
32
16
Esempio di uso del costrutto
monitor
L'algoritmo dell'ascensore (scan)
33
Algorimo scan
• In un edificio a N piani, l'ascensore deve servire le
richieste di utenti che competono per usare
l'ascensore, con l'obiettivo di spostarsi a un piano
diverso.
• L'ascensore puo` servire una richiesta alla volta.
• Movimento: l'ascensore si puo` spostare tra gli N
piani [0,..N-1] nelle due direzioni:
r dal basso verso l'alto (SU);
r nella direzione opposta (GIU);
• L'algoritmo SCAN e` una possibile politica di
scheduling delle richieste di uso dell'ascensore,
che ha come obiettivo la minimizzazione del
numero dei cambiamenti di direzione.
34
17
SCAN
Politica:
r
in ogni istante, all'ascensore e` associata una direzione corrente
(SU, GIU) e una posizione corrente (piano associato alla prossima
richiesta da servire).
r
ogni richiesta e` caratterizzata da una destinazione T (che
individua il piano di destinazione richiesto dall'utente in attesa):
• se l'ascensore sta salendo (direzione SU), verranno servite tutte le
richieste con T raggiungibile in quella direzione (T> posizione corrente)
• se l'ascensore sta scendendo (direzione GIU), verranno servite tutte le
richieste con T raggiungibile in quella direzione (T< posizione corrente).
r
r
le richieste sono servite secondo l’ordine di vicinanza alla richiesta
corrente.
Quando nella direzione scelta non ci sono più richieste da servire, la
direzione dell'ascensore viene invertita ed il procedimento è
ripetuto.
N.B.Viene servita una richiesta alla volta.
35
Soluzione: monitor
• Modelliamo ogni utente dell'ascensore con un processo (thread)
concorrente.
• Definiamo un monitor, il cui compito e` realizzare la politica di
servizio mediante le due procedure entry:
r
Richiesta(T): viene invocata dai processi per ottenere la
fermata dell'ascensore al piano T:
• se l'ascensore è occupato, la richiesta del processo viene
accodata in funzione dello stato corrente del'ascensore
(direzione e richiesta corrente) in una coda associata ad una
direzione in modo da rispettare la politica.
r
Rilascio: viene invocata da ogni processo arrivato a
destinazione per rilasciare l'ascensore:
• se vi sono richieste in attesa sulla stessa direzione, verra`
risvegliata la prima (la piu` vicina alla posizione corrente
dell'ascensore); altrimenti, la direzione verra` invertita e verra`
risvegliato il primo processo in attesa nella nuova direzione.
N.B.Viene servita una richiesta alla volta.
36
18
Monitor
• Il monitor e` caratterizzato dalle seguenti
variabili:
r occupato: indica se l'ascensore sta servendo
una richiesta;
r direzione: indica la direzione corrente
dell'ascensore (SU, GIU);
r posizione: indica la destinazione corrente;
r dir_SU, dir_GIU: condition associate alle due
direzioni, sulle quali si sospenderanno i processi
in attesa di servizio.
37
typedef int piano;
typedef enum{SU,GIU}dir;
monitor movimento_ascensore
{ piano posizione=0; /*inizialmente al piano terra..*/
boolean occupato=false;
/* inizialmente libero..*/
dir direzione=SU;
condition dir_SU,dir_GIU;
public void Richiesta(piano dest)
{ if (occupato==true)
if ((direzione==SU)&& (dest<=posizione))
dir_GIU.wait(dest);
else dir_SU.wait(N-dest);
occupato=true;
posizione=dest;
}
38
19
public void Rilascio
{ occupato=false;
if (direzione==SU)
if (dir_SU.queue)
dir_SU.signal;
else{
direzione=GIU;
dir_GIU.signal; }
else
if (dir_GIU.queue)
dir_GIU.signal;
else{
direzione=SU;
dir_SU.signal; }
}
} /* fine monitor*/
39
movimento_ascensore A; /* istanza del monitor*/
void Utente() /*codice di un generico utente */
{
piano P;
<assegnamento valore di P>
A.Richiesta(P);
<uso dell'ascensore>;
A.Rilascio();
}
40
20
Fly UP