Sommario
Più processi “competono” nell’uso di una risorsa.
Un gestore di una risorsa ha il compito di fornire un accesso sicuro e garantito alla risorsa che gestisce.
Tre problemi chiave nel controllo di accesso ad una risorsa:
Problema di competizione.
Due o più processi che vogliono utilizzare una risorsa ad uso esclusivo (come ad esempio una stampante), che chiameremo Risorsa Critica.
La porzione del codice che utilizza la risorsa è denominata Sezione Critica (la sezione critica può anche essere composta da più sezioni di codice appartenenti a più processi).
Indica la presenza di una condizione di attesa indefinita.
Esempio
Si dice che i due processi P1 e P2 sono in una situazione di stallo (deadlocked).
Indica quelle condizioni in cui ad un processo a bassa priorità non viene mai assegnata una risorsa.
In un sistema monoprocessore, al fine di garantire la mutua esclusione è sufficiente evitare che un processo, in una sezione critica, venga interrotto.
In un sistema multiprocessore, invece, la disabilitazione delle interruzioni in uno dei processori non garantisce la mutua esclusione.
L’approccio viola i principi di protezione: i processi utente dovrebbero poter disabilitare gli interrupt!!
Tuttavia è conveniente per il kernel quando deve aggiornare delle variabili condivise interne.
Soluzione basata su una variabile condivisa (lock) inizializzata a 0.
Quando un processo vuole entrare nella sezione critica, verifica il valore del lock:
Problema: più processi potrebbero accedere contemporaneamente alla sezione critica!
Un processo legge il lock, e riscontra il valore 0. Prima che possa posizionarlo ad 1, un altro processo è schedulato, legge il lock (ancora uguale a 0), lo posiziona ad 1 ed entra nella sezione critica. Quando il primo processo torna in esecuzione, posiziona anch’esso il lock a 1, ed entra nella sezione critica insieme al secondo processo!
Ciò è dovuto al fatto che la lettura e scrittura del lock possono essere eseguite in tempi diversi: sono divisibili.
Per risolvere via hardware il problema delle variabili lock, molti processori forniscono l’istruzione macchina:
TSL RX,LOCK
E’ eseguita in un solo ciclo: le operazioni di lettura e scrittura di LOCK sono quindi indivisibili.
La TSL inibisce l’accesso al bus di memoria durante la sua esecuzione: la soluzione è quindi utilizzabile anche nei sistemi multiprocessore.
La soluzione al problema della mutua esclusione è ora banale:
La soluzione è caratterizzata da condizioni di attesa attiva (busy wait), con relativo spreco di CPU.
Oltre al problema dell’attesa attiva, la soluzione con TSL presenta il problema della “priority inversion”.
Due processi accedono alla stessa sezione critica:
Lo scheduling è tale da assegnare la CPU ad H non appena è nello stato “pronto”.
Ad un certo istante, mentre L è nella sezione critica, H entra nello stato “pronto” (ad esempio, una op. di I/O completa).
H inizia il busy wait tramite TSL, ma siccome L non è mai schedulato mentre H è in esecuzione, L non lascerà mai la sezione critica: H rimane bloccato nel suo ciclo di attesa attiva per sempre.
I problemi di attesa attiva e di priority inversion vengono risolti “forzando” il processo che trova la variabile LOCK al valore 1 a sospendersi (transita nello stato bloccato) in attesa di un evento.
Utilizzo di due system call:
Variabili Speciali utilizzate per la cooperazione e la competizione tra processi.
Due o più processi possono competere o cooperare attraverso l’uso dei segnali, in modo tale che un processo può essere forzato a sospendersi in un determinato punto finché non riceve un segnale.
Sulle procedure di wait e signal sono disabilitate le interruzioni.
Un tipo di dato astratto s che incapsula:
Sono definite le seguenti operazioni:
Si utilizza un semaforo s (chiamato mutex), inizializzato a 1.
const int n = /* numero di processi */
semaphore mutex;
...
void P(int i) { /* il codice del processo i-mo*/
...
wait(mutex);
/* sezione critica */
signal(mutex);
...
}
...
int main() {
mutex.value = 1;
/* esecuzione concorrente di P(1), ..., P(n) */
}
Si hanno 4 processi concorrenti, denominati A, B, C e D.
Si supponga che l’esecuzione di A, B e C dipenda dall’esecuzione del processo D.
1) Inizialmente A è in esecuzione, B, C e D sono ready, il valore del semaforo è 1. A esegue una wait
4) D esegue una signal, che permette a B di essere inserito nella coda dei proc pronti. Si noti che in questo caso D non viene sospeso
5) D raggiunge la coda dei proc. pronti. C raggiunge il processore ed esegue una wait. C verrà sospeso. Anche A e B eseguono una wait, e per lo stesso motivo verranno sopesi
Problema di cooperazione.
Un processo “produttore” genera ciclicamente un messaggio e lo deposita in un buffer.
Un processo “consumatore” preleva dal buffer il messaggio e lo utilizza.
Vincoli:
Pur esistendo un problema di mutua esclusione nell’utilizzo del buffer comune.
La soluzione impone un ordinamento nelle operazioni dei due processi.
E’ necessario che produttori e consumatori si scambino segnali per indicare rispettivamente l’avvenuto deposito e prelievo.
Ciascuno dei due processi deve attendere, per completare la sua azione, l’arrivo del segnale dell’altro processo.
Due semafori
spazio_disponibile (v.i. 1)
messaggio_disponibile (v.i.0)
Processo Produttore
produttore{
do {
<produzione del nuovo messaggio>;
wait (spazio_ disponibile);
<deposito del messaggio nel buffer>;
signal (messaggio_ disponibile)
} while (!fine);
}
Processo Consumatore
consumatore{
do {
wait (messaggio_ disponibile);
<prelievo del messagio dal buffer>;
signal (spazio_ disponibile);
<consumo messaggio>;
} while (!fine);
}
1. Introduzione ai Sistemi Operativi
5. Scheduling nei sistemi mono-processore
6. Threads, SMP
8. Scheduling Multiprocessore e Real-Time
9. Gestione dei processi nei sistemi operativi Unix/Linux e Window...
10. Introduzione alla Programmazione Concorrente
11. Sincronizzazione nel modello ad ambiente globale
12. Problemi di cooperazione nel modello ad ambiente globale
14. Sincronizzazione nel modello ad ambiente locale
15. Deadlock
16. Programmazione Multithread
18. Memoria Virtuale
20. Il File System
21. Primitive di sincronizzazione nel kernel Linux
22. Esercitazione: System call per la gestione dei processi
23. Esercitazione: Inteprocess Communication e Shared Memory
24. Esercitazione: System Call per la gestione dei semafori in Linu...
25. Esercitazione: Problema dei Produttori e dei Consumatori
26. Posix Threads
P. Ancilotti, M.Boari, A. Ciampolini, G. Lipari, “Sistemi Operativi”, Mc-Graw-Hill (Cap. 3; par. 3.2, 3.3, 3.4)
A. Tanenbaum, “Operating Systems Desing and Implementation”, terza ed (Cap. 2, par. 2.2.3)