La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

Sincronizzazione dei processi

Presentazioni simili


Presentazione sul tema: "Sincronizzazione dei processi"— Transcript della presentazione:

1 Sincronizzazione dei processi

2 Outline Il problema della sincronizzazione Sincronizzazione hardware
Semafori Monitor Problemi tipici di sincronizzazione Produttori/Consumatori Lettori/Scrittori 5 filosofi Stallo

3 Outline Il problema della sincronizzazione Sincronizzazione hardware
Semafori Monitor Problemi tipici di sincronizzazione Produttori/Consumatori Lettori/Scrittori 5 filosofi Stallo

4 Il problema della sincronizzazione
Nei Sistemi Operativi multi-programmati diversi processi o thread sono in esecuzione asincrona e possono condividere dati I processi cooperanti possono… ...condividere uno spazio logico di indirizzi, cioè codice e dati (thread) …oppure solo dati L’accesso concorrente a dati condivisi può condurre all’incoerenza dei dati nel momento in cui non si adottano politiche di sincronizzazione Per garantire la coerenza dei dati occorrono meccanismi che assicurano l’esecuzione ordinata dei processi cooperanti … Vediamo un esempio per capire meglio il problema

5 Il problema produttore/consumatore - 1
Consideriamo il problema del produttore e del consumatore dove: Un produttore genera degli oggetti Un consumatore preleva gli oggetti Gli oggetti vengono inseriti/prelevati in un buffer condiviso Il buffer permette di inserire al più DIM_BUFFER elementi Una variabile contatore condivisa viene incrementa ogni volta che si aggiunge un elemento nel buffer e si decrementa ogni volta si preleva un elemento dal buffer Dati condivisi: #define DIM_BUFFER 10 typedef struct { . . . } Elemento; Elemento buffer[DIM_BUFFER]; int contatore  0;

6 Produttore e cinsumatore
inserisci: indice della successiva posizione libera nel buffer Consumatore preleva : indice della prima posizione piena nel buffer while (true) { /* produce un elemento in appena_Prodotto */ while (contatore == DIM_BUFFER) ; /* non fa niente */ buffer[inserisci] = appena_Prodotto; inserisci = (inserisci + 1) % DIM_BUFFER; contatore++; } while (true) { while (contatore == 0) ; /* non fa niente */ da_Consumare = buffer[preleva]; preleva = (preleva - 1) % DIM_BUFFER; contatore--; }

7 Inconsistenza dei dati - 1
La variabile contatore è usata in modo concorrente dal produttore e dal consumatore e quindi il suo valore potrebbe essere inconsistente ad un certo punto Se le istruzioni contatore++ e contatore– sono tipicamente implementate in linguaggio macchina nel seguente modo L’esecuzione concorrente di queste istruzioni può portare alla seguente sequenza di istruzioni interleaved: … Che portano ad avere contatore pari a 4 (per il consumatore) e 6 (per il produttore)! registro1 := contatore registro1 := registro1 + 1 contatore : = registro1 registro2 := contatore registro2 := registro2 - 1 contatore : = registro2 T0: produttore esegue registro1 = contatore {registro1 = 5} T1: produttore esegue registro1 = registro {registro1 = 6} T2: consumatore esegue registro2 = contatore {registro2 = 5} T3: consumatore esegue registro2 = register2 – 1 {registro2 = 4} T4: produttore esegue contatore = registro1 {contatore = 6} T5: consumatore esegue contatore = registro2 {contatore = 4}

8 Il problema della sezione critica
Supponiamo di avere n processi {P0, P1,…,Pn} ognuno con una porzione di codice, definita sezione critica (o regione critica), in cui può modificare dati condivisi Ad esempio una variabile, un file, etc. L’esecuzione della sezione critica da parte dei processi deve essere mutuamente esclusiva nel tempo Un solo processo alla volta può trovarsi nella propria sezione critica Processi devono quindi cooperare: Ogni processo deve chiedere il permesso di accesso alla sezione critica, tramite una sezione d’ingresso (entry section) La sezione critica è seguita da una sezione d’uscita (exit section) Il rimanente codice è non critico Un processo avrà la seguente struttura while (true) { sezione di ingresso sezione critica sezione di uscita sezione non critica }

