Esercitazione [9] Riepilogo sui Semafori Leonardo Aniello - [email protected] Daniele Cono D'Elia - [email protected] Sistemi di Calcolo - Secondo modulo (SC2) Programmazione dei Sistemi di Calcolo Multi-Nodo Corso di Laurea in Ingegneria Informatica e Automatica A.A. 2014-2015 Sommario • Soluzione esercizio sul Logger • Soluzione esercizio sull’EchoProcess su FIFO • Riepilogo semafori o Sezione critica o Limite sugli accessi concorrenti o Produttore/Consumatore Singolo Produttore/Singolo Consumatore Più Produttori/Più Consumatori • Esercizio sul Produttore/Consumatore • Esercizio sul limite sugli accessi concorrenti Soluzione Esercizio sul Logger • Nel main() - processo Server o Dopo la chiamata pipe() va chiuso Il descrittore della socket nel figlio I descrittori del file di log e di lettura da pipe nel padre o Dopo il while(1), va chiuso il descrittore di scrittura su pipe Per «pulizia», anche se il codice non viene mai eseguito!!! • Nello startLogger() - processo Logger o Chiudere il descrittore di scrittura su pipe o Lettura da pipe con gestione casi particolari La pipe è stata chiusa break La lettura è stata interrotta da un segnale continue Si è verificato un altro errore exit o All’uscita, chiudere il descrittore di lettura da pipe Soluzione Esercizio sull’EchoProcess su FIFO • Creazione FIFO tramite mkfifo() • Apertura FIFO tramite open() o Il server apre fifo_client in lettura ed fifo_echo in scrittura o Il client apre fifo_client in scrittura ed fifo_echo in lettura o Entrambi seguono lo stesso ordine di apertura, pena un deadlock! • Scrittura su FIFO o La dimensione del messaggio è nota, quindi la write() va gestita finché non sono stati scritti tutti i byte o Solita gestione di interruzioni ed errori • Lettura da FIFO o La dimensione del messaggio non è nota, quindi ha senso gestire letture parziali (cioè dei soli dati disponibili in quel momento) o Solita gestione di interruzioni ed errori Riepilogo Semafori • Inizializzazione: assegna un valore non negativo o Unnamed semaphore: funzione sem_init() o Named semaphore: funzione sem_open() • semWait: decrementa il valore, se è negativo il thread viene messo in attesa, altrimenti va avanti o Funzione sem_wait() • semSignal: incrementa il valore, se non è positivo un thread viene risvegliato o Funzione sem_post() • Chiusura: sem_destroy(unnamed), sem_close(named) o Per i named semaphore, va invocata alla fine la sem_unlink() nel processo responsabile della sua rimozione Uso dei semafori per implementare una sezione critica • Una sezione critica è una porzione di codice che deve essere eseguita in mutua esclusione o Non devono esserci più thread che eseguono quella porzione contemporaneamente • Implementazione o Il semaforo va inizializzato a 1 o sem_wait all’inizio della sezione critica o sem_post alla fine della sezione critica Uso dei semafori per imporre un limite all’accesso concorrente • Si vuole mettere un limite al numero di thread che eseguono in contemporanea una certa porzione di codice o Non devono esserci più di N thread che eseguono quella porzione contemporaneamente • Implementazione o Il semaforo va inizializzato a N o sem_wait all’inizio della porzione di codice o sem_post alla fine della porzione di codice Produttore/Consumatore • Thread produttori devono inserire delle entry nel buffer o Puntatore w_idx, inizializzato a 0 • Thread consumatori devono togliere delle entry dal buffer o Puntatore r_idx , inizializzato a 0 • Un consumatore non può togliere entry da un buffer vuoto, deve aspettare • Un produttore non può inserire entry in un buffer pieno, deve aspettare • Un consumatore può togliere una entry alla volta • Un produttore può inserire una entry alla volta • Una entry può essere tolta da un solo consumatore • Una entry può essere inserita da un solo produttore Singolo Produttore/Singolo Consumatore • Un semaforo per contare le posizioni occupate nel buffer, fill_count, inizializzato a 0 • Un semaforo per contare le posizioni libere nel buffer, empty_count, inizializzato a N (dimensione massima del buffer) Produttore sem_wait(empty_count) Consumatore sem_wait(fill_count) buffer[w_idx] = entry w_idx = (w_idx + 1) mod N entry = buffer[r_idx] r_idx = (r_idx + 1) mod N sem_post(fill_count) sem_post(empty_count) Più Produttori/Più Consumatori • L’inserimento di una entry è una sezione critica da proteggere con un semaforo w_mutex • La rimozione di una entry è una sezione critica da proteggere con un semaforo r_mutex Produttore sem_wait(empty_count) sem_wait(w_mutex) buffer[w_idx] = entry w_idx = (w_idx + 1) mod N sem_post(w_mutex) sem_post(fill_count) Consumatore sem_wait(fill_count) sem_wait(r_mutex) entry = buffer[r_idx] r_idx = (r_idx + 1) mod N sem_post(r_mutex) sem_post(empty_count) Esercizio EchoServer multi-thread con Logger • Nell’EchoServer viene lanciato un thread Logger che «vive» nella funzione logger() e che si occupa di scrivere messaggi di log su file • Nei thread che gestiscono le connessioni client, viene usata una funzione log() per «produrre» messaggi di log • Questi messaggi di log vengono «consumati» dal thread Logger • In ottica produttore/consumatore o Ci sono più produttori (thread che gestiscono le connessioni client) ed un singolo consumatore (thread Logger) o Il buffer contiene messaggi di log • Esercizio: completare il codice dell’EchoServer o È anche disponibile un client multi-thread che può effettuare un cospicuo numero di richieste parallele così da far generare al server molti messaggi di log in concorrenza Esercizio EchoServer multi-process con limite al # di connessioni gestite in parallelo • Nella pratica, un server non può gestire un numero illimitato di connessioni simultaneamente (e.g., HTTP 503 Server unavailable) • Possibile approccio: semaforo per limite all’accesso concorrente • Scenario: server multi-process, limite MAX_CONCURRENCY o Un semaforo anonimo richiederebbe uso di shared memory o Un semaforo named può essere acceduto tra processi tra loro «related» usando lo stesso puntatore, poiché è un oggetto IPC speciale allocato su shared memory dal sistema operativo stesso! • Esercizio: estendere il codice dell’EchoServer multi-process – Nei commenti /* ===> HINT: insert code here <=== */ vi viene suggerito dove estendere il programma, ma non in che modo… – Esercizio proposto: estendere la versione multi-thread (cosa cambia?)