# Esercizi ## 5 filosofi *(slides per implementazione monitor)* ### Dimostrazione di correttezza E' necessario dimostrare la correttezza della seguente: ``` SUM(i from 0 to 4) fork[i] + 2*E = 10 ``` Dove E e' il **numero di filosofi che mangiano** (hanno fatto *takeforks* ma non *release*). * Analizzando *takeforks*, quando un processo esce, **aumenta E di 1**. Di fatto pero', diminuisco due *forks* e aumento di 1 E, di modo che l'equaglianza in tesi rimanga valida. * Analizzando *release*, questa si comporta in maniera simmetrica a *takeforks*: aumenta di 2 forks e diminuisce di 1 E, preservando l'eguaglianza in tesi. La funzione quindi **sveglia un altro processo**, che riprende da *takeforks*. Quando questo termina, viene eseguito un altro *signal* che sveglia un secondo processo. L'eguaglianza e' sempre valida, essendo che non ci sono condizioni in cui le funzioni creino uno stato invalido. ## Molecole d'acqua (monitor) Il problema e' stato trattato in precedenza con l'utilizzo dei semafori, e' necessario dimostrare la correttezza della soluzione monitor. *(vedi slides per implementazione)* L'invariante in tesi e': ``` 0 <= nH - 2*nO <= 2 ``` Che deve quindi essere sempre vera. * Definiamo `count(t) = nH(t) - 2*nO(t)` dove `t` e' un qualunque istante durante l'esecuzione del processo. * All'inizio, `count(0) = 0` * Dopo una chiamata di *stampaH*, ho incrementato di 1 `nH` e `count`, che quindi rimane valido. * Dopo una chiamata di *stampaO*, pongo `count` a 0 se `count == 2` e' valida. Questo significa: ``` nH(t) - 2*nO(t) = 2 nH(t) = 2*nO(t) + 2 ``` * Pertanto, porre `count = 0` e' corretto, in quanto sto considerando `nO = 0`. Il valore massimo di `count` sara' quindi 2, dato che ogni volta che raggiunge 2 viene resettato. L'eguaglianza e' quindi dimostrata, in quanto non ci sono condizioni in cui `count` sia invalido. # Programmazione Concorrente - Modello a Scambio di Messaggi I processori (fisici o virtuali) hanno una propria **memoria privata** e quindi utilizzano una **rete di comunicazione** per interagire (LAN, WAN, architettura monoprocessore con multiplexing CPU (e partizioni di memoria **fisse**)). ## Caratteristiche del sistema * Ogni processo puo' accedere **unicamente alle risorse allocate nella propria risorsa**. * Ogni risorsa del sistema **e' accessibile ad un solo processo (alla volta)**. * Se una risorsa e' necessaria a piu' processi, bisognera' definire un **processo server** come gestore della risorsa, che esegue le operazioni richieste dagli altri processi e ritorna i risultati. Questo modello **client-server** da luogo a una programmazione concorrente definita **centralizzata**, opposta alla programmazione concorrente **distribuita**. ## Modello a scambio di messaggi ### Aspetti caratterizzanti Il concetto fondamentale e' quello di **canale**, ossia collegamento **logico** tra due processi (**pipe**). Il **nucleo** del sistema (kernel, sistema di rete...) deve quindi fornire delle primitive di canali come mezzo di comunicazione tra processi. Il linguaggio di programmazione deve quindi fornire strutture ad alto livello per la gestione delle primitive. #### Parametri di un canale * Tipologia del canale (direzione flusso dati) * Sorgente e destinatario della comunicazione, nonche' la **designazione** del canale. A seconda del tipo di comunicazione (asimmetrica / simmetrica) puo' essere necessario conoscere solo il destinatario oppure entrambi. * Tipo di sincronizzazione adottata tra i processi. #### Tipi di canale * **link**: canale simmetrico (uno - uno). * **port**: canale asimmetrico (molti - uno). Questa e' utilizzata per la struttura **client-server**. Qui, una *port* e' di proprieta' di un processo **server**, che e' noto a vari processi **client**. Il server riceve i messaggi sulla port e li gestisce, fornendo il servizio richiesto. Molto usata a livello kernel (gestione di code, etc) * **mailbox**: canale asimmetrico (molti - molti) *(vedi slides per rappresentazione grafica)* #### Tipi di sincronizzazione * Comunicazione **asincrona** * Comunicazione **sincrona** (**rendez-vous tra processi**) * Comunicazione con **sincronizzazione estesa** (come precedente, ma forza la risposta del servizio richiesto). Questa e' la struttura delle cosiddette **procedure estese**. ### Primitive di Comunicazione * Dichiarazione di canale *asimmetrico molti - uno*: ``` port // ex: port int channel1; ``` * Invio: ``` send () to ; ``` * Ricezione: ``` receive() from ; ``` Nota: port identifica il canale, mentre **var e' una variabile dello stesso tipo di port**, che e' identificatore di una variabile a cui assegnare il processo. * Comandi con **guardia**: ``` (); ``` La guardia e' una **tuple** di espressione booleana / primitiva receive che viene valutata e puo' fornire diversi valori. #### Guardia: valori e funzionamento 1. **guardia fallita**: l'espressione booleana e' *false* 2. **guardia ritardata**: l'espressione booleana e' *vera*, ma bloccata (receive e' bloccante) 3. **guardia valida**: l'espressione booleana e' *vera* e la receive puo' essere eseguita L'esecuzione di una guardia e' strutturata con una struttura if/else in cui vengono valutate le guardie di tutti i rami, ese ci sono **piu' guardie valide**, ne viene eseguita una **scelta in maniera non deterministica**. Se **tutte le guardie valide sono ritardate**, il processo si sospende **in attesa che una delle guardie sia abilitata da un nuovo messaggio**. Se **non ci sono guardie valide / ritardate**, il proceso viene terminato (ma non viene considerato un errore, quindi puo' essere utilizzato similmente al *break*). *(vedi slides per esempi)* ### Primitive Asincrone Comunicazione asimmetrica: * **send non bloccante** * **receive bloccante** I canali sono definiti mediante il costrutto *port*. *(vedi slides per esempi)* *(vedi slides per comparison monitor / messaggi client-server)* #### Simulazione di un semaforo E' piu' potente la primitiva semaforica oppure un sistema a scambio di messaggi asimmetrico client / server? Il passaggio da semafori a scambio di messaggi e' una variante dello schema producer / consumer. Il contrario (scambio di messaggi -> semaforo), *illustrato sulle slides*, implica la **creazione di un processo utilizzando le primitive di scambio di messaggi.** Il processo `process semaphore` sara' quindi l'implementazione contenente due *port*: `port signal P` e `port signal V`, che saranno le uniche operazioni eseguibili sul semaforo. L'**atomicita'** di P e V, richiesta dalla definizione classica di semaforo, e' da discutere. * V e' sicuramente atomica, perche' **il semaforo e' l'unico ad avere accesso alle sue risorse** (non interrompibile). * P e' chiamata da un processo che effettua una richiesta, ed e' costituito da una decrementazione di `valore` e da una send. Il `valore`, come per V, e' unico al semaforo, e send non e' interrompibile, pertanto l'implementazione *sulle slides* e' valida.