9 Soluzione al problema della sezione critica
Il problema della sezione critica si affronta progettando un protocollo che i processi possono usare per cooperare e che deve deve soddisfare i 3 requisiti: Mutua esclusione Se il processo Pi è in esecuzione nella sua sezione critica nessun altro processo può trovarsi nella propria sezione critica Ad esempio nessun processo può impossessarsi della stampante se è già utilizzata da un processo Progresso Se nessun processo è in esecuzione nella propria sezione critica ed esiste qualche processo che desidera accedervi, allora la selezione del processo che entrerà prossimamente nella propria sezione critica non può essere rimandata indefinitamente In altre parole, si deve permettere l’accesso Attesa limitata Deve esistere un limite al numero di volte che si consente ai processi di entrare nella propria sezione critica dal momento in cui un processo precedentemente ha richiesto di farlo In altre parole, un processo entrerà prima o poi in tempo finito nella propria sezione critica (politica fair per evitare la starvation)

10 Uso del lock Per una corretta gestione della sezione critica bisogna garantire mutua esclusione progresso attesa illimitata L’accesso alla sezione critica può essere risolto utilizzando uno strumento detto lock (lucchetto) Tutte le strutture dati condivise sono protette da uno o più lock Per accedere alle strutture dati è necessario acquisire il possesso del lock che verrà restituito all’uscita della sezione critica while (true) { acquisisce il lock sezione critica restituisce il lock sezione non critica }

11 Outline Il problema della sincronizzazione Sincronizzazione hardware
Semafori Monitor Problemi tipici di sincronizzazione Produttori/Consumatori Lettori/Scrittori 5 filosofi Stallo

12 Sincronizzazione hardware
Per poter implementare il concetto della lock esistono istruzioni implementate in hardware nelle moderne CPU, ad esempio: testAndSet(var) - testa e modifica il valore di una cella di memoria swap(var1, var2) - scambia il valore di due celle di memoria L’aspetto fondamentale di queste istruzioni macchina è che sono atomiche ovvero non possono essere interrotte da un cambio di contesto Due istruzione atomiche eseguite contemporaneamente saranno sempre eseguite completamente in modo sequenziale in un ordine arbitrario

13 TestAndSet Avendo definito la variabile globale che mantiene lo stato del lock come segue: boolean lock = false; L’istruzione testAndSet può essere definita nel seguente modo: Il significato è quello di impostare la variabile lock a true e restituire il suo precedente valore contenuto in valorePrecedente boolean testAndSet(boolean *lockVar) { boolean valorePrecedente = *lockVar; *lockVar = true; return valorePrecedente; }

14 Mutua esclusione con testAndSet
La mutua esclusione mediate testAndSet può essere realizzata attraverso una variabile booleana globale lock inizializzata a false nel seguente modo La testAndSet per poter risolvere il problema della sezione critica deve poter soddisfare i requisiti di: Mutua esclusione Progresso Attesa limitata Sono soddisfatti tutti e tre i requisiti? while (true) { while(testAndLock(&lock)) ; sezione critica lock = false; sezione non critica }

15 TestAndSet – Mutua Esclusione
while (true) { while(testAndLock(&lock)) ; sezione critica /*lock = true*/ lock = false; sezione non critica } Dati due processi P1 e P2 il primo dei due che esegue la testAndSet imposta lock a true ed entra nella sezione critica L’altro processo troverà lock a true e si metterà in attesa nel ciclo while

16 TestAndSet – Progresso
while (true) { while(testAndLock(&lock)) ; sezione critica /*lock = true*/ lock = false; sezione non critica } Un processo P dopo aver lasciato la sezione critica imposta lock a false permettendo ad un altro processo di entrare nella sezione critica

17 TestAndSet con attesa limitata
while (true) { attesa[i] = true; chiave = true; while (attesa[i] && chiave) chiave = testAndSet(&lock); attesa[i] = false; sezione critica j = (i + 1) % n; while ((j != i) && !attesa[i]) j = (j + 1) % n; if (j==i) lock = false; else attesa[j] = false; sezione non critica } Strutture dati condivise: lock e attesa[n] Mutua esclusione: Entra nella sezione critica quando chiave è false o attesa[i] è false Progresso: In uscita imposta lock=false a Attesa limitata: Scorre la lista e trova il primo interessato ad entrare Il processo che esce dalla propria sezione critica designa attraverso una coda circolare il prossimo processo che può entrare Qualsiasi processo che intende entrare nella sezione critica attenderà al più n-1 turni

18 Outline Il problema della sincronizzazione Sincronizzazione hardware
Semafori Monitor Problemi tipici di sincronizzazione Produttori/Consumatori Lettori/Scrittori 5 filosofi Stallo

