135 lines
4.9 KiB
Markdown
135 lines
4.9 KiB
Markdown
# Monitor
|
|
|
|
Un **monitor** e' una combinazione di ADT (abstract data type, *tipo astratto*) e **mutua esclusione**.
|
|
*(vedi slides per definizione)*
|
|
* I monitor sono strutture dati che implementano operazioni mutualmente esclusive.
|
|
|
|
## Implementazione
|
|
|
|
```
|
|
monitor stack {
|
|
.
|
|
.
|
|
public push()
|
|
public pop()
|
|
.
|
|
.etc
|
|
}
|
|
```
|
|
Un monitor e' uno **stack**. *(slides per implementazione classica)*
|
|
|
|
### Esempio
|
|
|
|
* La definizione di monitor definisce un tipo *alpha*, secondo la definizione classica.
|
|
* Il tipo definito puo' essere instanzializzato:
|
|
```
|
|
alpha x
|
|
```
|
|
* Le operazioni generiche e **pubbliche** definite dal monitor sono chiamate con **dot notation**:
|
|
```
|
|
x.op_i()
|
|
```
|
|
**Tutte le operazioni implementate dal monitor sono mutualmente esclusive**, permettendo quindi una semplificazione e sicurezza maggiore nella scrittura di programmi concorrenti.
|
|
|
|
**NOTA**: Il runtime support del linguaggio, che opera a livello kernel, usa comunque primitive semaforiche. L'utilizzo e' pero' astratto dal costrutto monitor.
|
|
|
|
## Variabili tipo condizione
|
|
|
|
Sono variabili che si possono usare solo all'interno del monitor. In genere, **ogni condition e' implementata tramite una coda**, di solito FIFO. Le procedure dei monitor agiscono sulle variabili condizione con le operazioni:
|
|
|
|
* wait(cond) <- sospende il processo in ogni caso e lo introduce nella coda individuata da cond
|
|
* signal(cond) <- rende attivo un processo in attesa nella coda individuata da cond
|
|
|
|
**NOTA**: wait e signal **non sono** equivalenti a P,V per i semafori, in quanto gestiscono automaticamente le code e non permettono di testare condizioni (*if(cond)...* va utilizzato prima di wait).
|
|
|
|
*(slides per esempio)*
|
|
Le variabili condizione possono essere utilizzate anche come semafori privati (N variabili condizione per N processi).
|
|
|
|
### Signal: realizzazione semantica
|
|
|
|
Signal non e' in grado di discriminare tra processi bloccati. Quindi bisogna discriminare, tra due processi, quale debba proseguire nella SC (processo *svegliante* o processo *svegliato*).
|
|
|
|
**Dipende dall'implementazione del singolo linguaggio**. Java, ad esempio, da la precedenza a un processo svegliante.
|
|
|
|
Questo implica l'utilizzo di un while loop per svegliare tutta la pool di processi in coda, che pero' non e' starvation free.
|
|
#### Java: Signal and continue
|
|
|
|
Quando un processo viene svegliato da un altro, viene rimesso in coda, mentre il processo svegliante puo' continuare nella SC. Il processo svegliato quindi potrebbe ritrovarsi alla fine della coda.
|
|
**Problema starvation svegliato**
|
|
|
|
#### Signal and wait
|
|
|
|
Opposto del signal and continue: il processo svegliato prende il posto dello svegliante, e lo svegliante va in fondo alla coda.
|
|
**Problema starvation svegliante**
|
|
|
|
#### Signal and urgent wait
|
|
|
|
Signal and wait, ma il processo svegliante acquisisce la prima posizione nella coda (**urgent queue**). Questo e' l'approccio piu' *fair* rispetto a tutti i processi.
|
|
|
|
*(slides per esempio allocatore)*
|
|
|
|
**In una semantica SAUW, non ha senso utilizzare primitive signalAll**, in quanto violano il principio della mutua esclusione.
|
|
|
|
## Comparison: Monitor e Semafori
|
|
|
|
Qual'e' il piu' *potente* (inteso: che risolve la piu' vasta gamma di problemi) tra monitor e semafori?
|
|
|
|
* Sono **equivalenti**, in quanto con i semafori e' possibile implementare i monitor, e con i monitor e' possibile implementare i semafori. Non sono quindi piu' potenti dei semafori, sono solo un'astrazione di essi.
|
|
|
|
*(vedi slides per dimostrazione di realizzazione)*
|
|
|
|
## Operazioni aggiuntive su variabili condizione
|
|
|
|
* **wait(cond,p)**: wait con indicazione della priorita', che e' data come numerica, in ordine di solito decrescente. (priorita' 0 = massima). Il linguaggio implementa quindi una *priority queue*.
|
|
|
|
* **empty(cond)**: true se la coda e' vuota, false se ci sono processi in attesa
|
|
|
|
* **signalAll(cond)**: rende attivi tutti i processi in coda, ma si puo' utilizzare **solo con signal and continue**.
|
|
|
|
## Esempi
|
|
|
|
### Shortest-job-next: allocazione di risorse
|
|
|
|
```
|
|
monitor allocatore {
|
|
bool occupata = false;
|
|
condition non_occ;
|
|
|
|
public void richiesta (int tempo) {
|
|
if (occupata) {
|
|
wait (non_occ, tempo);
|
|
occupata = true;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
*(altri esempi su slides, disco a teste mobili, lettori / scrittori con monitor)*
|
|
|
|
#### Monitor: Lettori / scrittori - Prova di correttezza
|
|
|
|
La prova di correttezza di un monitor si ottiene tramite gli **invarianti di monitor**, dimostrate valide per induzione:
|
|
|
|
* sono vere al momento dell'inizializzazione del monitor
|
|
* sono vere all'uscita di esecuzione della chiamata al monitor
|
|
|
|
L'invariante da dimostrare e' il seguente:
|
|
siano:
|
|
* R - numero di processi in lettura
|
|
* W - numero di processi in scrittura
|
|
|
|
```
|
|
(R>0 => W=0) and (W <= 1) and (W = 1 => R = 0)
|
|
```
|
|
* Facilmente dimostrabile e' il seguente gruppo di invarianti:
|
|
```
|
|
R = num_lett
|
|
W = num_scritt
|
|
R >= 0
|
|
W >= 0
|
|
```
|
|
##### Dimostrazione
|
|
|
|
* All'inizio, l'invariante in tesi e' chiaramente vero.
|
|
* Supposto vero all'inizio di ciascuna delle 4 procedure di monitor, e' vero anche alla fine.
|
|
|
|
*(vedi slides per casi specifici)*
|