lab - problematiche sicro processi e thread

advertisement
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 ;-)
Download