19 Semafori Un semaforo S è una variabile intera utilizzata come strumento di sincronizzazione Le due operazioni atomiche possibili su S sono: wait(S) signal(S) wait(S) { while(S <= 0) ; //no-op S--; } signal(S) { S++; }

20 Esempi di uso del semaforo
Accesso a sezioni critiche Un semaforo può essere utilizzato per risolvere il problema dell’accesso alla sezione critica tra n processi mediante un semaforo comune mutex (mutual exclusion) inizializzato ad 1 Sincronizzazione di operazioni Supponiamo che il processo P1 debba compiere una operazione op1 prima dell’operazione op2 del processo P2. Quindi op2 sarà eseguita solo dopo che op1 termina Utilizziamo un semaforo mutex inizializzato a 0 while (true) { wait(mutex); sezione critica signal(mutex); sezione non critica } op1; signal(mutex); wait(mutex); op2;

21 Evitare il busy waiting dei semafori - 1
I processi che girano inutilmente mentre attendono un semaforo per il rilascio di una risorsa sono chiamati spin lock (girano sul lucchetto) Il busy waiting o attesa attiva costituisce un problema per i sistemi multiprogrammati, perché la condizione di attesa spreca cicli di CPU che altri processi potrebbero sfruttare produttivamente L’attesa attiva è vantaggiosa solo quando è molto breve perché, in questo caso, può evitare un context switch Per evitare il busy waiting possiamo modificare la definizione di wait in modo che: Il processo invoca una wait per ottenere una risorsa Se la risorsa è in uso, blocca se stesso inserendosi in una coda di attesa del semaforo e passando nello stato di attesa Lo scheduler della CPU può quindi selezionare un altro processo pronto per l’esecuzione Il processo sarà sbloccato da una signal da parte di qualche altro processo

22 Evitare il busy waiting dei semafori - 2
Per realizzare l’operazione di bloccaggio abbiamo bisogno di una struttura dati semaforo implementata nel seguente modo Il sistema operativo deve inoltre fornire due operazioni: block() – sospende il processo che la invoca wakeup(P) – pone in stato di pronto il processo P bloccato Le operazioni wait() e signal() saranno realizzate nel seguente modo: typedef struct { int valore; struct Processo *lista; } semaforo; wait(semaforo *S) { S->valore--; if(S->valore < 0) { aggiungi questo processo a S->lista block(); } signal(semaforo *S) { S->valore++; if(S->valore <= 0) { togli un processo P da S->lista wakeup(P); }

23 Outline Il problema della sincronizzazione Sincronizzazione hardware
Semafori Monitor Problemi tipici di sincronizzazione Produttori/Consumatori Lettori/Scrittori 5 filosofi Stallo

24 Monitors Realizza l’accesso in mutua esclusione ai dati che incapsula
Incapsula dati osservabili e modificabili solamente dall’ “interno” del monitor Costrutto linguistico Realizza l’accesso in mutua esclusione ai dati che incapsula una sola procedura d’accesso alla volta può essere attiva (1 processo alla volta)

25 Monitor Il monitor è un costrutto di sincronizzazione di alto livello che permette la condivisione sicura di un tipo astratto di dati fra processi concorrenti Incapsula i dati privati mettendo a disposizione metodi pubblici per operare su tali dati La rappresentazione di un tipo monitor è formata da: dichiarazioni di variabili i cui valori definiscono lo stato di un’istanza del tipo procedure o funzioni che realizzano le operazioni sul tipo stesso Supportati da Concurrent Pascal, C#, Java

26 Esempio di monitor in Java?
Variabili condivise il cui accesso deve avvenire in mutua esclusione class MyMonitor { private int dato1; private Vector dato 2; public void cambiaDato2() { synchronized(this) { } public int getDato1() {

27 Outline Il problema della sincronizzazione Sincronizzazione hardware
Semafori Monitor Problemi tipici di sincronizzazione Produttori/Consumatori Lettori/Scrittori 5 filosofi Stallo

28 Problemi tipici di sincronizzazione
Alcuni problemi tipici di sincronizzazione dove poter verificare schemi di sincronizzazione sono: Problema dei produttori e consumatori Problema dei lettori e degli scrittori Problema dei cinque filosofi

29 Problema dei produttori e consumatori
Il problema del produttore e del consumatore con memoria limitata può essere risolto utilizzando i semafori e le seguenti strutture dati buffer[DIM_BUFFER] buffer circolare di dimensione limitata Semafori piene, vuote, mutex piene inizializzato a 0, mantiene il numero degli elementi inseriti vuote inizializzato a n, mantiene il numero delle posizioni libere mutex inizializzato ad 1, sincronizza gli accessi al buffer Va in attesa se buffer è utilizzato (mutex = 0) Va in attesa se buffer è utilizzato (mutex = 0) Va in attesa se vuote è <= 0 Va in attesa se piene è <= 0 while(true) { produce un elemento in prod wait(vuote); wait(mutex); inserisce in buffer l’elemento prod signal(mutex); signal(piene); } while(true) { wait(piene); wait(mutex); rimuove un elemento da buffer e lo mette in prod signal(mutex); signal(piene); consuma l’elemento in prod } Produttore Consumatore

30 Problema dei lettori e degli scrittori
while(true) { wait(mutex); numLettori++; if(numLettori == 1) wait(scrittura); signal(mutex); esegue l’operazione di lettura numLettori--; if(numLettori == 0) signal(scrittura); } Lettore Un insieme di dati (ad es., un file) deve essere condiviso da più processi concorrenti che possono: Lettori: richiedere la sola lettura del contenuto… Scrittori: richiedere l’accesso ai dati sia in lettura che in scrittura I processi lettori possono accedere contemporaneamente al file Un processo scrittore deve accedere in mutua esclusione con tutti gli altri processi (sia lettori che scrittori) Strutture dati condivise: numLettori: Inizializzato a 0, mantiene il numero di processi che leggono i dati Semafori mutex ,scrittura mutex inizializzato a 1, gestisce l’accesso a numLettori scrittura inizializzato a 1, gestisce la mutua esclusione nella scrittura while(true) { wait(scrittura); esegue l’operazione di scrittura signal(scrittura); } Scrittore

31 Problema dei cinque filosofi
Il problema dei 5 filosofi è un classico problema di sincronizzazione sviluppato da Dijkstra Cinque filosofi passano la loro vita pensando e mangiando I filosofi siedono attorno ad un tavolo rotondo con 5 posti Un filosofo può essere nei seguenti stati: Pensa: non interagisce con niente e nessuno Mangia: tenta di prendere le bacchette alla sua destra ed alla sua sinistra Il filosofo può appropriarsi di una sola bacchetta alla volta Quando un filosofo affamato possiede due bacchette contemporaneamente, mangia; terminato il pasto, appoggia le bacchette e ricomincia a pensare Variabili condivise: La zuppiera di riso (l’insieme dei dati) Cinque semafori, bacchetta[5], inizializzati ad 1 La soluzione con i semafori non esclude la possibilità di deadlock I filosofi hanno fame contemporaneamente ed ognuno si impossessa della bacchetta alla propria sinistra while(true) { wait(bacchetta[i]); wait(bacchetta[(i+1) % 5]); mangia signal(bacchetta[i]); signal(bacchetta[(i+1) % 5]); pensa }

32 Stallo e attesa indefinita
La sincronizzazione può portare in generale ai seguenti problemi: Un processo attende indefinitamente il rilascio di una risorsa da parte di altri processi Questo genere di situazioni prendono il nome di stallo (deadlock) Un processo può non essere mai rimosso dalla coda associata al semaforo nella quale lui è sospeso Questo genere di situazioni prendono il nome di attesa indefinita (starvation)

33 Outline Il problema della sincronizzazione Sincronizzazione hardware
Semafori Monitor Problemi tipici di sincronizzazione Produttori/Consumatori Lettori/Scrittori 5 filosofi Stallo

34 Come risolvere il deadlock?

35 Condizioni Necessarie e Sufficienti per Generare uno Stato di Deadlock
acquisizione incrementale delle risorse divieto di rapina (no preemption) accesso in mutua esclusione alle risorse attese circolari

36 Accesso in mutua esclusione
Alcune risorse sono per natura non condivisibili quindi è difficile prevenire lo stallo negando la condizione di mutua esclusione …

37 Acquisizione incrementale
Acquisire tutte le risorse atomicamente acquisire le 2 forchette con una unica azione Rilascio risorse con timeout rilasciare una forchetta acquisita se trascorso un certo tempo non si riesce a progredire (starvation, livelock)

38 No Preemption È possibile “rubare” le risorse a processi che le hanno già acquisite prendere una forchetta già acquisita da un filosofo Rollback rilasciare tutte le forchette e ricominciare

39 Attese circolari Protocolli ad-hoc
Introdurre un ordine specifico di acquisizione delle risorse alcuni filosofi acquisiscono le forchette in ordine inverso Escludere alcuni processi dall’attività solamente 4 dei 5 filosofi possono sedersi al tavolo

40 Alcune soluzioni al problema dei filosofi
Se dopo un certo perido di tempo la bacchetta mancante non si libera, si rilascia qualla acquisita Viola acquisizione incrementale Un filosofo può prendere le bacchette solo se sono entrambe disponibili Alcuni filosofi prendono le bacchette con ordine inverso Viola attese circolari Solo quattro filosofi possono stare a tavola contemporaneamente

41 Deadlock e Sistemi Operativi
L’algoritmo dello struzzo speriamo non succeda! altrimenti kill process/restart OS Rare eccezioni Unix file locking system locking records in DBMS DEADLOCK AVOIDANCE


Scaricare ppt "Sincronizzazione dei processi"

Presentazioni simili


Annunci Google