Laboratorio di Sistemi Operativi Parte 7: Problematiche e side-effects nell'utilizzo di meccanismi di sincronizzazione Dott. Roberto Iannone Email: iannone@crmpa.unisa.it 1 Problematiche e side-effects 2 Introduzione Deadlock Starvation Inversione di priorità Introduzione Non è tutt'oro quel che luccica Se da un lato i meccanismi di sincronizzazione che abbiamo visto consentono di risolvere alcuni problemi spinosi (non determinismo ad esempio) dall'altro possono introdurre dei sideeffects (effetti collaterali) ancora peggiori! Situazioni di stallo: gruppi di processi restano bloccati in modo indistricabile. Attese infinite (o comunque molto lunghe): uno o più processi attendono per la schedulazione per un tempo indefinito. Se le situazioni nelle quali si verificano questi problemi riguardano i processi o i thread di gestione di un sistema operativo, il risultato può essere catastrofico! 3 Introduzione « Ingorgo a croce uncinata » Situazione comune di traffico a Napoli nel film di Luciano De Crescenzo Così Parlò Bellavista (1984). Il giro della morte (Deadlock) è una delle situazioni più pericolose che si possono presentare nella comunicazione tra processi. Situazione di traffico, purtroppo reale. Una situazione si stallo che può verificarsi quando più processi vengono bloccati in una situazione di attesa circolare. Nei sistemi Real-time, ad esempio quelli che comandano una centrale nucleare, queste situazioni sono da evitare assolutamente 4 Introduzione Una lunga attesa... Successe al MIT nel 1973 quando fu necessario spegnere una macchina IBM. Si trovò un processo che era rimasto in attesa dal 1967! Questi problemi possono verificarsi in sistemi che utilizzano scheduling a priorità senza aging. Il tristo mietitore Linux utilizza uno scheduling a priorità con aging. Quindi processi che “invecchiano” passano durante la loro vita per le varie code di priorità dello scheduler. 5 Deadlock Deadlock: veloce riepilogo 6 Si verifica un deadlock quando ogni processo in un insieme di processi è in attesa di un evento che solo un altro processo dell'insieme è in grado di generare (Prof. Andrew Tanenbaum). Per verificarsi un deadlock devono verificarsi tutte le seguenti condizioni (Coffman 1971): Mutua esclusione: ogni risorsa è associata esattamente ad un processo o è disponibile. Hold and wait: ogni processo che detiene attualmente una risorsa presto si metterà in attesa per un'altra risorsa. No preemption: una risorsa detenuta da un processo non potrà essere assegnata ad un altro forzatamente. Si dovrà attendere che il processo la rilasci. Attesa circolare: deve esistere una catena circolare di due o più processi dove ogni processo nella catena aspetta una risorsa detenuta dal prossimo membro della catena. Deadlock Il ”dilemma” dei filosofi a cena I 5 filosofi a cena è un classico problema didattico per mostrare una situazione di sincronizzazione tra processi (o thread) che può portare ad una situazione di deadlock. L'esempio fu descritto nel 1965 da Edsger Dijkstra, che se ne servì per esporre un problema di sincronizzazione: cinque computers competono per l'accesso a 5 tape drive (nastri) Esempio con le forchette: impensabile mangiare spaghetti con due forchette! 7 Deadlock Descrizione del problema con le bacchette Intorno ad una tavola circolare di un ristorante cinese, sono riuniti 5 dei più grandi filosofi nel nostro tempo. I filosofi (un po' spilorci) hanno deciso di mangiare riso in un grosso piatto al centro della tavola. Il ristorante però si trova sprovvisto di bacchette ce ne sono solo 5 invece delle 10 necessarie (spilorcio anche il ristorante!) per far mangiare i filosofi. La cena si svolge come segue: 1) Durante la cena i filosofi non fanno altro che mangiare e pensare. 2) Di volta in volta, un filosofo diventa affamato e cerca di prendere prima una bacchetta alla sua destra e poi quella alla sua sinistra per mangiare. 3) Ogni filosofo può prendere solo una bacchetta alla volta ed ha bisogno di entrambe per poter mangiare. 4) Quando un filosofo è affamato ed ha le due bacchette, mangia senza rilasciare le bacchette. 5) Quando un filosofo ha finito di mangiare rilascia le bacchette e si mette a pensare e il ciclo ricomincia. Tutti i filosofi devono mangiare almeno una volta! 8 Deadlock 5philosopher.c 1/4 #include <pthread.h> #include <stdio.h> #include <stdlib.h> #define PHILOSOPHERS 5 #define ITERATION 500 /* bacchette (simulate dai mutex) */ pthread_mutex_t chopstick[PHILOSOPHERS]; int eating[PHILOSOPHERS]; /* stampa messaggio di errore e termina */ void error(char *f){ perror(f); exit(1); } Utilizziamo un mutex per ogni bacchetta. ITERATION rappresenta il numero di iterazioni che esegue ogni filosofo prima di terminare la sua esecuzione. 9 Deadlock 5philosopher.c 2/4 /* filosofo pensa */ void waiting(int min, int max){ sleep(rand()%(max-min+1) + min); } /* filosofo id prende la bacchetta destra */ void gotRightChopstick(int id){ if (pthread_mutex_lock(&chopstick[id])) error("pthread_mutex_lock"); printf("sono il filosofo #%d: prendo la bacchetta destra\n", id + 1); } /* filosofo id prende la bacchetta sinistra */ void gotLeftChopstick(int id){ if (pthread_mutex_lock(&chopstick[(id+1) % PHILOSOPHERS])) error("pthread_mutex_lock"); printf("sono il filosofo #%d: prendo la bacchetta sinistra\n", id + 1); } /* filosofo id lascia la bacchetta destra */ void leftRightChopstick(int id){ if (pthread_mutex_unlock(&chopstick[id])) error("pthread_mutex_unlock"); printf("sono il filosofo #%d: lascio la bacchetta destra\n", id + 1); } /* filosofo id lascia la bacchetta sinistra */ void leftLeftChopstick(int id){ if (pthread_mutex_unlock(&chopstick[(id+1) % PHILOSOPHERS])) error("pthread_mutex_unlock"); printf("sono il filosofo #%d: lascio la bacchetta sinistra\n", id + 1); } 10 Deadlock 5philosopher.c 3/4 /* routine che esegue ogni filosofo */ void *philosopherRoutine(void *idp){ int id=*(int *)idp, i; eating[id] = 0; /* reset eating counter */ for (i=0; i<ITERATION; i++) { printf("sono il filosofo #%d ho fame!\n", id + 1); waiting(0,0); gotRightChopstick(id); waiting(0,0); gotLeftChopstick(id); printf("sono il fiosofo #%d: sto mangiando\n", id + 1); eating[id]++; waiting(0,0); /* rilascio delle bacchette */ leftRightChopstick(id); leftLeftChopstick(id); } return NULL; } 11 Deadlock 5philosopher.c 4/4 int main(int argc, char *argv[]){ int i; struct { int id; pthread_t thread_id; } philosopher[PHILOSOPHERS]; srand(time(NULL)); / inizializza il generatore di numeri casuai */ /* crea i mutex */ for (i=0; i<PHILOSOPHERS; i++) if (pthread_mutex_init(&chopstick[i], NULL)) error("pthread_mutex_init"); /* esegue i filosofi */ for (i=0; i<PHILOSOPHERS; i++){ philosopher[i].id=i; if (pthread_create(&philosopher[i].thread_id, NULL, philosopherRoutine, &philosopher[i].id)) error("pthread_create"); } /* join e report dei risultati */ for (i=0; i<PHILOSOPHERS; i++) if (pthread_join(philosopher[i].thread_id, NULL)) error("pthread_join"); printf ("\n\nrisultati:\n\n"); for (i=0; i<PHILOSOPHERS; i++){ if (eating[i] > 0) printf ("- il filosofo %d ha mangiato %d volte\n", i + 1, eating[i]); else printf ("- il filosofo %d è morto di fame!\n", i + 1); } return 0; } 12 Deadlock Problemi possibili Deadlock: se i filosofi vengono schedulati in modo da prendere tutti una bacchetta nello stesso momento, non potranno più proseguire dal momento che tutti aspetteranno che si liberi l'altra bacchetta per continuare (attesa circolare). Starvation: trebbe accadere (in base alle politiche di scheduling) che il filosofo P(i) tra P(i-1 % 5) e P(i+1 % 5) sia più lento degli altri due (ad esempio pensa di più). In tal caso il filosofo P(i) potrebbe morire di inedia non mangiando mai! 13 Deadlock Come evitare il deadlock: soluzione 1 Introdurre una asimmetria nella scelta dell'ordine di acquisizione delle bacchette. Ad esempio numerando i filosofi in modo crescente: Filosofi con numero pari, prendono prima la bacchetta destra e poi la sinistra. Filosofi con numero dispari, prendono prima la bacchetta sinistra e poi la destra. Questo consente di eliminare una delle condizioni fondamentali per avere il deadlock: l'attesa circolare. 14 Deadlock 5philosopher_2.c 1/5 #include <pthread.h> #include <stdio.h> #include <stdlib.h> #define PHILOSOPHERS 5 #define TEST_SLEEP 0 /* struttura che contiene i dati dei filosofi */ typedef struct { int id; pthread_t thread_id; int eat; int sched; int rstick; int lstick; } philosopher; philosopher philosophers[PHILOSOPHERS]; int no_deadlock = 0; /* se 1 imposta la versione senza deadlock */ /* bacchette (simulate dai mutex) */ pthread_mutex_t chopstick[PHILOSOPHERS]; /* stampa un messaggio di errore e termina */ void error(char *f){ perror(f); exit(1); } 15 philosopher: Struttura per mantenere lo stato di ogni filosofo. no_deadlock: flag per specificare se utilizzare la versione con (0) o senza deadlock (1). TEST_SLEEP: indica ogni quanti secondi controllare lo stato di deadlock. Deadlock 5philosopher_2.c 2/5 /* filosofo pensa */ void waiting(int min, int max){ sleep(rand()%(max-min+1) + min); } /* filosofo id prende la bacchetta destra */ void gotRightChopstick(int id){ if (pthread_mutex_lock(&chopstick[id])) error("pthread_mutex_lock"); philosophers[id].rstick = 1; } /* filosofo id prende la bacchetta sinistra */ void gotLeftChopstick(int id){ if (pthread_mutex_lock(&chopstick[(id+1) % PHILOSOPHERS])) error("pthread_mutex_lock"); philosophers[id].lstick = 1; } /* filosofo id lascia la bacchetta destra */ void leftRightChopstick(int id){ if (pthread_mutex_unlock(&chopstick[id])) error("pthread_mutex_unlock"); philosophers[id].rstick = 0; } /* filosofo id lascia la bacchetta sinistra */ void leftLeftChopstick(int id){ if (pthread_mutex_unlock(&chopstick[(id+1) % PHILOSOPHERS])) error("pthread_mutex_unlock"); philosophers[id].lstick = 0; } 16 Deadlock 5philosopher_2.c 4/5 int main(int argc, char *argv[]){ int i, rstick_count, opt; /* parse degli argomeni */ no_deadlock = 0; while ((opt = getopt(argc, argv, "d")) != -1){ switch (opt){ case 'd': no_deadlock = 1; break; default: error("bad usage!\n"); } } /* inizializza dati e strutture */ srand(time(NULL)); for (i=0; i<PHILOSOPHERS; i++){ philosophers[i].id = i; philosophers[i].eat = 0; philosophers[i].rstick = 0; philosophers[i].lstick = 0; philosophers[i].sched = 0; if (pthread_mutex_init(&chopstick[i], NULL)) error("pthread_mutex_init"); } La funzione getopt esegue il parse degli argomenti passati sulla riga di comando. 18 5philosopher_2.c 5/5 Deadlock /* esegue i filosofi */ for (i=0; i<PHILOSOPHERS; i++){ if (pthread_create(&philosophers[i].thread_id, NULL, philosopherRoutine, &philosophers[i].id)) error("pthread_create"); } /* cliclo infinito */ while(1){ rstick_count = 0; sleep(TEST_SLEEP); for (i = 0; i < PHILOSOPHERS; i++) printf ("p%d[%d,%d]:%d/%d\t", philosophers[i].id, philosophers[i].lstick, philosophers[i].rstick, philosophers[i].eat, philosophers[i].sched); printf("\n"); /* controllo deadlock */ for (i = 0; i < PHILOSOPHERS; i++){ rstick_count += philosophers[i].rstick; } if (rstick_count == PHILOSOPHERS){ /* tutti i filosofi hanno preso una bacchetta ... nessuno mangerà più */ printf("Si è verificato un deadlock!\n"); for(i = 0; i < PHILOSOPHERS; i++) pthread_cancel(philosophers[i].thread_id); for (i = 0; i < PHILOSOPHERS; i++) printf ("p%d[%d,%d]:%d/%d\t", philosophers[i].id, philosophers[i].lstick, philosophers[i].rstick, philosophers[i].eat, philosophers[i].sched); printf("\n"); exit(0); } } return 0; } 19 Deadlock Confronto con e senza deadlock Con deadlock. L'esecuzione viene bloccata dal controllo deadlock. p0[1,1]:1/11175 p1[0,0]:0/4744 p0[1,1]:1/11211 p1[0,0]:0/4744 p0[0,0]:1/11247 p1[0,0]:0/4744 p0[1,1]:0/11284 p1[0,0]:0/4744 p0[1,1]:1/11320 p1[0,0]:0/4744 p0[0,0]:1/11356 p1[0,0]:0/4744 p0[0,0]:1/11392 p1[0,0]:0/4744 Si è verificato un deadlock! p0[0,1]:0/11422 p1[0,1]:0/5369 p2[0,0]:0/1966 p2[0,0]:0/1966 p2[0,0]:0/1966 p2[0,0]:0/1966 p2[0,0]:0/1966 p2[0,0]:0/1966 p2[0,0]:0/1966 p3[1,1]:1/14772 p3[1,1]:1/14772 p3[1,1]:1/14772 p3[1,1]:1/14772 p3[1,1]:1/14772 p3[1,1]:1/14772 p3[1,1]:1/14772 p4[0,0]:0/127 p4[0,0]:0/127 p4[0,0]:0/127 p4[0,0]:0/127 p4[0,0]:0/127 p4[0,0]:0/127 p4[0,0]:0/127 p2[0,1]:0/1990 p3[0,1]:0/34596 p4[0,1]:0/127 Senza deadlock l'esecuzione continuerà per sempre. p0[1,1]:0/1608928 p0[0,0]:0/1608964 p0[0,0]:0/1608999 p0[1,1]:1/1609034 p0[0,0]:1/1609070 p0[1,1]:1/1609106 p0[1,1]:1/1609141 p0[0,0]:0/1609176 p0[1,1]:1/1609212 p0[0,0]:0/1609247 p0[1,1]:1/1609283 20 p1[1,0]:0/900764 p1[1,0]:0/900764 p1[1,0]:0/900764 p1[1,0]:0/900764 p1[1,0]:0/900764 p1[1,0]:0/900764 p1[1,0]:0/900764 p1[1,0]:0/900764 p1[1,0]:0/900764 p1[1,0]:0/900764 p1[1,0]:0/900764 p2[0,0]:0/685177 p2[0,0]:0/685177 p2[0,0]:0/685177 p2[0,0]:0/685177 p2[0,0]:0/685177 p2[0,0]:0/685177 p2[0,0]:0/685177 p2[0,0]:0/685177 p2[0,0]:0/685177 p2[0,0]:0/685177 p2[0,0]:0/685177 p3[0,0]:0/453588 p3[0,0]:0/453588 p3[0,0]:0/453588 p3[0,0]:0/453588 p3[0,0]:0/453588 p3[0,0]:0/453588 p3[0,0]:0/453588 p3[0,0]:0/453588 p3[0,0]:0/453588 p3[0,0]:0/453588 p3[0,0]:0/453588 p4[0,1]:0/493861 p4[0,1]:0/493861 p4[0,1]:0/493861 p4[0,1]:0/493861 p4[0,1]:0/493861 p4[0,1]:0/493861 p4[0,1]:0/493861 p4[0,1]:0/493861 p4[0,1]:0/493861 p4[0,1]:0/493861 p4[0,1]:0/493861 Deadlock Come evitare il deadlock: soluzione 2 Dato che i filosofi non sono in grado di pensare a se stessi, la presenza di un ”cameriere” può aiutarli. Il cameriere consiglia i filosofi prima di prendere le bacchette. Quando un filosofo ha fame chiede al cameriere se le bacchette sono libere e in tal caso le acquisisce. Quando ha finito rilascia le bacchette e il cameriere può avvertire gli altri filosofi in attesa. 21 Deadlock Linux e i deadlock Domanda: linux utilizza qualche contromisura per individuare e/o prevenire i deadlock ? Risposta: No! Troppo costoso in termini di overhead Pulse: A Dynamic Deadlock Detection Mechanism Using Speculative Execution (Tong e altr. 2005). Un progetto implementato in una versione del kernel 2.6 di linux per l'individuazione dei deadlock basata su esecuzione preventiva dei processi (attualmente non adottata in Linux). Pulse gira come un daemon e periodicamente controlla i processi che sono in stato di sleep da diverso tempo (ad esempio aspettano eventi di I/O). Per determinare se i processi sono in deadlock, Pulse esegue in modo seculativo i processi per scoprirne le dipendenze, genera un grafo delle dipendenze delle risorse e cerca cicli in questo grafo, se li trova allora i processi sono in deadlock. 22 Starvation Starvation con i 5 filosofi Consideriamo il seguente scenario: Entrambe P0 e P3 vengono schedulati per mangiare. P1, P2 e P4 cercano di mangiare ma devono attendere. P0 e P3 terminano quasi nello stesso tempo, ma lo scheduler sceglie P1 e P2 quando i mutex vengono rilasciati. Ora P0 e P3 cercano di mangiare ma devono aspettare P1 e P2. P1 e P2 completano e lo scheduler,incurante delle necessità di P4, sceglie di eseguire P0 e P3 e il ciclo si ripete. P4 non verrà mai schedulato e quindi andrà in starvation. Questo comportamento non può verificarsi in Linux in base alle politiche di scheduling che vengono adottate dal kernel. Linux garantisce che nessun processo vada mai in starvation. 23 Inversione di priorità What really happened on Mars ? Mike Jones <mbj@MICROSOFT.com> Sunday, December 07, 1997 6:47 PM The Mars Pathfinder mission was widely proclaimed as "flawless" in the early days after its July 4th, 1997 landing on the Martian surface […] But a few days into the mission, not long after Pathfinder started gathering meteorological data, the spacecraft began experiencing total system resets, each resulting in losses of data. This week at the IEEE Real-Time Systems Symposium I heard a fascinating keynote address by David Wilner, Chief Technical Officer of Wind River Systems. Wind River makes VxWorks, the real-time embedded systems kernel that was used in the Mars Pathfinder mission. In his talk, he explained in detail the actual software problems that caused the total system resets of the Pathfinder spacecraft, how they were diagnosed, and how they were solved. I wanted to share his story with each of you. VxWorks provides preemptive priority scheduling of threads. Tasks on the Pathfinder spacecraft were executed as threads with priorities that were assigned in the usual manner reflecting the relative urgency of these tasks [...] 24 Inversione di priorità What really happened on Mars ? ASI/MET: modulo che si occupa dei sensori per il recupero dei dati atmosferici e metereologici. cruiser/lander: gestisce le funzionalità di navigazione ed atterraggio della navicella. Pathfinder contiene un "information bus" (1553 bus) che può essere visto come una memoria condivisa usata per passare informazioni tra le varie parti della sonda. Un task di gestione del bus (bc_sched) che gira con alta priorità, frequentemente sposta dati dento e fuori dal bus. L'accesso al bus è sincronizzato con mutex. 25 Inversione di priorità What really happened on Mars ? L'architettura software di controllo del bus 1553, si compone di due task: è un task di schedulazione che controlla il setup delle transazioni sul bus 1553 (ha alta priorità) bc_sched: è un task di distribuzione che gestisce i risultati ottenuti (dati raccolti). bc_dist: Il ciclo di scheduluzaione è di 8 Hz (0.125 sec) Il ciclo deve essere terminato prima che bc_sched trasmetta le schedulazioni per il prossimo ciclo. 26 Inversione di priorità What really happened on Mars ? t1 – l'hardware del bus avvia il controllo del ciclo di 8 Hz. La transazione per questo ciclo è stata impostata dalla precedente esecuzione del task bc_sched. t2 – il traffico sul bus 1553 è completo (dati raccolti) e quindi viene svegliato il task bc_dist per iniziare la distribuzione dei dati. t3 - bc_dist completa la distribuzione dei dati. t4 - bc_sched viene svegliato per impostare la transazione per il prossimo ciclo. t5 - bc_sched termina la sua attività. Tipico ciclo di schedulazione del pathfinder. 27 Inversione di priorità What really happened on Mars ? L'architettura software di controllo del bus 1553, si compone di due task principali: bc_sched: è un task di schedulazione che controlla il setup delle transazioni sul bus 1553 (ha alta priorità) ha anche il compito di watchdog ed eseguire manovre correttive in caso qualcosa vada storto. bc_dist: è un task che distribuisce i risultati ottenuti (dati raccolti) agli altri task del sistema. Il ciclo di scheduluzaione è di 8 Hz (0.125 msec) Il ciclo deve essere terminato prima che bc_sched trasmetta le schedulazioni per il prossimo ciclo. Tipico ciclo di schedulazione del pathfinder. 28 Inversione di priorità What really happened on Mars ? Il task bc_dist distribuisce i dati ai vari task usando: POSIX pipes (IPC) per il task ASI/MET Uno switched buffer per gli altri task. Comunicazione dei dati in bc_dist 29 Inversione di priorità La ricetta pathfinder per l'inversione di priorità Il task responsabile del malfunzionamento è ASI/MET ASI/MET raccoglie dati metereologici e li trasmette usando un IPC basato su pipe(). Altri task leggono dalla pipe usando l'istruzione select() che nasconde un semaforo di mutua esclusione. bc_sched ha priorità massima bc_dist ha priorità 3 Viene usata per controllare l'accesso a sincronizzato a più descrittori di file (vedi man pselect e select). ASI/MET ha basa priorità ASI/MET chiama select() ba prima di rilasciare il mutex viene rilasciata forzatamente dallo scheduler da un task a priorità media (bc_dist). Questi cerca di acquisire il semaforo per la pipe ma non può dal momento che è bloccato da ASI/MET (inversione di priorità) Quando bc_sched inizia il nuovo ciclo individua che il ciclo precedente non si è completato (bc_dist non ha terminato il suo lavoro) e quindi retetta completamente il sistema. 30 Inversione di priorità La ricetta pathfinder per l'inversione di priorità ASI/MET acquisice il controllo del bus eseguendo la lock sul semaforo che controlla la pipe. bc_dist viene schedulato e si ferma sul lock perchè bloccato da ASI/MET. bc_sched viene attivato ed individua che bc_dist è ancora in esecuzione dopo la sua deadline. bc_sched individua l'errore di timing e manda in reset il sistema. 31 Inversione di priorità Soluzione: priority inheritance La versione del software era vecchia e sulle pipe era stata disabilitata la priority inheritance. La soluzione al problema appena descritto prende il nome di ereditarietà della priorità (o priority inheritance), e consiste nell'assegnare temporaneamente al processo che detiene la risorsa la priorità del processo a maggiore priorità che la richiede (il processo con bassa priorità viene “promosso”); non appena il processo rilascia la risorsa, ad esso viene riassegnata la priorità originaria (viene cioè “retrocesso”). In questo modo si “accelera” il processo che detiene la risorsa, e quindi si riduce il periodo di tempo durante il quale il processo a maggiore priorità risulta essere bloccato. 32 Inversione di priorità Soluzione: priority inheritance ASI/MET non viene più interrotto dal processo a priorità media (bc_dist) dal momento che eredita la sua priorità (temporaneamente). 33 Inversione di priorità Gestione dell'inversione di priorità nei POSIX threads SYNOPSIS #include <pthread.h> int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *restrict attr, int *restrict protocol); int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol); /* gestione del ceiling */ int pthread_mutex_getpriociling(const pthread_mutex_t *restrict mutex, int *restrict prioceiling); int pthread_mutex_setprioceiling(pthread_mutex_t *restrict mutex, int prioceiling, int *restrict old_ceiling); Vi ricordate il pthread_mutex_attr ? Si può usare pthread_mutex_setprotocol per impostare il protocollo di controllo priorità. pthread_mutex_t mymutex; pthread_mutexattr_t mta; pthread_mutexattr_init(&mta); pthread_mutexattr_setprotocol(&mta, PTHREAD_PRIO_INHERIT); /* inizializzazione del mutex */ pthread_mutex_init(&mymutex, &mta); 34 Sezione 3 del man Inversione di priorità Tipo di protocolli per il gestione della priorità Protocollo Descrizione PTHREAD_PRIO_NONE Nessuno schema di gestione della priorità verrà considerato durante l'utilizzo del mutex. PTHREAD_PRIO_INHERIT Un thread che sta bloccando uno più thread a priorità più alta perchè detiene uno o più mutex verrà eseguito con la priorità più alta tra quelle dei thread che stanno aspettando per i mutex bloccati dal thread e inizializzati con il PTHREAD_PRIO_INHERIT. Al rilascio del o dei mutex il thread riprenderà la sua priorità originaria. PTHREAD_PRIO_PROTECT Un thread che detiene uno o più mutex inizializzati con il PTHREAD_PRIO_PROTECT protocol verrà eseguito alla più alta priorità o al più alto tetto di priorità di tutti i mutex detenuti da questo thread e inzializzati con PTHREAD_PRIO_PROTECT senza considerare se altri thread sono bloccati su uno di questi mutex o meno. Il tetto di priorità viene stabilito solitamente come il massimo delle priorità di tutti i thread che richiedono l'accesso al mutex. 35 Riferimenti Approfondimenti DPP (Dinner Philosopher Problem) http://en.wikipedia.org/wiki/Dining_philosophers_problem Varie implementazioni reperibili su Internet (google it!) Inversione di priorità http://en.wikipedia.org/wiki/Priority_inversion Starvation http://it.wikipedia.org/wiki/Starvation What really happened on Mars ? http://research.microsoft.com/en-us/um/people/mbj/mars_pathfinder/ http://www.infres.enst.fr/~pautet/sar/fset/sha90pcp.pdf Così parlo Bellavista di Luciano De Crescenzo (1984) 36 Correte ad affittare questo film! Assolutamente da non perdere ;-)