177 lines
4.6 KiB
Markdown
Executable file
177 lines
4.6 KiB
Markdown
Executable file
# Semafori e Sezioni critiche condizionali (continua)
|
|
|
|
## Implementazione di Accesso condizionale
|
|
|
|
Inizializzazione:
|
|
```
|
|
struttura dati R;
|
|
semaphore mutex = 1;
|
|
semaphore sem = 0; //blocca accesso a sc
|
|
int csem = 0; //contatore per #processi bloccati su sem
|
|
```
|
|
Due operazioni: **Richiesta** e **Rilascio**
|
|
|
|
### Soluzione classica
|
|
|
|
#### Richiesta
|
|
|
|
Un processo cerca di entrare nella SC, verificare la condizione (C) ed eseguire l'operazione
|
|
```
|
|
public void request () {
|
|
// ingresso in SC
|
|
P (mutex);
|
|
|
|
// verifica della condizione
|
|
// finche' non e' verificata, loop
|
|
while (!C) {
|
|
csem++;
|
|
V (mutex); // libero la SC
|
|
P (sem); // aspetto che sem sia libero
|
|
P (mutex); // rientro in SC
|
|
}
|
|
|
|
// se arriva qui, C e' verificata e il processo e' nella sezione critica
|
|
<< operazione critica >>
|
|
V (mutex)
|
|
}
|
|
```
|
|
**NOTA**: E' possibile che un processo entri nella sezione critica mentre l'altro e' dentro il loop while. Questo fenomeno si chiama *spiffero*.
|
|
|
|
#### Rilascio
|
|
|
|
Rilascio delle risorse dopo aver eseguito l'operazione critica.
|
|
```
|
|
public void release () {
|
|
P (mutex);
|
|
<< operazione critica >>
|
|
if (csem > 0) {
|
|
csem--;
|
|
V (sem);
|
|
}
|
|
V (mutex);
|
|
}
|
|
```
|
|
|
|
### Soluzione alternativa
|
|
|
|
Questa soluzione, per quanto piu' efficiente,**e' utilizzabile solo quando l'operazione critica cambia il valore della condizione C, permettendo a un altro processo di accedervi**. C deve essere quindi condivisa tra processi.
|
|
|
|
Due operazioni, come nella soluzione classica.
|
|
#### Richiesta
|
|
```
|
|
public void request () {
|
|
|
|
if (!C) { // simile alla soluzione classica
|
|
csem++; // niente loop perche' assumiamo che eseguire l'OC cambi il valore di C
|
|
V (mutex);
|
|
P (sem);
|
|
csem--; // V (sem) puo' essere chiamato qui, o durante il rilascio
|
|
}
|
|
|
|
<< operazione critica >>
|
|
V (mutex);
|
|
}
|
|
```
|
|
#### Rilascio
|
|
```
|
|
public void release () {
|
|
P (mutex);
|
|
<< operazione critica >>
|
|
if (csem > 0) {
|
|
V (sem);
|
|
} else {
|
|
V (mutex);
|
|
}
|
|
}
|
|
```
|
|
*(vedi esempio con pool di risorse)*
|
|
|
|
## Producer and Consumer (produttore e consumatore) - Semaforo contatore
|
|
|
|
Si presenta qui una soluzione alternativa al problema del produttore e consumatore, che utilizza un semaforo contatore per gestire un buffer **circolare**;
|
|
Si assume 1 produttore e 1 consumatore.
|
|
|
|
### Inizializzazione
|
|
```
|
|
semaphore empty = N;
|
|
semaphore full = 0;
|
|
T buffer[N];
|
|
int head = 0;
|
|
int tail = 0;
|
|
```
|
|
N e' la dimensione del buffer, e empty e' inizializzato ad essa perche' tutte le celle del buffer sono vuote all'inizializzazione. *head* e *tail* sono due contatori, *tail* per il producer e *head* per il consumer, che indicano la cella occupata.
|
|
|
|
#### Producer (invio di dati)
|
|
```
|
|
public void send (T data) {
|
|
P (empty);
|
|
buffer[tail] = data;
|
|
tail = (tail++) % N;
|
|
V (full);
|
|
}
|
|
```
|
|
|
|
#### Consumer (ricezione di dati)
|
|
```
|
|
public T receive () {
|
|
P (full);
|
|
T data = buffer[head];
|
|
head = (head++) % N;
|
|
V (empty);
|
|
return data;
|
|
}
|
|
```
|
|
Si nota come il buffer circolare implichi gli incrementi di *head* e *tail* in modulo N.
|
|
|
|
### Dimostrazione di Correttezza
|
|
|
|
* Siano:
|
|
```
|
|
nd(t) = # inserimenti (depositi) da parte del produttore
|
|
ne(t) = # estrazioni (ricezioni) da parte del consumatore
|
|
```
|
|
* La precedente dimostrazione del problema producer-consumer aveva portato:
|
|
```
|
|
0 <= nd(t) - ne(t) <= 1
|
|
```
|
|
* Perche' non possono esserci piu' estrazioni che inserzioni, e le inserzioni devono essere al massimo 1 in piu' delle estrazioni.
|
|
|
|
* Da qui, utilizzando l'invariante semaforico:
|
|
```
|
|
NP (empty, t) <= NV (empty, t) + N
|
|
```
|
|
* il che rispecchia l'inizializzazione del problema.
|
|
|
|
* Per dimostrare la correttezza, supponiamo che esista un istante t in cui il producer deposita e il consumer estrae. Dobbiamo quindi dimostrare che **head != tail**, in quanto non e' possibile che producer e consumer lavorino sulla stessa cella del buffer.
|
|
|
|
* Siano:
|
|
```
|
|
p1(t) = # incrementi in coda (tail)
|
|
p2(t) = # incrementi in testa (head)
|
|
```
|
|
* Da questo, la tesi puo' essere riscritta come:
|
|
```
|
|
1 <= p1(t) - p2(t) <= N-1
|
|
```
|
|
* Supponendo che entrambi i processi debbano ancora uscire dalla sezione critica:
|
|
```
|
|
p1(t) = NV(full, t) = NP (empty, t) - 1
|
|
p2(t) = NV(empty, t) = NP (full, t) - 1
|
|
```
|
|
* Da cui si puo' utilizzare l'invariante semaforico per ottenere:
|
|
```
|
|
p2(t) = NP(full,t) - 1 <= NV(full, t) - 1 <= p1(t) - 1 <= NP(empty, t) - 2 <= NV(empty, t) - 2 <= NV(empty, t) - 2 + N <= p2(t) + N - 2
|
|
```
|
|
* Considerare solo i termini p1, p2:
|
|
```
|
|
p2(t) <= p1(t) - 1 <= p2 (t) + N - 2
|
|
```
|
|
* Possiamo sommare 1 a tutti i termini senza cambiare la validita' della disequazione:
|
|
```
|
|
p2(t) + 1 <= p1(t) <= p2(t) + N - 1
|
|
```
|
|
* Che, sottraendo a ogni termine p2(t), risulta:
|
|
```
|
|
1 <= p1(t) - p2(t) <= N - 1
|
|
```
|
|
* Che e' proprio la tesi di partenza della dimostrazione di correttezza. CVD
|