Sincronizzazione dei processi

Slides:



Advertisements
Presentazioni simili
Meccanismi di IPC Problemi classici di IPC
Advertisements

INFORMATICA Algoritmi fondamentali
Capitolo 3 Risorse e Stallo 3.1. Risorse 3.2. Introduzione
Recupero debito quarto anno Primo incontro
Linguaggi a memoria condivisa Lidea è di aggiungere ad un linguaggio imperativo dei costrutti per rappresentare lesecuzione parallela di statements. Supponiamo.
Java: programmazione concorrente con condivisione di memoria
Algoritmi e Programmazione
1 Astrazioni sui dati : Specifica ed Implementazione di Tipi di Dato Astratti in Java.
Classi ed Oggetti in Java (Cenni). Richiami Ruolo delle Classi in Java Oggetti.
Liste di Interi Esercitazione. Liste Concatenate Tipo di dato utile per memorizzare sequenze di elementi di dimensioni variabile Definizione tipicamente.
Le gerarchie di tipi.
Informatica Generale Marzia Buscemi
1 Processi e Thread Meccanismi di IPC, Inter Process Communication (1)
1 Processi e Thread Meccanismi di IPC (1). 2 Comunicazioni fra processi/thread Processi/thread eseguiti concorrentemente hanno bisogno di interagire per.
Grafi Algoritmi e Strutture Dati. Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano Algoritmi e strutture dati 2/ed 2 Copyright © The McGraw.
Programmazione Concorrente
Realizzazione del file system
Silberschatz, Galvin and Gagne Operating System ConceptsDeadlock Modello del sistema Caratterizzazione dei deadlock Metodi per la gestione dei.
Sincronizzazione fra processi
Sincronizzazione di processi
Deadlock Modello del sistema Caratterizzazione dei deadlock
1 Istruzioni, algoritmi, linguaggi. 2 Algoritmo per il calcolo delle radici reali di unequazione di 2 o grado Data lequazione ax 2 +bx+c=0, quali sono.
Semantiche dei linguaggi di programmazione
2 luglio 2006URM2 – ING- OOP0304 OL G. Cantone e A. Lomartire 1 Programmazione Orientata agli Oggetti Processi, task e thread Java (ed esempi) Università
DIPARTIMENTO DI ELETTRONICA E INFORMAZIONE Puntatori Marco D. Santambrogio – Ver. aggiornata al 21 Marzo 2013.
Informatica di base A.A. 2003/2004 Algoritmi e programmi
1 Corso di Informatica (Programmazione) Lezione 11 (19 novembre 2008) Programmazione in Java: controllo del flusso (iterazione)
1 Corso di Informatica (Programmazione) Lezione 10 (12 novembre 2008) Programmazione in Java: espressioni booleane e controllo del flusso (selezione)
Corso di Informatica (Programmazione)
Corso di Laurea in Biotecnologie Informatica (Programmazione)
Risorse e Stallo.
Gestione dei processi Un sistema operativo multitasking è in grado di gestire più processi Un processo corrisponde ad un programma in esecuzione. Un programma.
Introduzione alla programmazione lll
Sincronizzazione dei Processi
Il Linguaggio Macchina
Approfondimento delle classi
memoria gestita staticamente:
Sincronizzazione fra thread
Sistemi Operativi GESTIONE DEI PROCESSI.
Strutture dei sistemi di calcolo Funzionamento di un sistema di calcolo Struttura di I/O Struttura della memoria Gerarchia delle memorie Architetture di.
1 Strutture Dinamiche Corso di Informatica A Vito Perrone.
Strutture di controllo in C -- Flow Chart --
Concorrenza e Sincronizzazione di Thread e Processi
INTRODUZIONE l sistema operativo è il primo software che lutente utilizza quando accende il computer; 1)Viene caricato nella memoria RAM con loperazione.
1 Programmazione = decomposizione basata su astrazioni (con riferimento a Java)
Esercizio 10.* Un cassiere vuole dare un resto di n centesimi di euro usando il minimo numero di monete. a) Descrivere un algoritmo goloso per fare ciò.
Interazione e sincronizzazione
CODIFICA Da flow-chart a C++.
I Metodi in Java Il termine "metodo" è sinonimo di "azione". Quindi, affinché un programma esegua qualche istruzione, deve contenere metodi.
Configurazione in ambiente Windows Ing. A. Stile – Ing. L. Marchesano – 1/23.
1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti dispense prof. G. Levi.
Capitolo 3 Strutture dati elementari Algoritmi e Strutture Dati Camil Demetrescu, Irene Finocchi, Giuseppe F. Italiano.
1 Lucidi delle esercitazioni di Sistemi di Elaborazione in Rete Università degli Studi della Calabria Corso di Laurea in Ingegneria Gestionale A.A. 2003/2004.
File system distribuito transazionale con replicazione
ISTITUTO STATALE DI ISTRUZIONE SUPERIORE F. ENRIQUES CORSO JAVA – PROVA INTERMEDIA DEL 12 MARZO 2007 NOME: COGNOME: ________________________________________________________________________________.
Processi.
Threads.
Costruzione di una semplice Agenda telefonica Elettronica Esercizio sull'uso delle principali system call Unix.
Fondamenti di Informatica 2 Ingegneria Informatica Docente: Giovanni Macchia a.a
1 Il Buffer Cache Unix (Bach: the Design of the Unix Operating System (cap: 3)
I processi.
1 Un esempio con iteratore: le liste ordinate di interi.
Introduzione a Javascript
Esercitazione su Vector. Permette di definire collezioni di dati generiche, che sono in grado di memorizzare elementi di ogni sottotipo di Object Definito.
Programmazione Concorrente e Distribuita
Architettura dei Sistemi Operativi
1 Processi e Thread Meccanismi di IPC (2) Problemi classici di IPC.
Liste di Interi Esercitazione. Una variante Liste concatenate di Integers Non modificabile Costruttori per creare la lista vuota o un nodo Metodi d’istanza.
Processi e Thread Processi Thread
1 Competizione e cooperazione pag. 88 Cap.3 - Esecuzione concorrente di processi 1.
Transcript della presentazione:

Sincronizzazione dei processi

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

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

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

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;

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--; }

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 = registro1 + 1 {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}

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 }

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)

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 }

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

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

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; }

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 }

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

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

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

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

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++; }

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;

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

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); }

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

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)

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

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() {

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

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

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

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

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 }

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)

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

Come risolvere il deadlock?

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

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

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)

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

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

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

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