Reti e problematiche di Rete I Processi Concorrenti Enrico Cavalli Anno Accademico 2008-2009 I problemi con i processi concorrenti 1
I processi concorrenti La concorrenza tra processi avviene secon diverse modalità. Si parla infatti di competizione nell accesso alle risorse come nel caso di due amici che litigano per la restituzione di un prestito e di cooperazione nell evoluzione dei processi come nel caso della produzione dei celini paga degli stipendi eseguiti da due processi, uno dei quali effettua il calcolo della paga e l altro la stampa La cooperazione tra processi può avvenire con la condivisione di risorse, per esempio mediante la condivisione di un area della memoria centrale per processi concorrenti su un singolo calcolatore, oppure tramite comunicazione, con lo scambio di messaggi, come avviene ai processi che interagiscono in un sistema distribuito. La competizione nell accesso alle risorse può portare allo stallo dei processi mentre la cooperazione tra processi concorrenti può causare l inconsistenza dei dati elaborati. I problemi di concorrenza tra processi si presentano sia con i processi di sistema che con i processi degli utenti. La concorrenza è importante sia per la comprensione dei sistemi operativi sia nello sviluppo di applicazioni con linguaggi che supportano il multi threading. Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 3 I processi concorrenti P 0 P 1 Dati Clienti Dato Buffer Estratto Conto La produzione degli estratti conto con due processi cooperanti /* ------------------------------------------------------------------- * P 0 esegue P 1 esegue * ------------------------------------------------------------------- */ ProduceDatiCliente; LeggeDatiDaBuffer; ScriveDatiNelBuffer; StampaEstrattoConto; while (EsistonoClienti); while (EsistonoClienti); ProduceDatiCliente; eseguito da P 0 ScriveDatiNelBuffer; eseguito da P 0 LeggeDatiDaBuffer; eseguito da P 1 StampaEstrattoConto; eseguito da P 1 while (EsistonoClienti); Il comportamento desiderato Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 4 2
I processi concorrenti L elaborazione concorrente può portare a malfunzionamenti per: accesso concorrente alle informazioni e differente velocità di esecuzione dei processi Dati 0 Buffer Cliente B Cliente A NumeroConto Sal 5784 215.32 5783 3225.15 Dati1 Cliente A 5783 3225.15 typedef struct { int NumeroConto; uble Sal; } contocorrente; contocorrente Dati0, Dati1, Buffer; /*----------------------------------------------------------------- * Per effetto dell accesso concorrente può succedere che *----------------------------------------------------------------- */ Buffer.NumeroConto = Dati0.NumeroConto; eseguito da P 0 Dati1.NumeroConto = Buffer.NumeroConto; eseguito da P 1 Dati1.Sal = Buffer.Sal; eseguito da P 1 Buffer.Sal = Dati0.Sal; eseguito da P 0 Dati 0 Buffer Cliente B Cliente B NumeroConto Sal 5784 215.32 5784 215.32 Dati1 Cliente? 5784 3225.15 Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 5 I processi concorrenti La differente velocità di esecuzione dei processi può portare alla perdita di informazioni o alla duplicazione delle informazioni. Se P 0 è più veloce di P 1 può succedere che P 0 pone nel buffer i dati di C prima che P 1 ha letto quelli di B, che non saranno mai stampati. Se P 1 è più veloce di P 0, può accadere che P 1, po avere stampato i dati di A, acceda al buffer prima che P 0 lo abbia aggiornato con i dati di B. In tale caso P 1 stamperebbe i dati di A due volte. I due problemi principali della programmazione concorrente sono: - Accesso concorrente alle informazioni - Sincronizzazione dei processi Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 6 3
I processi concorrenti Per risolvere il problema dell accesso concorrente alle informazioni bisogna impedire che mentre P 0 modifica il buffer, P 1 lo acceda per leggerlo. In pratica questo significa che le due procedure ScriveDatiNelBuffer, LeggeDatiDaBuffer devono essere eseguite in mutua esclusione: Le porzioni di codice P 0, P 1,.., P n, sono eseguite in mutua esclusione se l esecuzione di P i può iniziare solo se nessuna P j, i j, è in esecuzione. Le attività dei due processi devono anche essere sincronizzate per evitare che un estratto conto non sia stampato o che sia stampato più volte: P 0 può scrivere nel buffer solo se il buffer è vuoto. In pratica: P 0 può scrivere nel buffer solo po che P 1 lo ha letto svuotanlo. P 1 può leggere il buffer solo se il buffer è pieno. In pratica: P 1 può leggere il buffer solamente po che P 0 ha scritto nel buffer riempienlo. Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 7 I processi concorrenti /* ---------------------------------------------------------------------- * Processo P 0 Processo P 1 * ---------------------------------------------------------------------- */ ProduceDatiCliente; AttendiBufferPieno; AttendiBufferVuoto; LeggeDatiDaBuffer; ScriveDatiNelBuffer; BufferVuoto; BufferPieno; StampaEstrattoConto; while(esistonoclienti); while(esistonoclienti); /* ------------------------------------------------------------- * Programma EstrattoConto * ------------------------------------------------------------- */ typedef enum {vuoto,pieno} stato; stato StatoBuffer; contocorrente Buffer; par P0(); P1(); par void P0(); void P1(); P StatoBuffer = vuoto; 0 e P 1 sono eseguite in parallelo par P0(); P1(); par Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 8 4
I processi concorrenti /* ------------------------------------------------------------------------ * Processo P 0 * ------------------------------------------------------------------------ */ void P0() ProduceDatiCliente; while (StatoBuffer == pieno) endwhile; attende: buffer == vuoto ScriveDatiNelBuffer; StatoBuffer = pieno; buffer vale pieno while (EsistonoClienti); /* ----------------------------------------------------------------------- Soluzione ottenuta con: * Processo P 1 - Attesa attiva * ----------------------------------------------------------------------- - Processi forzati alla stessa velocità */ void P1() while (StatoBuffer == vuoto) endwhile; attende: buffer == pieno LeggeDatiDaBuffer; StatoBuffer = vuoto; buffer vale vuoto StampaEstrattoConto; while (EsistonoClienti); Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 9 Accesso alle regioni critiche /* ---------------------------------------------------------------------- * Conteggio dei posti liberi * ---------------------------------------------------------------------- */ LOAD R1,Posti Posti = Posti - 1; SUB R1,UNO STORE R1,Posti Esecuzione concorrente di due processi che eseguono: Posti = Posti - 1; P 0 : Posti-- P 1 : Posti-- 50 Istruzioni Processo R1 Posti... 50 LOAD R1,posti P 0 50 50... -- schedulato P 1, salvato R1(50) LOAD R1,posti P 1 50 50 SUB R1,uno P 1 49 50 STORE R1,posti P 1 49 49... - riprende P 0, ripristinato R1(50) SUB R1,uno P 0 49 49 STORE R1,Posti P 0 49 49... Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 10 5
Corse critiche Per risolvere il problema delle corse critiche servono due istruzioni speciali: EntraSezioneCritica; EsciSezioneCritica; da inserire immediatamente prima e po la sezione critica che ne garantiscono l esecuzione in mutua esclusione. /* -------------------------------------------------------------------- * Processo P 0 Processo P 1 * -------------------------------------------------------------------- */ ArrivaSpettatore; ArrivaSpettatore; EntraSezioneCritica; EntraSezioneCritica; Posti = Posti - 1; Posti = Posti - 1; EsciSezioneCritica; EsciSezioneCritica; Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 11 Corse critiche /* ------------------------------------------------------------------------- * Programma ContaPostiLiberi * ------------------------------------------------------------------------- */ int Posti; typedef enum {P0, P1} turno; turno Processo; void P0(); void P1(); Posti = 400; Processo = P0; par P0(); P1(); par Soluzione corretta ma non accettabile operativamente perché... /* ------------------------------------------------------------------------- * Processo P 0 Processo P 1 * ------------------------------------------------------------------------- */ void P0() void P1() ArrivaSpettatore; ArrivaSpettatore; while(processo==p1) endwhile; while(processo==p0) endwhile; Posti = Posti - 1; Posti = Posti - 1; Processo = P1; Processo = P0; Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 12 6
La mutua esclusione in software La mutua esclusione in software Una buona soluzione al problema della mutua esclusione richiede che siano rispettate certe condizioni: - la mutua esclusione fra i processi deve essere garantita; - un processo fuori dalla sezione critica non deve impedire né ritardare l accesso alla sezione critica da parte degli altri processi che vi vogliono accedere; - non si possono fare ipotesi sulla velocità relativa dei diversi processi; - i processi rimangono nella loro sezione critica per un tempo limitato; - non ci deve essere stallo nè ritar indefinito. Consideriamo diversi approcci alla soluzione del problema della mutua esclusione Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 14 7
Mutua esclusione in software /* ----------------------------------------------------------------------- * * Programma MutuaEsclusione1 * * ----------------------------------------------------------------------- */ boolean Occupata; void P0(); void P1(); La mutua esclusione non è garantita Occupata = false; par P0(); P1(); par // ------------------------------------------------------------------------ void P0() void P1()...... while (Occupata) endwhile; while (Occupata) endwhile; Occupata = true; Occupata = true; <Sezione Critica> <Sezione Critica> Occupata = false; Occupata = false;...... Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 15 Mutua esclusione in software /* --------------------------------------------------------------------- * * Programma MutuaEsclusione2 * * --------------------------------------------------------------------- */ boolean VoglioEntrare[2]; void P0(); void P1(); C è pericolo di VoglioEntrare[0] = VoglioEntrare[1] = false; stallo par P0(); P1(); par //------------------------------------------------------------------------ void P0() void P1()...... VoglioEntrare[0] = true; VoglioEntrare[1] = true; while (VoglioEntrare[1]) while (VoglioEntrare[0]) endwhile; endwhile; <Sezione Critica> <Sezione Critica> VoglioEntrare[0] = false; VoglioEntrare[1] = false;...... Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 16 8
Mutua esclusione in software /* --------------------------------------------------------------------- * * Programma MutuaEsclusione3 * * --------------------------------------------------------------------- */ boolean VoglioEntrare[2]; void P0(); void P1(); C è pericolo di VoglioEntrare[0] = VoglioEntrare[1] = false; ritar indefinito par P0(); P1(); par //------------------------------------------------------------------------ void P0() void P1()...... VoglioEntrare[0] = true; VoglioEntrare[1] = true; while (VoglioEntrare[1]) while (VoglioEntrare[0]) VoglioEntrare[0]=false; VoglioEntrare[1]=false; AspettaCasuale(); AspettaCasuale(); VoglioEntrare[0]=true; VoglioEntrare[1]=true; endwhile; endwhile; <Sezione Critica> <Sezione Critica> VoglioEntrare[0] = false; VoglioEntrare[1] = false;...... Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 17 Mutua esclusione in software Nel 1965 l olandese Dekker ha proposto un algoritmo per risolvere via software il problema della mutua esclusione. L algoritmo di Dekker è piuttosto complicato e difficile da comprendere. Solo nel 1981 Peterson ha proposto una nuova soluzione molto più semplice di quella proposta da Dekker. Il fatto che ci siano voluti ben 16 anni per scoprire un nuovo semplice algoritmo per risolvere via software il problema della mutua esclusione lascia comprendere come siano difficili da risolvere i problemi che si incontrano con i processi concorrenti. L algoritmo di Peterson, che combina le idee di ContaPostiLiberi e MutuaEsclusione2 è, per così dire, un algoritmo basato sulla gentilezza perché, in caso di accesso contemporaneo alla sezione critica, entrambi i processi ceno il passo all altro. Si noti che anche l algoritmo di Peterson, semplice ed elegante, fa uso del ciclo di attesa attiva e, di conseguenza, non è molto efficiente. Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 18 9
Mutua esclusione in software /* ---------------------------------------------------------------------- * * AlgoritmoDiPeterson * * ---------------------------------------------------------------------- */ boolean VoglioEntrare[2]; Se P int Turno; 0 è nella propria sezione void P0(); void P1(); VoglioEntrare[0] = VoglioEntrare[1] = false; critica allora non Turno = 1; può esserlo P 1 par P0(); P1(); par //------------------------------------------------------------------------ void P0() void P1()...... VoglioEntrare[0] = true; VoglioEntrare[1] = true; Turno = 1; Turno = 0; while (VoglioEntrare[1] AND while (VoglioEntrare[0] AND Turno==1) endwhile; Turno==0) endwhile; <Sezione Critica> <Sezione Critica> VoglioEntrare[0] = false; VoglioEntrare[1] = false;...... Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 19 Supporti Hardware per la programmazione concorrente 10
Supporti hardware La ragione del fallimento della mutua esclusione nel programma MutuaEsclusione1 è vuto al fatto che le coppia di istruzioni: while (Occupata) endwhile; Occupata = true; non è eseguita in mo atomico. Se fosse possibile eseguire quella coppia di istruzione in mo atomico MutuaEsclusione1 funzionerebbe correttamente. Ci sono due modi per eseguire le due istruzioni atomicamente: - disabilitare le interruzioni prima di eseguire le istruzioni che devono essere eseguite in mo atomico e riabilitarle subito po. La tecnica della disabilitazione delle interruzioni è fortemente sconsigliata per tre ragioni: è pericoloso autorizzare un processo a disabilitare le interruzioni; il calcolatore funziona in mo inefficiente perché il sistema operativo non può attuare le proprie politiche di ottimizzazione; non funziona nei sistemi multi processore (e questo taglia la testa al toro) - usare istruzioni macchina (se ci sono) che controllano il valore di un dato e lo modificano nella medesima istruzione Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 21 L istruzione TSL In molti processori è presente l istruzione macchina TSL, acronimo di Test and Set Lock: TSL Registro, Blocco che trasferisce in un registro il contenuto della parola di memoria Blocco e, nella medesima istruzione, memorizza in Blocco un valore intero diverso da zero: il comportamento di TSL può essere descritto come quello della funzione TestSet che: restituisce il valore del proprio argomento X (1) e assegna a X il valore true (2) TestSet(X): true 2 1 X /* ---------------------------------------------------------------- * * TestSet * * ---------------------------------------------------------------- */ boolean TestSet(boolean &X) Boolean SalvaX; SalvaX = X; X = true; return SalvaX; X Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 22 11
Mutua esclusione con TSL /* ----------------------------------------------------------------------- * * MutuaEsclusioneTestSet * * ----------------------------------------------------------------------- */ boolean Blocco; Blocco = false; par P0(); P1(); par //------------------------------------------------------------------------ void P0() void P1()...... while (TestSet(Blocco)) endwhile; while (TestSet(Blocco)) endwhile; <Sezione Critica> <Sezione Critica> Blocco = false; Blocco = false;...... La soluzione è generalizzabile a N processi concorrenti e opera correttamente sia in ambiente mono che multi processore. Purtroppo usa un ciclo di attesa attiva: sarebbe meglio sospendere un processo e poi svegliarlo Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 23 Attesa / risveglio: I Semafori 12
I semafori I semafori sono variabili pensate per gestire i meccanismi di collaborazione tra processi concorrenti. Un processo può fermarsi in attesa di ricevere un segnale inviato da un altro processo. Per inviare e ricevere segnali un processo dispone delle due primitive Wait e Signal: Signal: invia un segnale a un semaforo che lo riceve e lo memorizza Wait: ricevere i segnali inviati a un semaforo: se il segnale non è stato inviato il processo è sospeso in attesa della trasmissione; se invece il segnale è già stato trasmesso il processo lo riceve e prosegue nella esecuzione. I semafori possono essere pensati come oggetti composti da una variabile intera e una coda di processi. Sono creati assegnan un valore opportuno alla variabile intera e possono essere manipolati con le sole primitive Wait e Signal che non sono interrompibili. typedef struct {int Count; CodaDiProcessi Coda;} semaforo; semaforo S (=1); S.Count=1 S.Coda è vuota La variabile intera S.Count conta i segnali inviati al semaforo S; la coda S.Coda è inizialmente vuota Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 25 I semafori S -3 S.Count = -3 Tre processi in S.Coda P2 P1 P0 Una Wait eseguita da un processo P sul semaforo S decrementa S.Count e, se il valore decrementato è minore di 0, blocca P e lo inserisce nella coda associata al semaforo S. L esecuzione di Signal su un semaforo S incrementa S.Count e, se S.Coda non è vuota, sveglia uno dei processi in attesa nella coda associata a S. Si dice che Wait consuma i segnali inviati a un semaforo, mentre Signal invia segnali a un semaforo. Count tiene conto del numero di segnali inviati a un semaforo e del loro utilizzo Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 26 13
I semafori /* ---------------------------------------------------------------------- * * Wait * * ---------------------------------------------------------------------- */ void Wait(semaforo &S) S.Count = S.Count - 1; if (S.Count < 0 ) InserisciProcesso(S.Coda); BloccaProcesso; endif; /* ---------------------------------------------------------------------- * * Signal * * ---------------------------------------------------------------------- */ void Signal(semaforo &S) processo P; if (S.Count < 0) P = EstraiProcesso(S.Coda); AttivaProcesso(P); endif; S.Count = S.Count + 1; Usa un segnale inviato al semaforo Se non ci sono segnali da usare, il processo P che ha attivato la Wait è fermato e inserito in S.Coda Se la coda non è vuota, attiva un processo Conta i segnali inviati al semaforo Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 27 I semafori P: Wait(S) P: Wait(S) 1 0-1 P Q: Signal(S) Q: Signal(S) La figura mostra che la variabile Count di un semaforo S: Per Count 0 indica il numero di segnali di sveglia che sono stati inviati a S con Signal(S) e non sono stati ancora ricevuti con Wait(S); Quan Count < 0 indica quanti sono i processi nella coda di S, in attesa di ricevere un segnale di sveglia. Con i semafori si risolvono i problemi di mutua esclusione e sincronizzazione Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 28 14
Mutua esclusione con i semafori L accesso in mutua esclusione a una porzione di codice si ottiene usan un semaforo S inizializato a 1 e facen precedere la regione critica con Wait(S) e segnalan l uscita dalla regione critica con Signal(S) /* ----------------------------------------------------------------- * * MutuaEsclusione con i semafori * * ----------------------------------------------------------------- */ Mutua Esclusione: il semaforo Mutex(Count=1); semaforo è inizializzato a 1 void P0(); void P1(); par P0(); P1(); par /* ----------------------------------------------------------------- */ void P0() void P1()...... Wait(Mutex); EntraCritica Wait (Mutex) Wait(Mutex); SezioneCritica; SezioneCritica; Signal(Mutex); EsciCritica Signal (Mutex) Signal(Mutex);...... Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 29 Mutua esclusione con i semafori Se P 0 è il primo processo che riesce a eseguire Wait(Mutex)... 1 P 0 0 Wait(Mutex) Se anche P 1 cerca di accedere alla sezione critica... 0 P 1 Wait(Mutex) -1 P 1 Quan P 0 lascia la sezione critica P 1 è svegliato e prosegue... -1 P 0 Signal(Mutex) P 1 0 Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 30 15
Sincronizzazione con i semafori Per sincronizzare due processi su un evento si usa un semaforo S inizializzato a 0. Il processo che rileva l evento esegue Signal(S) e l altro processo si sincronizza sull evento con Wait(S). /* ----------------------------------------------------------------- * * Sincronizzazione con i semafori * * ----------------------------------------------------------------- */ Sincronizzazione: il semaforo è semaforo S(Count=0); inizializzato a 0 void P0(); void P1(); S.Count= 0; par P0(); P1(); par /* ----------------------------------------------------------------- */ void P0() void P1() AzioniPreEvento; Wait(S); AzioniDopoEvento; Wait: per sincronizzarsi sull evento Signal: per rilevare l evento RilevaEvento; Signal(S); AltreOperazioni; Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 31 Implementazione dei semafori /* ----------------------------------------------------------------- * * Implementazione di Wait(S) * * ----------------------------------------------------------------- */ void Wait(semaforo &S) while (TestSet(Occupata)) endwhile; S.Count = S.Count 1; if (S.Count < 0) BloccaProcesso; InserisciProcesso(S.Coda); endif; Occupata = false; /* ----------------------------------------------------------------- * * Implementazione di Signal(S) * * ----------------------------------------------------------------- */ void Signal(semaforo &S) while (TestSet(Occupata)) endwhile; if (S.Count < 0) P = EstraiProcesso(S.Coda); AttivaProcesso(P); endif; S.Count = S.Count + 1; Occupata = false; boolean Occupata BloccaProcesso Realizzata richieden i servizi del sistema operativo per operare il cambiamento di stato del processo AttivaProcesso Realizzata richieden i servizi del sistema operativo per operare il cambiamento di stato del processo Realizzate nel nucleo. In alternativa... Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 32 16
Problemi caratteristici con i semafori Il problema del produttore consumatore Produttore Dati Consumatore Buffer il produttore deve essere sicuro di riempire un buffer vuoto; il consumatore deve essere sicuro di svuotare un buffer pieno. /* ----------------------------------------------------------------- * * Programma ProduttoreConsumatore * * ----------------------------------------------------------------- */ semaforo Prelevato(Count=1); semaforo Depositato(Count=0); string Buffer; void Produttore(); void Consumatore(); par Produttore; Consumatore; par Count = 1: Via libera al produttore Count = 0: Blocca il consumatore Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 34 17
Il problema del produttore consumatore /* ----------------------------------------------------------------- * Produttore: si sincronizza sull evento Prelevato e rileva * l evento Depositato * Consumatore: si sincronizza sull evento Depositato e rileva * l evento Prelevato * ----------------------------------------------------------------- */ Prelevato.Count = 1: Via libera al produttore Depositato.Count = 0: Blocca il consumatore void Produttore() string Dato; void Consumatore() string Dato; Produce(Dato); Wait( Depositato ); Wait( Prelevato ); Dato = Buffer; Buffer = Dato; Signal( Prelevato ); Signal( Depositato ); Consuma(Dato); while (CiSonoDati); while (CiSonoDati); Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 35 Produttore consumatore con un buffer circolare string Buffer[10]; Produttore NextIn=(NextIn+1)% N; NextIn 0 1 2 3 4 5 6 7 8 9 xxxxxx xxxxxx xxxxxx xxxxxx Buffer NextOut NextOut=(NextOut+1)% N; Consumatore /* ----------------------------------------------------------------- * * Programma BufferCircolare * * ----------------------------------------------------------------- */ const N =...; string Buffer[N]; int NextIn(=0), NextOut(=0); semaforo Pieno(Count=N) semaforo Vuoto(Count=0); void Produttore(); void Consumatore(); par Produttore(); Consumatore(); par Pieno permette di inserire N dati nel buffer prima di fermare il produttore Vuoto blocca subito il consumatore Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 36 18
Produttore consumatore con un buffer circolare Pieno.Count = N via libera N volte al produttore Vuoto.Count = 0 blocca subito il consumatore NextIn = NextOut = 0 /*----------------------------------------------------------------------------*/ void Produttore() void Consumatore() string Dato; string Dato; Produce(Dato); Wait(Vuoto); Wait(Pieno); Dato = Buffer[NextOut]; Buffer[NextIn] = Dato; NextOut = (NextOut+1) % N; NextIn = (NextIn+1) % N; Signal(Pieno); Signal(Vuoto); Consuma(Dato); while (CiSonoDati); while (CiSonoDati); Le corse critiche si possono avere solo se NextIn = NextOut cioè in caso di buffer vuoto oppure di buffer pieno. Supponiamo che il buffer sia pieno, per esempio perché il consumatore è fermo: allora Pieno.Count = 0 e il produttore non può accedere alla sezione critica. Se invece il buffer è vuoto si ha: Vuoto.Count = 0 e il consumatore si ferma prima di accedere alla sezione critica per effetto di Wait(Vuoto). Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 37 Produttore consumatore con un buffer circolare /* ------------------------------------------------------------------------ Attenzione: se ci sono più consumatori e/o più produttori * * Programma ProduttoriConsumatori l accesso al buffer e l aggiornamento dei puntatori devono * * ------------------------------------------------------------------------ avvenire in mutua esclusione. Si introduce il semafori Mutex */ inizializzato con Mutex.Count = 1 const N =...; string Buffer[N]; int NextIn, NextOut; semaforo Pieno(Count=N), Vuoto(Count=0), Mutex(Count=1); void Produttore(1); void Produttore(2);... void Consumatore(1);... par Produttore(1); Produttore(2);.. ; Consumatore(1); Consumatore(2);.. ; par /* ------------------------------------------------------------------------- */ void Produttore(i) void Consumatore(i) string Dato; string Dato; Produce(Dato); Wait(Vuoto); Wait(Pieno); Wait(Mutex); Wait(Mutex); Dato = Buffer[NextOut]; Buffer[NextIn] = Dato; NextOut = (NextOut+1)%N; NextIn = (NextIn+1)%N; Signal(Mutex); Signal(Mutex); Signal(Pieno); Signal(Vuoto); Consuma(Dato); while (CiSonoDati); while (CiSonoDati); Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 38 19
Il problema dei lettori e degli scrittori Scrittore Scrittore Scrittore Area Dati Lettore Lettore Un insieme di processi acceno a dati condivisi, un file o il record di un file, per esempio. I processi scrittori, inseriscono nuovi dati e/o modificano quelli esistenti, mentre i processi lettori si limitano a leggere i dati, senza modificarli. - La scrittura dei dati può essere fatta da un solo processo per volta - La lettura dei dati può essere effettuata da più lettori a condizione che non ci siano processi scrittori che stanno modifican i dati - Quan uno scrittore accede ai dati nessun lettore o scrittore può accedervi Sono regole abbastanza naturali e sono lo standard nell accesso ai dati di un database Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 39 Il problema dei lettori e degli scrittori /* ----------------------------------------------------------------- * * Programma LettoriScrittori * * ----------------------------------------------------------------- */ int Lettori; semaforo Scrivi(Count=1); semaforo Mutex(Count=1); Lettori = 0; par Lettore(); Lettore(); Scrittore(); par /* ---------------------------------------------------------------- */ void Lettore() Wait(Mutex); Lettori==1: il primo lettore Lettori++; if(lettori==1)wait(scrivi);endif; Signal(Mutex); LeggeDati; Wait(Mutex); Lettori==0: non ci sono più lettori Lettori--; if(lettori==0)signal(scrivi);endif; Signal(Mutex); Lettori: contatore dei lettori Scrivi: le operazioni di scrittura devono avvenire in mutua esclusione Mutex: mutua esclusione nei conteggi void Scrittore() Wait(Scrivi); ScriveDati; Signal(Scrivi); Priorità ai lettori Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 40 20
Il problema del barbiere che rme Il negozio, gestito da un solo barbiere, è tato di una poltrona da barbiere, ma ci sono un certo numero di sedie per i clienti in attesa. In assenza di clienti il barbiere si accomoda sulla sedia di lavoro e rme, diversamente il barbiere li serve in ordine di arrivo. I clienti entrano nel negozio e, se ci sono posti a sedere liberi si accomodano, diversamente lasciano immediatamente il negozio /* ---------------------------------------------------------------- * * Programma NegozioDelBarbiere * * ---------------------------------------------------------------- */ const MaxClienti =...; NumClienti: conta i clienti in attesa int NumClienti; Cliente: riconosce la presenza di clienti semaforo Mutex(Count=1); semaforo Barbiere(Count=1); Barbiere: rileva la disponibilità del barbiere semaforo Cliente(Count=0); Mutex: mutua esclusione conteggio clienti NumClienti = 0; par Barbiere(); Cliente(); Cliente();... Cliente(); par Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 41 Il problema del barbiere che rme void Cliente() EntraNelNegozio; Wait(Mutex); if (NumClienti < MaxClienti) NumClienti++; Se Signal(Mutex); Signal(Cliente); Wait(Barbiere); AccedeAlTaglio; else Signal(Mutex); endif; EsceDalNegozio; NumClienti MaxClienti il cliente lascia il negozio void Barbiere() AperturaNegozio; Wait(Cliente); Wait(Mutex); NumClienti--; Signal(Mutex); TaglioCapelli; Signal(Barbiere); Anche in questo problema c è una ppia sincronizzazione tra cliente e barbiere. Il cliente deve segnalare il proprio arrivo e attendere che il barbiere sia libero. Il barbiere deve segnalare la propria disponibilità e attendere che ci sia un cliente Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 42 21
Costrutti linguistici per la concorrenza Esigenza di costrutti del linguaggio semaforo Mutex(Count=1); int Posti(=0); /* -------------------------------------------------------------- */ void P(int J) Mutua Esclusione: Semaforo inizializzato a 1... Wait(Mutex); Wait: in ingresso alla sezione critica Posti = Posti + 1; Signal: in uscita dalla sezione critica Signal(Mutex);... Cosa succede se erroneamente: 1. Si inizializza Mutex a 0 2. Si omette Signal in uscita dalla sezione critica 3. Si omette Wait in ingresso alla sezione critica 4. Si scambiano Wait con Signal Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 44 22
I monitor Il monitor è un costrutto del linguaggio di programmazione con il quale si definisce un modulo software che contiene al proprio interno dati e procedure per manipolarli e codice per inizializzarli, oltre a speciali variabili dette variabili condizione. monitor NomeMonitor VariabiliDelMonitor; condition VariabiliCondizione; Variabili e Variabili Condizione del monitor /* ---------------------------------------------------------------------- */ void ProceduraMonitor1(.. )... void ProceduraMonitor2(.. )... Procedure di monitor... void ProceduraMonitorN(.. )... /* ---------------------------------------------------------------------- */ InizializzazioneVariabiliModulo; Inizializzazioni Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 45 I monitor Variabili e procedure di monitor sono caratterizzate da: - Le variabili del monitor sono accessibili solo attraverso le procedure di monitor - Le procedure di monitor sono eseguite in mutua esclusione - Una sola procedura può essere attiva in un dato momento - Un processo P è entrato nel monitor quan riesce a eseguire una procedura di monitor. - Se più processi cercano di entrare contemporaneamente nel monitor uno solo riesce ad accedere al monitor, l altro è inserito in una coda di attesa abbinata al monitor - Le variabili di condizione sono manipolabili solo con Wait(Variabile) per sospendere i processi su code abbinate alle variabili e Signal(Variabile) per riattivare i processi in attesa nella coda abbinata alla variabile - Per richiamare una procedura di monitor si usa la notazione: NomeMonitor.NomeProcedura(Parametri); I monitor sono stati implementati in alcuni linguaggi tra cui: Java, Concurrent C, Concorrent Pascal e Modula 2. Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 46 23
I monitor A, B sono in attesa su C 1 M Dati del monitor A B C 1 X, Y, Z sono in attesa su C 2 Q, R, S sono in attesa di entrare nel monitor X Y Z C 2 P è nel monitor Procedura 1 Q R S Procedura 2 Procedura 3 Inizializzazioni P Il monitor è un tipo di dato astratto con due particolari caratteristiche: la presenza delle variabili condizione e il fatto che le procedure di monitor sono eseguite in mutua esclusione. Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 47 Mutua esclusione con i monitor /* ----------------------------------* * definizione del monitor Posti * * ----------------------------------*/ monitor Posti PostiLiberi: int PostiLiberi; variabile privata di Posti /*-----------------------------*/ void AssegnaPosto(boolean &Ok) if (PostiLiberi > 0) PostiLiberi--; Ok = true; else Ok = false; AssegnaPosto: endif; procedura del monitor Posti /*-----------------------------*/ PostiLiberi = 350; Inizializzazioni del monitor /* -------------------------------- * * Il programma Prenotazioni * * -------------------------------- */ monitor Posti; void Cassa(); par Cassa();... ; Cassa(); par La mutua esclusione si ottiene /* -------------------------------- trasforman tutte le sezioni */ void Cassa() critiche in procedure di monitor boolean Eseguito; ArrivaSpettatore; Posti.AssegnaPosto(Eseguito); if (Eseguito)... else... endif; Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 48 24
Sincronizzazione con i monitor Per risolvere problemi di sincronizzazione si usano le variabili condizione e le speciali operazioni Wait e Signal nel seguente mo: si introduce E, variabile condizione abbinata all evento la procedura che rileva l evento lo segnala con l operazione Signal(E) la procedura che si deve sincronizzare sull evento usa Wait(E) per sospendersi in attesa dell evento. Le operazioni Wait e Signal dei monitor differiscono dalle omonime operazioni sui semafori per l assenza del meccanismo di conteggio dei segnali inviati con Signal: Wait ferma sempre il processo che la esegue Signal(E) cerca sempre di riattivare un processo nella coda associata alla variabile condizione E. Se la coda associata a E è vuota, il segnale viene perso. Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 49 Sincronizzazione con i monitor Produttore Produttore Dato Buffer Consumatore /* ----------------------------------------------------------------------- * * Il programma ProduttoreConsumatore * * ----------------------------------------------------------------------- */ monitor Buffer; void Produttore(); void Consumatore(); par Produttore(); Produttore(); Consumatore(); par /* ------------------------------------------------------------------- */ void Produttore() void Consumatore() string Dato; string Dato; Produce(Dato); Buffer.Preleva(Dato); Buffer.Inserisci(Dato); Usa(Dato); Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 50 25
Sincronizzazione con i monitor monitor Buffer string Serbatoio; boolean Pieno; condition Inserimenti, Prelievi; Inserisci: /* -------------------------------------------------------------- */ void Inserisci(string Dato) if (NOT Pieno) Serbatoio = Dato; Pieno = true; Signal(Prelievi); else Wait(Inserimenti); endif; /* -------------------------------------------------------------- */ void Preleva(String &Dato) if (Pieno) Dato = Serbatoio; Pieno = false; Signal(Inserimenti); else Wait(Prelievi); Preleva: endif; Se il buffer è pieno preleva un dato, diversamente fermati nella coda della /* -------------------------------------------------------------- condizione Prelievi */ Pieno = false; // monitor Buffer Se il buffer è vuoto deposita il dato diversamente fermati nella coda della condizione Inserimenti Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 51 Costrutti linguistici per sistemi distribuiti 26
Scambio di messaggi I problemi di concorrenza in ambienti distribuiti si possono risolvere mediante lo scambio di messaggi. Le primitive tipiche per realizzare lo scambio di messaggi sono Send e Receive: Send(Destinazione, Messaggio) Receive(Sorgente, Messaggio) Send invia un messaggio a un destinatario, mentre Receive serve a ricevere un messaggio da una determinata sorgente: P 0 invia un messaggio a un processo P 1, mentre P 1 riceve un messaggio da P 0. P 0 P 1 Send(P1,msg) Receive(P0,msg) Lo scambio di messaggi si può usare nei sistemi distribuiti e negli ambienti mono e multi processore a memoria condivisa. Il canale di comunicazione è una connessione fisica per i sistemi distribuiti, mentre è un area di memoria gestita dal sistema operativo per i sistemi a memoria condivisa. Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 53 Messaggi e Mailbox Destinatario Mittente Tipo Messaggio Priorità Lunghezza Messaggio Dati di Controllo Messaggio Si è fatto riferimento all indirizzamento diretto nel quale mittente e destinatario sono specificati esplicitamente nelle primitive di comunicazione. Un approccio alternativo è quello dell indirizzamento indiretto secon il quale i messaggi sono inviati a una struttura dati condivisa da mittente e destinatario detta mailbox (casella postale) o porta. Questo approccio è particolarmente utile nelle situazioni ve il ricevente deve accettare tutti i messaggi che gli sono stati inviati anche se non conosce i processi mittenti. Questo avviene, per esempio, nel caso di un database server che deve rispondere alle richieste di tutti i processi client. Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 54 27
Messaggi e Mailbox C 1 Send( Mailbox, Msg ) C 2 C 3 Receive( Mailbox, Msg ) S C 4 Mailbox I processi spediscono messaggi alle mailbox e li ricevono prelevanli dalla mailbox. La mailbox memorizza i messaggi in apposite code che sono gestite con diverse modalità, tenen conto, per esempio, della priorità dei messaggi inviati alla mailbox. Per esempio: quattro processi client richieno i servizi a un processo server S invian messaggi a una mailbox. Il processo server riceve i messaggi dalla mailbox, esamina le richieste e risponde in mo opportuno. Una mailbox contiene una quantità finita di messaggi: conveniamo che se un processo invia un messaggio a una mailbox piena si ferma in attesa che un processo rimuova un messaggio dalla mailbox. Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 55 Send e Receive Deve essere prevista una qualche forma di sincronizzazione nella spedizione e ricezione dei messaggi. Per farlo bisogna definire il comportamento di Send e Receive al momento dell esecuzione. La Send è bloccante se il processo che la esegue si blocca fino a quan il messaggio è stato ricevuto (e ne ha avuto conferma tramite una sorta di ricevuta di ritorno ). La primitiva Send è invece non bloccante se il processo, po avere spedito un messaggio, prosegue nella normale esecuzione. Anche la Receive può essere bloccante oppure non bloccante. Si pensi a un processo P che esegue Receive(Q, msg). Nel caso di Receive bloccante, il processo P si ferma in attesa di ricevere il messaggio inviato da Q. Nel caso di Receive non bloccante, si hanno due possibilità: se il messaggio è già stato inviato P lo riceve e prosegue mentre, se Q non ha inviato il messaggio, P prosegue senza ricevere il messaggio. In questo caso, in genere, P emette un messaggio di errore. Il caso Send bloccante con Receive bloccante stabilisce una forma di sincronizzazione stretta tra mittente e ricevente e si parla di rendez-vous (incontro in francese) tra i due processi che rimangono bloccati fino a trasmissione del messaggio avvenuta. Il caso Send non bloccante con Receive bloccante è molto diffuso, per esempio, nei processi server ve bisogna rispondere rapidamente alle richieste dei processi client. Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 56 28
Sincronizzazione con rendez-vous Nel caso di Send bloccante con Receive bloccante (rendez-vous) si stabilisce una forma di sincronizzazione stretta tra mittente e ricevente. In caso di rendez-vous due processi, per esempio un processo produttore e un processo consumatore, si possono sincronizzare nel seguente mo: /* ------------------------------------------------------------- * Sincronizzazione con Rendez-vous * ------------------------------------------------------------- */ void Produttore() void Consumatore() string Dato; string Dato; Produce(Dato); Receive(Produttore,Dato); Send(Consumatore,Dato); Consuma(Dato); Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 57 Mutua Esclusione con lo scambio di messaggi Mostriamo come risolvere i problemi base della programmazione concorrente nel caso di Send non bloccante, Receive bloccante e uso di mailbox. Mutua Esclusione con /* ---------------------------------------------------------------------- lo scambio di messaggi * * MutuaEsclusione con lo scambio di messaggi * * ---------------------------------------------------------------------- */ mailbox Casella; void P0(); void P1(); Mutua Esclusione: Invio di CreazioneMailbox(Casella); un messaggio nella mailbox Send(Casella, ); per iniziare par P0(); P1(); par /* ------------------------------------------------------------------ */ void P0() void P1() string Msg; string Msg; Receive(Casella,Msg); Receive(Casella,Msg); // SezioneCritica; EntraCritica Receive // SezioneCritica; Send(Casella,Msg); EsciCritica Send Send(Casella,Msg); AltroCodice; AltroCodice; Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 58 29
Programmazione concorrente con lo scambio di messaggi Soluzione dei problemi base della programmazione concorrente con lo scambio di messaggi: Send non bloccante, Receive bloccante e uso di mailbox. Mutua Esclusione con lo scambio di messaggi Per implementare la mutua esclusione con i messaggi si usa una mailbox che contiene un solo messaggio. La porzione di codice che deve essere eseguita in mutua esclusione deve essere delimitata in entrata con una Receive bloccante e in uscita da una Send non bloccante inviate a quella casella. Sincronizzazione con lo scambio di messaggi Per sincronizzare processi con lo scambio di messaggi si procede nel seguente mo: il processo che rivela l evento invia un messaggio con Send non bloccante a una mailbox vuota, mentre il processo che si deve sincronizzare sull evento esegue una Receive bloccante su quella mailbox Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 59 Sincronizzazione con lo scambio di messaggi Sincronizzazione con lo scambio di messaggi /* ---------------------------------------------------------------------- * * Sincronizzazione con lo scambio di messaggi * * ---------------------------------------------------------------------- */ mailbox Casella; void P0(); void P1(); CreazioneMailbox(Casella); par P0(); P1(); par /* ------------------------------------------------------------------ P0 rimane bloccato da */ Receive sino a quan P1 void P0() invia un messaggio void P1() string Msg(= ); string Msg(= ); AzioniPreEvento; RilevaEvento; Receive(Casella,Msg); Send(Casella,Msg); AzioniDopoEvento; AltreOperazioni; Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 60 30
Esempio: produttore - consumatore Doppia sincronizzazione /* ---------------------------------------------------------------------- * * Produttore-Consumatore con lo scambio di messaggi * * ---------------------------------------------------------------------- */ mailbox Consumato, Protto; string Msg; void Produttore(); void Consumatore(); CreaMailbox(Protto); CreaMailbox(Usato); Via libera al produttore Send(Usato, ); par Produttore(); Consumatore(); par Bloccato il consumatore /* ------------------------------------------------------------------- */ void Produttore() string Dato; Produce(Dato); Receive(Usato,Msg); Msg = Dato; Send(Protto,Msg); void Consumatore() string Dato; Send: Rileva evento Receive(Protto, Msg); Dato = Msg; Receive: Si sincronizza Send(Usato,Msg); sull evento Consuma(Dato); Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 61 Domande(1) 1. I processi P0 e P1 sono eseguiti in parallelo e cooperano come indicato di seguito: /*-------------------------------------------------------- * P0 esegue P1 esegue *-------------------------------------------------------- */ ProduceDati; EntraCritica; EntraCritica; LeggeBuffer; ScriveBuffer; EsceCritica; EsceCritica; StampaDati; L accesso in mutua esclusione al buffer è garantito? La sincronizzazione è garantita? Cosa può accadere se il processo P0 è più veloce del processo P1? Cosa può accadere se il processo P0 è più lento del processo P1? 2. Quali delle seguenti affermazioni sono vere (V) e quali sono false (F) - I semafori sono speciali variabili pensate per implementare il meccanismo attesa risveglio fra processi concorrenti - I semafori sono rappresentabili come un intero e una coda di processi - I semafori sono una struttura normalmente programmabile - La primitiva Wait decrementa la variabile intera del semaforo - Se il contatore di un semaforo vale -5 allora ci sono 4 processi in coda sul semaforo - Se il contatore di un semaforo è >= 0 non ci sono processi in coda su quel semaforo Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 62 31
Domande(2) 3. Il valore di Count del semaforo S è 4. Un processo P esegue per tre volte Wait(S) e subito po il processo Q esegue due Wait(S). Quali sono lo stato di P e di Q? 4. Il valore di Count del semaforo S è 0. Un processo P esegue Wait(S) e poco po il processo Q esegue Signal(S). Quali sono lo stato di P e di Q? 5. Il valore di Count del semaforo S è -2 e il processo P è in attesa nella coda associata ad S. Un processo Q esegue Signal(S). Quali sono lo stato di P e di Q? 6. I processi P e Q competono per accedere a una sezione critica secon il seguente codice che usa il semaforo Mutex inizializzato a 1. /* ------------------------------------------------------------------------- * P Q * ------------------------------------------------------------------------- */...... Wait(Mutex); Wait(Mutex); //SezioneCritica; //SezioneCritica; Signal(Mutex); Wait(Mutex);...... Commentate la soluzione. Commentate la soluzione nel caso di Mutex inizializzato a 0 ed a 2. Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 63 Domande(3) 7. Il processo P si deve sincronizzare con Q attraverso un evento rilevato da Q. Per farlo si usa un semaforo S e il seguente codice: /* --------------------------------------------------------------- * * P Q * * --------------------------------------------------------------- */...... AzioniPreEvento; RilevaEvento; Wait(S); Signal(S); AzioniDopoEvento; AltreOperazioni;...... Commentate la soluzione nel caso si S inizializzato a 0 ed a 1. 8. Nel programma ProduttoreConsumatore si usano: Depositato(Count=0) e Prelevato(Count=1): void Produttore() void Consumatore() string Dato; string Dato; Produce(Dato); Wait( Depositato ); Wait( Prelevato ); Dato = Buffer; Buffer = Dato; Signal( Prelevato ); Signal( Depositato ); Consuma(Dato); while (CiSonoDati); while (CiSonoDati); Commentate la soluzione. Commentate la soluzione nel caso che i due semafori siano inizializzati come Depositato(Count=1) e Prelevato(Count=0). Nel programma ProduttoreConsumatore a cosa serve Signal(Prelevato)? Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 64 32
Domande(4) 9. Nel monitor M ci sono due procedure P e Q. Due processi eseguono contemporaneamente M.P e M.Q. Cosa succede? 10. Due processi P e Q si vogliono sincronizzare su un evento E rilevato da P usan la primitiva Send non bloccante, Receive bloccante e una mailbox di nome Casella. Per farlo: a. - P esegue Send(Casella, msg) Q esegue Send(Casella, msg) b. - P esegue Send(Casella, msg) Q esegue Receive(Casella, msg) c. - P esegue Receive(Casella, msg) Q esegue Receive(Casella, msg) d. - P esegue Receive(Casella, msg) Q esegue Send(Casella, msg) 11. Due processi P e Q devono accedere in mutua esclusione a una regione critica usan la primitiva Send non bloccante e Receive bloccante e una mailbox di nome Casella che contiene un solo messaggio. Per farlo entrambi i processi circondano la sezione critica: a. - Con Receive(Casella, msg) prima della regione critica Send(Casella, msg) po b. - Con Receive(Casella, msg) prima della regione critica Receive(Casella, msg) po c. - Con Send(Casella, msg) prima della regione critica Receive(Casella, msg) po d. - Con Send(Casella, msg) prima della regione critica Send(Casella, msg) po Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 65 Esercizi(1) 1. I processi P0 e P1, eseguiti in parallelo, sono composti da istruzioni che consideriamo indivisibili. /*-------------------------------------------------------- * P0 esegue P1 esegue *-------------------------------------------------------- */ A = A + 1; A = A * 3; A = A - 1; Se il valore iniziale di A è 3 quali sono i possibili valori di A po l esecuzione concorrente dei due processi P0 e P1 2. Modificare il programma ContaPostiLiberi (diapositiva 12) in mo che possa funzionare con più casse. Naturalmente la soluzione sarà soggetta alle medesime limitazioni del programma originale. 3. Nel programma ContaPostiLiberi si vogliono contare il numero di posti ancora liberi e quello dei posti occupati. Per farlo si usano due variabili Liberi e Occupati che devono essere aggiornate dai processi P0 e P1. Si progettino due possibili soluzioni al problema usan le speciali istruzioni EntraCritica ed EsciCritica. Una delle due soluzioni deve garantire che in ogni momento sia rispettato il vincolo di integrità: Liberi+Occupati = 500 e l altra che non garantisca questa condizione. 4. Mostrate come sincronizzare due processi con l istruzione TSL Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 66 33
Esercizi(2) 5. Si mostri come risolvere, con i semafori, il problema dell accesso a due regioni critiche RCX ed RCY da parte dei due processi concorrenti P e Q che interagiscono nel seguente mo: /*----------------------------------------------------------------- * P Q *----------------------------------------------------------------- */ Azioni1_P; Azioni1_Q; RCX_P; RCY_Q; Azioni2_P; Azioni2_Q; RCY_P; RCX_Q; Azioni3_P; Azioni3_Q; La regione RCX indica una sezione critica nella quale si accede alla variabile condivisa X mentre RCY indica una sezione critica nella quale si accede alla variabile condivisa Y 6. In alcuni processori è disponibile un istruzione macchina che scambia il valore di un dato in memoria con quello di un registro. L istruzione di scambio può essere definita formalmente come: void Scambia(boolean &X, boolean &Y) boolean Temp; Temp = X; X = Y; Y = Temp; Mostrate come risolvere il problema della mutua esclusione con l istruzione Scambia Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 67 Esercizi(3) 7. I semafori binari sono semafori nei quali la variabile Count può assumere i soli valori 0 ed 1 e le operazioni Wait e Signal per i semafori binari sono definite da: /* ------------------------------------------------------------------------- * * Wait per i semafori binari * * ------------------------------------------------------------------------- */ void Wait(semaforoBinario &S) if ( S.Count == 1 ) S.Count = 0; else InserisciProcesso(S.Coda); BloccaProcesso; endif; /* ------------------------------------------------------------------------- * * Signal per i semafori binari * * ------------------------------------------------------------------------- */ void Signal(semaforoBinario &S) processo P; if ( S.Coda è vuota ) S.Count = 1; else P = EstraiProcesso(S.Coda); AttivaProcesso(P); endif; Descrivete con le vostre parole il comportamento di un semaforo binario e mostrate come implementarlo usan i semafori come sono stati definiti nel corso. Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 68 34
Esercizi(4) 8. Dopo aver controllato come implementare le procedure Wait e Signal con con TestSet mostrate come realizzarle con Scambia 9. Risolvete l problema presentato nell esercizio 3 con i semafori 10. Risolvere il problema trattato in EstrattoConto (diapositive 8 e 9) con i semafori 11. Risolvere il problema trattato in ContaPostiLiberi (diapositiva 12) con i semafori 12. Dopo aver controllato come implementare le procedure Wait e Signal con con TestSet mostrate come realizzarle sfruttan le interruzioni 13. Dite per quale ragione è ragionevole implementare i semafori con le interruzioni e in quale situazione è invece impossibile usare le interruzioni per implementare i semafori. 14. Dite se è possibile realizzare Wait e Signal in software e nel caso riteniate sia possibile mostrare come farlo. 15. Un ponte sul quale si può viaggiare in un solo senso per volta, attraversa un fiume in direzione nordsud. Implementare con i semafori la gestione dell accesso al ponte secon le seguenti regole: il passaggio da nord a sud è possibile se sul ponte non ci sono veicoli che viaggiano in senso opposto; il passaggio da sud a nord è possibile solo se il ponte è libero e non ci sono veicoli in attesa nell altro senso. (Suggerimento: controllate accuratamente il testo e confrontatelo con quello del problema LettoriScrittori) Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 69 Esercizi(5) 16. Un ponte molto breve, sul quale passa una sola automobile per volta, attraversa un fiume in direzione nord-sud. Implementare con i semafori la gestione dell accesso al ponte secon le seguenti regole: il passaggio da nord a sud è possibile se il ponte è libero; il passaggio da sud a nord è possibile solo se il ponte è libero e non ci sono veicoli in attesa nell altro senso. In poche parole: senso unico alternato con precedenza ai veicoli da nord a sud. 17. Si consideri un ascensore che trasporta i visitatori a una torre. I passeggeri acceno all ascensore, in gra di trasportare 50 passeggeri per volta, passan attraverso due tornelli. Raggiunto il numero massimo di visitatori trasportabili i tornelli si bloccano e l ascensore parte. Risolvete il problema di controllare i tornelli e l ascensore supponen un flusso continuo di visitatori 18. Risolvete con i semafori il problema di un processo produttore e un processo consumatore che interagiscono, usan due buffer, secon lo schema: /*----------------------------------------------------------------- * P0 esegue P1 esegue *----------------------------------------------------------------- */ ProduceDati1; LeggeBuffer1; ScriveBuffer1; UsaDati1; ProduceDati2; LeggeBuffer2; ScriveBuffer1; UsaDati1; Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 70 35
Esercizi(6) 19. Il problema dei filosofi a tavola ha la seguente formulazione: Ci sono 5 filosofi che vivono assieme e passano il loro tempo pensan o mangian. Per cibarsi si accomodano a un tavolo circolare apparecchiato con cinque piatti ripieni di spaghetti. Di fianco a ogni piatto c è una forchetta per magiare la pasta. Quan un filosofo vuole mangiare prende la forchetta alla destra e quella alla sinistra del piatto e mangia. Gestire la situazione con i semafori. Ci sono problemi di mutua esclusione perché la forchetta alla sinistra di un piatto è anche la forchetta alla destra di un altro e di conseguenza due filosofi che si sieno uno di fianco all altro non possono mangiare contemporaneamente. semaforo Forchetta[5](Count=1); void Filosofo(int J) Pensa; Wait(Forchetta[J]); Wait(Forchetta([(J+1)%5]); PrendePosate; Mangia; DepositaPosate; Signal(Forchetta([(J+1)%5]); Signal(Forchetta[J]); La procedura in figura mostra il comportamento di uno dei filosofi in una possibile soluzione che usa un array di semafori inzializzati a 1. Studiate la soluzione è spiegate che non è accettabile perché può portare allo stallo. Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 71 Esercizi(7) 20. C è una soluzione al problema dei filosofi a tavola che evita lo stallo: un filosofo prima di impugnare le forchette controlla che siano entrambe disponibili e poi le prende e mangia. Lo schema di soluzione è il seguente: semaforo Mutex(Count = 1); void Filosofo(int J) Pensa; Wait(Mutex); if (ci sono le posate) PrendePosate; Mangia; DepositaPosate; endif Signal(Mutex); Scrivete il codice completo della soluzione. Per controllare l esistenza delle posate servirà un array di flag per sapere se una posata è libera o in uso. Spiegare perché con la soluzione proposta c è un solo filosofo alla volta che mangia e c è il pericolo di ritar indefinito. 21. Affinate la soluzione proposta nel precedente esercizio in mo che ci sia più di un filosofo che sta mangian. Per farlo osservate che la mutua esclusione deve riguardare solo il momento nel quale si decide di mangiare e si impugnano le posate e non quan si mangia. Anche in questa soluzione c è il rischio di ritar indefinito. 22. Risolvere il problema discusso in EstrattoConto (diapositive 8-9) con i monitor. Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 72 36
Esercizi(8) 23. Risolvere il problema dei Lettori/Scrittori (diapositiva 40) con i monitor. 24. Risolvere il problema della salita in ascensore dell esercizio 17 con i monitor. 25. Risolvere il problema del produttore/consumatore con un buffer circolare usan i monitor. 26. Risolvere il problema discusso in EstrattoConto (diapositive 8-9) con lo scambio di messaggi 27. Risolvere il problema di un processo produttore e un processo consumatore che interagiscono, usan due buffer, secon lo schema dell esercizio 18, con lo scambio di messaggi. 28. Risolvere, mediante lo scambio di messaggi, il problema di un processo produttore e un processo consumatore che si scambiano dati per mezzo di un buffer in gra di memorizzare sino a 20 oggetti protti. Reti e problematiche di rete - Enrico Cavalli - Università di Bergamo 73 37