UniTO/anno2/YearI/MCAD/lesson7-18102017.md

184 lines
5.1 KiB
Markdown
Raw Normal View History

2018-11-22 13:09:11 +01:00
# Semafori concorrenti - HHO
*(scrivi il resto)*
## No deadlock
Per assurdo, supponiamo deadlock al tempo T: i semafori si bloccano su P (idrogeno su P(sH), ossigeno su uno dei P(sO))
* Invarianti semaforici
```
val(sH,T) = 0
=> NP(sO, T) = NV(s0,T) // nessuna P eseguita con successo, il valore di sH e' 0 perche' il processo e' bloccato
val(sO,T) = 0
=> NP(sH, T) = NV(sH, T) + 2
```
* utilizziamo gli invarianti topologici
```
NP (sH, T) = NV (sO, T)
=> 0 <= NP(sO,T) - NV (sH,T) <= 1
```
* Cerchiamo un assurdo:
```
NV (sH, T) + 2 = NP (sH, T) = NV (sO, T) <= NV (sH,T) + 1
```
* Il risultato non e' ammissibile (2<1) quindi non e' possibile ottenere deadlock dalla soluzione presentata.
# Uso di Semafori Privati
Semafori (binari/contatori) di **proprieta' di uno o piu' processi**.
I proprietari sono gli unici a poter eseguire P sul suddetto semaforo, ma **non ci sono restrizioni sull'uso di V**.
## Utilizzi
* Svegliare un determinato processo, per rendere la gestione di una coda di processi indipendente dall'implementazione del semaforo (kernel space). In questo modo posso stabilire una gerarchia di prorita' tra processi.
### Vantaggi
Precisione nella gestione di pool di processi.
### Svantaggi
Complessita' aggiunta all'implementazione: il programma deve mantenere piu' semafori (anche a livello di kernel: i semafori privati possono essere implementati **al di fuori** di una coda). Il caso piu' comune e' la necessita' di dover mantenere un semaforo per ogni processo in coda, invece che uno solo (e una coda).
## Schemi di gestione
### Schema 1
Contesto:
```
< struttura dati gestore >
semaphore mutex;
semaphore priv[n] = {0}; // array di semafori privati, ognuno dedicato a un processo (n processi)
```
#### Procedura di acquisizione
```
public int acquisisci (int p) {
// simile ai semafori condizionali, anche qui dovremo testare l'ottenibilita' di una risorsa
int j; // risorsa da allocare
P (mutex);
if ( ! Cp) { // Cp e' la condizione per il processo p
< registrare sospensione del processo p >
V (mutex);
P (priv[p]);
< registro che il processo p non e' piu' sospeso >
}
< alloco risorsa j >
V (mutex);
return j;
}
```
#### Procedura di rilascio
```
public void rilascio (int j) {
int p;
P (mutex);
< registro rilascio risorsa j >
if ( < esiste almeno 1 processo sospeso > ) {
< scelgo il processo p con massima priorita' >
V (priv[p]);
} else {
V (mutex);
}
}
```
### Esempio: allocazione di pool di risorse
```
int avail = M;
bool libero[M] = {true};
bool sospeso[n] = {false};
int cnts = 0; // conta i sospesi
semaphore mutex;
semaphore priv[n] = {0}; // array di semafori privati, ognuno dedicato a un processo (n processi)
```
#### Procedura di acquisizione
```
public int acquisisci (int p) {
int j; // risorsa da allocare
P (mutex);
if ( avail == 0) {
sospeso[p] = true;
cnts++;
V (mutex);
P (priv[p]);
sospeso[p] = false;
cnts--;
}
avail--;
while (libero[j]) j++;
libero[i] = false;
V (mutex);
return j;
}
```
#### Procedura di rilascio
```
public void rilascio (int j) {
int p;
P (mutex);
avail++;
libero[j] = true;
if ( cnts > 0 ) {
p = maxpriority (sospeso); // ottengo il processo con priorita' massima
V (priv[p]);
} else {
V (mutex);
}
}
```
### Schema 2
Siccome e' complesso dal punto di vista della programmazione strutturata (alto livello), e' stato proposto un secondo schema.
#### Procedura di acquisizione
```
public void acquisizione (int p) {
P (mutex);
if (Cp) {
< alloca risorsa >
V (priv[p]);
} else {
< registro sospensione processo p>
}
V (mutex);
P (priv[p]); // a seconda del valore di Cp, questa P e' sospensiva (!Cp) o vanificata dalla V (Cp)
}
```
*nota: la risorsa in questo caso e' garantita dal processo che rilascia, perche' se Cp non e' verificata, questa procedura non alloca nulla*
#### Procedura di rilascio
```
public void rilascio (int j) {
int p;
P (mutex);
< registro rilascio risorsa j >
if ( < esiste almeno 1 processo sospeso > ) {
< scelgo il processo p con massima priorita' >
< alloco risorsa >
< registro che p non e' sospeso >
V (priv[p]);
}
V (mutex);
}
```
#### Pro e contro della soluzione 2
* Un problema della soluzione 2 e' che e' limitato a 1 dato libero per volta (se i processi consumatori hanno bisogno di piu' di un "dato" (risorsa), non ho modo di sapere quanti ne sto rilasciando), mentre la soluzione 1 implementata con un *while* risolve questo problema.
* Siccome il processo rilasciante si occupa anche di allocare le risorse, non si puo' effettuare il passaggio di parametri. Questo caso e' ovviato dall'utilizzo di un array condiviso *allocazione* che fornisce l'indice della risorsa che e' stata allocata.
# Problema dell'accesso unico (senso unico alternato) - Readers & Writers
## Precedenza agli scrittori
L'implementazione con precedenza agli scrittori permette di bloccare i readers se un writer e' nella SC, ma anche se un writer e' in attesa.
## Soluzione bilanciata
Utilizzando dei semafori privati, e' possibile associare un semaforo privato a lettori e scrittori, ottenendo un accesso limitato degli scrittori alla SC, e quindi ottenendo una soluzione equilibrata.