istre-07-pthread.ppt [modalità...

14
14/05/2014 1 7. Pthread library 1. Descrizione generale 2. Gestione di thread 3. Scheduler in Linux 4. Gestione del tempo 5. Gestione di thread periodici 6. Mutua esclusione 7. Esempi Sommario Un processo rappresenta l'unità di esecuzione in un SO. Programma: entità passiva descritta dal codice sorgente. Processo: entità attiva determinata dall'esecuzione del programma su un particolare insieme di dati. Processo Un processo è rappresentato dai seguenti elementi: codice: istruzioni compilate ed eseguibili; dati: spazio di memoria per le variabili globali; contesto: stato dei registri di CPU; stack: spazio di memoria per le variabili locali. parametri: tipo, priorità, argomenti, ... I processi non condividono memoria! Processi diversi eseguono codici diversi; accedono a spazi di memoria distinti. Essi possono comunicare mediante un meccanismo a scambio di messaggi messo a disposizione dal SO: Processo dati dati P 1 P 2 channel Un processo può essere costituito da più sottoprocessi concorrenti, detti thread. I thread di uno stesso processo condividono lo stesso spazio di memoria; hanno stack distinti; possono eseguire lo stesso codice. Processi e thread dati P i 1 2 3 Esempio di processo composto da 3 thread: Funzioni pthread_create: crea un thread pthread_exit: termina il thread chiamante pthread_cancel: termina un altro thread pthread_join: aspetta la terminazione di un thread Compilazione Per utilizzare la libreria, inserire #include <pthread.h> gcc prova.c -o prova -lpthread -lrt

Upload: dinhdien

Post on 02-Nov-2018

218 views

Category:

Documents


2 download

TRANSCRIPT

14/05/2014

1

7. Pthread library

1. Descrizione generale

2. Gestione di thread

3. Scheduler in Linux

4. Gestione del tempo

5. Gestione di thread periodici

6. Mutua esclusione

7. Esempi

Sommario

Un processo rappresenta l'unità di esecuzione in un SO.

Programma: entità passiva descritta dal codice sorgente.

Processo: entità attiva determinata dall'esecuzione delprogramma su un particolare insieme di dati.

Processo

Un processo è rappresentato dai seguenti elementi:

codice: istruzioni compilate ed eseguibili;

dati: spazio di memoria per le variabili globali;

contesto: stato dei registri di CPU;

stack: spazio di memoria per le variabili locali.

parametri: tipo, priorità, argomenti, ...

I processi non condividono memoria!

Processi diversi

eseguono codici diversi;

accedono a spazi di memoria distinti.

Essi possono comunicare mediante un meccanismo ascambio di messaggi messo a disposizione dal SO:

Processo

datidati

P1 P2channel

Un processo può essere costituito da più sottoprocessiconcorrenti, detti thread.

I thread di uno stesso processo

condividono lo stesso spazio di memoria;

hanno stack distinti;

possono eseguire lo stesso codice.

Processi e thread

dati

Pi1 2 3

Esempio di processocomposto da 3 thread:

Funzioni

pthread_create: crea un thread

pthread_exit: termina il thread chiamante

pthread_cancel: termina un altro thread

pthread_join: aspetta la terminazione di un thread

Compilazione

Per utilizzare la libreria, inserire #include <pthread.h>

gcc prova.c -o prova -lpthread -lrt

14/05/2014

2

Thread creation

int pthread_create(thread_t *id,pthread_attr_t *attr,void *(*body)(void *),void *arg);

Crea un thread:

id memorizza l'identificatore unico del thread creato.

attr puntatore a una struttura che definisce gli attributi delthread (se NULL utilizza valori di default).

body puntatore alla funzione che definisce il codice del thread.

arg puntatore al singolo argomento del thread. Per passare piùargomenti definire un puntatore a struttura.

Thread creation

Esempio:

pthread_t tid;

pthread_create(&tid, NULL, task, NULL);

void *task(){

printf("I am a simple thread.\n");}

usa attributidi default

nessunargomento

Thread ID

Thread ID

Poiché l'identificatore di thread viene restituito a chieffettua la create, esistono le seguenti funzioni:

pthread_self()

restituisce l'ID del thread chiamante

pthread_equal(tid1, tid2)

restituisce un valore 0, se tid1 = tid2, 0 altrimenti.

pthread_t tid1, tid2;

tid1 = pthread_self();

if (pthread_equal(tid1,tid2))return 0;

else return 1;

Thread creation

Main thread thread th1

pthread_create(&th1,…

pthread_t th1;

Dopo la create, non è detto che il thread sia attivo, esso hasolo un ID e le strutture dati necessarie. Può accadere che: il thread non sia ancora partito; sia attivo in coda pronti; sia già terminato.

Un thread può terminare per diverse cause:

• dopo che viene eseguita l'ultima istruzione dellafunzione ad esso associata (terminazione normale);

• quando esso chiama la primitiva pthread_exit.

• quando viene terminato da un altro thread attraverso laprimitiva pthread_cancel.

• quando termina il processo a cui appartiene, nel nostrocaso il main(), a causa di una terminazione normale o diuna chiamata alla primitiva exit.

Thread termination Thread termination

void pthread_exit(void *retval);

Termina il thread chiamante e restituisce in retval il valorerestituito dal thread terminato.

int pthread_cancel(pthread_t th);

Invia una richiesta di cancellazione per il thread th. Laterminazione effettiva dipende da due attributi del thread:

state: enabled (default) o disabled

type: asynchronous o deferred (default)

assegnabili per mezzo delle primitive

pthread_setcancelstate e pthread_setcanceltype

14/05/2014

3

Thread joining

int pthread_join(pthread_t th, void **retval);

Aspetta la terminazione del thread indicato da th.

• Se retval NULL, il valore di ritorno del threadterminato viene copiato in *retval.

• Se il thread è già terminato, *retval assume il valorePTHREAD_CANCELED.

• Retistuisce 0 in caso di successo, o un codice di errore.

E' possibile attendere la terminazione di un threadattraverso la funzione pthread_join:

Esempio - create

#include <pthread.h>

void *task(void *p);

int main(){pthread_t tid1, tid2;int tret1, tret2;int a = 1, b = 2;

tret1 = pthread_create(&tid1, NULL, task, (void*)&a);tret2 = pthread_create(&tid2, NULL, task, (void*)&b);

pthread_join(tid1, NULL);pthread_join(tid2, NULL);

printf("Thread1 returns %d\n", tret1);printf("Thread2 returns %d\n", tret2);return 0;

}

vengono creati 2 thread con lostesso codice, che si differenzianomediante il parametro passato.

void *task(void *p){int *pi;

pi = (int *)p;printf("This is TASK %d\n", *pi);

}

Esempio - create Joinable vs. detached

Quando si chiama la pthread_join è possibile che il threadsia già terminato, quindi il sistema deve mantenere delleinformazioni anche dopo la terminazione.

Tali informazioni vengono distrutte (e la memorialiberata) con l'operazione di join.

Se non ci si deve sincronizzare con un thread e non sichiama la pthread_join, la memoria non viene liberata.

Per evitare spreco di memoria quando la join non èrichiesta, un thread può essere dichiarato di tipodetached (per default, un thread è di tipo joinable).

Joinable vs. detached

Esistono due modi per definire un thread di tipo detached:

Usare l'attributo detachstate in fase di creazione.

Chiamare la funzione pthread_detach().

Quando termina un thread di tipo detached, il sistemalibera automaticamente tutte le strutture dati di sistema daesso utilizzate.

Detached threads

int pthread_detach(pthread_t th);

Definisce il thread th come detached. Retistuisce 0 in casodi successo, o un codice di errore.

Tale funzione può essere chiamata anche dallo stessothread che si vuole definire come detached.

In tal caso, l'identificatore del thread può essere recuperatochiamando la funzione pthread_self():

pthread_t my_id;

my_id = pthread_self();

pthread_detach(my_id);

14/05/2014

4

Thread attributes

Specificano le caratteristiche del thread:

stack size dimensione della memoria di stack.

state tipologia (joinable o detached).

priority livello di priorità del thread.

scheduler algoritmo con cui schedulare il thread.

Gli attributi devono essere inizializzati e distrutti:

int pthread_attr_init(pthread_attr_t *attr);

int pthread_attr_destroy(pthread_attr_t *attr);

Lo stato del thread che deve essere creato viene definitomodificando gli attributi di default per mezzo dellafunzione pthread_attr_setdetachstate():

pthread_t tid;pthread_attr_t attr;

pthread_attr_init(&attr);

pthread_attr_setdetachstate(&attr,

PTHREAD_CREATE_DETACHED);

pthread_create(&tid, &attr, task, NULL);

Thread state

Thread priority

struct sched_param mypar;

mypar.sched_priority = 23;

pthread_attr_setschedparam(&myatt, &mypar);

struct sched_param {

int sched_priority;};

La priorità di un thread viene specificata mediante unastruttura contenente un solo campo:

La priorità deve essere prima assegnata per mezzo di unastruttura locale e poi inserita negli attributi del threadmediante un'apposita funzione:

Linux Scheduling

CPU

priority

Linux prevede 99 livelli di priorità: 1 (bassa), 99 (alta).Tuttavia lo standard POSIX richiede di garantirne 32.

Ad ogni priorità è associata una coda, in cui vengonoinseriti i thread con la stessa priorità.

Il primo thread della coda a più alta priorità vieneselezionato come running task:

Linux prevede tre tipologie di scheduler (per ogni thread):

SCHED_OTHER (Default Policy)Scheduler Round Robin con meccanismo di aging.

SCHED_FIFOScheduler prioritario: thread a pari priorità vengonogestiti con politica FIFO.

SCHED_RRScheduler prioritario: thread a pari priorità vengonogestiti con politica Round‐Robin.

Linux Scheduling

SCHED_FIFO e SCHED_RR sono dette politiche real timee possono essere usate solo da root.

Politiche real time

SCHED_FIFO (Fixed‐Priority Scheduling + FIFO)

Scheduler prioritario: thread a pari priorità vengonogestiti con politica FIFO. Un thread viene eseguito fino aterminazione, cancellazione, bloccaggio, o preemption.

SCHED_RR (Fixed‐Priority Scheduling + RR)

Scheduler prioritario: thread a pari priorità vengonogestiti con politica Round‐Robin. Un thread vieneeseguito fino a terminazione, cancellazione, bloccaggio,preemption o esaurimento del quanto temporale.

Il quanto dipende dal sistema e non può essere definitodall'utente, ma può essere conosciuto chiamando lafunzione sched_rr_get_interval().

14/05/2014

5

Round-Robin quantum

int sched_rr_get_interval(pid_t pid, struct timespec *tp);

Scrive nella struttura puntata da tp il valore del quantotemporale utilizzato dallo scheduler Round‐Robin per ilprocesso identificato da pid.

Se pid = 0, viene restituito il valore del quanto utilizzato peril processo chiamante:

#include <sched.h>

struct timespec q;

sched_rr_get_interval(0, &q);

printf("Q: %ld s, %ld ns\n",q.tv_sec, q.tv_nsec);

Anche lo scheduler viene definito attraverso gli attributi,utilizzando la funzione pthread_attr_setschedpolicy.

pthread_attr_t myatt;

pthread_attr_init(&myatt);

pthread_attr_setinheritsched(&myatt, PTHREAD_EXPLICIT_SCHED);

pthread_attr_setschedpolicy(&myatt, SCHED_FIFO);

Linux Scheduler

Per default, un thread viene schedulato con la stessapolitica usata per il thread padre, ossia SCHED_OTHER.

Per usare politiche diverse è necessario comunicare alkernel tale intenzione attraverso la funzionepthread_attr_setinheritsched:

Esempio - create

int main(){pthread_attr_t myatt; /* attribute structure */struct sched_param mypar; /* priority structure */pthread_t tid; /* thread id */

pthread_attr_init(&myatt);

pthread_attr_setinheritsched(&myatt, PTHREAD_EXPLICIT_SCHED);pthread_attr_setschedpolicy(&myatt, SCHED_FIFO);

mypar.sched_priority = 23;pthread_attr_setschedparam(&myatt, &mypar);

err = pthread_create(&tid, &myatt, task, NULL);

pthread_join(tid, NULL);pthread_attr_destroy(&myatt);

}

Per evitare che applicazioni errate blocchino il sistema,esiste un meccanismo di protezione temporale chegarantisce al sistema una minima quantità di tempo.

Tale meccanismo è configurabile attraverso 2 parametri:

/proc/sys/kernel/sched_rt_period_us

stabilisce il periodo (in microsecondi) della reservation.Default = 1000000 µs (1 secondo).

/proc/sys/kernel/sched_rt_runtime_us

stabilisce il budget (in microsecondi) da allocare alleattività real‐time in ogni periodo. Default = 950000 µs.

Protezione temporale

Dunque, per default, il 95% della CPU è riservato alleattività real time e il 5% a quelle gestite con SCHED_OTHER.

Ciò significa che un thread ad alta priorità può essereinterrotto anche se schedulato con SCHED_FIFO.

La percentuale riservata ai thread real time può esseremodificata o disabilitata (solo da root):

Protezione temporale

> echo 980000 > /proc/sys/kernel/sched_rt_runtime_us

Imposta il budget al 98%.

> echo ‐1 > /proc/sys/kernel/sched_rt_runtime_us

Imposta il budget al 100%.

14/05/2014

6

Tipi di clock

Linux supporta i seguenti clock:

CLOCK_REALTIME: è il valore piùvicino possibile al tempo assoluto.Tuttavia, esso può essere discontinuoin quanto può subire aggiustamenti.

CLOCK_MONOTONIC: rappresenta iltempo trascorso a partire da unimprecisato istante. Esso non èaffetto da aggiustamenti, per cui è lasoluzione migliore per misurare iltempo intercorso tra due eventi.

Rappresentazione del tempo

Nello standard POSIX, il tempo è rappresentato per mezzodella seguente struttura, definita in <time.h>:

struct timespec {time_t tv_sec; /* seconds */long tv_nsec; /* nanoseconds */

}

Il tipo time_t dipende dall'implementazione, ma disolito è un intero a 32 bit.

Purtroppo la libreria standard non fornisce funzioni disupporto per compiere operazioni sul tempo, per cui ènecessario definirsi delle funzioni ausiliarie.

Copia di tempi

void time_copy(struct timespec *td,struct timespec ts)

{td->tv_sec = ts.tv_sec;td->tv_nsec = ts.tv_nsec;

}

Tale funzione copia il tempo sorgente ts nella variabiledestinataria puntata da td:

Somma di millisecondi

void time_add_ms(struct timespec *t, int ms){

t->tv_sec += ms/1000;t->tv_nsec += (ms%1000)*1000000;

if (t->tv_nsec > 1000000000) {t->tv_nsec -= 1000000000;t->tv_sec += 1;

}}

Tale funzione somma al tempo t un valore ms espresso inmillisecondi:

Confronto di tempi

int time_cmp(struct timespec t1,struct timespec t2)

{if (t1.tv_sec > t2.tv_sec) return 1;if (t1.tv_sec < t2.tv_sec) return -1;if (t1.tv_nsec > t2.tv_nsec) return 1;if (t1.tv_nsec < t2.tv_nsec) return -1;return 0;

}

Tale funzione confronta due tempi t1 e t2 e restituisce0 se uguali, 1 se t1 > t2, ‐1 se t1 < t2:

Funzioni disponibili

int clock_getres(clockid_t clk_id, struct timespec *res);

Se res NULL, scrive in res la risoluzione del clockspecificato in clk_id. Questa dipende dalla particolareimplementazione e non può essere impostata.

int clock_gettime(clockid_t clk_id, struct timespec *t);

Memorizza in t il valore del clock specificato in clk_id.

int clock_settime(clockid_t clk_id, struct timespec *t);

Imposta il clock specificato in clk_id al valore t. Se t non èmultiplo della risoluzione, esso viene troncato.

14/05/2014

7

Funzioni disponibili

int clock_nanosleep(clockid_t clk_id, int flag,const struct timespec *t, struct timespec *rem);

Sospende l'esecuzione del thread chiamante finché il clockclk_id non raggiunge il tempo specificato in t.

Se flag = 0, il tempo t viene interpretato come relativo altempo corrente;

Se flag = TIMER_ABSTIME, il tempo t viene interpretatocome assoluto.

Se il thread viene risvegliato prima del tempo impostato,il tempo rimanente viene memorizzato in rem.

Esempio

struct timespec t; /* absolute time */struct timespec dt; /* time interval */int delta = 500; /* delay in milliseconds */

clock_gettime(CLOCK_MONOTONIC, &t);time_add_ms(&t, delta);

/* suspend until absolute time t */

clock_nanosleep(CLOCK_MONOTONIC,TIMER_ABSTIME, &t, NULL);

/* suspend for a relative interval of 500 ms */

dt.tv_sec = 0;dt.tv_nsec = delta*1000000;clock_nanosleep(CLOCK_MONOTONIC, 0, &dt, NULL);

Un thread non periodico

void *task(void *arg){struct timespec t;int period = 100; /* period in milliseconds */

while (1) {

/* do useful work */

clock_gettime(CLOCK_MONOTONIC, &t);

time_add_ms(&t, period);

clock_nanosleep(CLOCK_MONOTONIC,TIMER_ABSTIME, &t, NULL);

}}

Il task ha un periodo variabile pari a 100 ms + il tempo dirisposta (che varia da job a job).

Un thread periodico

void *task(void *arg){struct timespec t;int period = 100; /* period in milliseconds */

clock_gettime(CLOCK_MONOTONIC, &t);time_add_ms(&t, period);

while (1) {

/* do useful work */

clock_nanosleep(CLOCK_MONOTONIC,TIMER_ABSTIME, &t, NULL);

time_add_ms(&t, period);}

}

Controllo deadline miss

void *task(void *arg){struct timespec t, now;int period = 100; /* period in milliseconds */

clock_gettime(CLOCK_MONOTONIC, &t);time_add_ms(&t, period);

while (1) {

/* do useful work */

clock_gettime(CLOCK_MONOTONIC, &now);if (time_cmp(now, t) > 0) exit(-1);

clock_nanosleep(CLOCK_MONOTONIC,TIMER_ABSTIME, &t, NULL);

time_add_ms(&t, period);}

}

14/05/2014

8

Thread periodici

Un thread periodico, dunque, segue il seguente schema:

void *task(void *p){<local state variables>

set_period();

while (1) {

<thread body>

if (deadline_miss()) <do action>;wait_for_period();

}}

Struttura di parametri

Conviene definire una struttura per i parametri del thread:

struct task_par {int arg; /* task argument */long wcet; /* in microseconds */int period; /* in milliseconds */int deadline; /* relative (ms) */int priority; /* in [0,99] */int dmiss; /* no. of misses */struct timespec at; /* next activ. time */struct timespec dl; /* abs. deadline */

};

Tale struttura deve essere inizializzata da chi effettuala thread_create e passata come argomento al thread.

Occorre definire una stuttura per ogni thread.

Esempio: mainstruct sched_param mypar;struct task_partp[NT];pthread_attr_t att[NT];pthread_t tid[NT];

for (i=0; i<NT; i++) {tp[i].arg = i;tp[i].period = 100;tp[i].deadline = 80;tp[i].priority = 20;tp[i].dmiss = 0;

pthread_attr_init(&att[i]);pthread_attr_setinheritsched(&att[i], PTHREAD_EXPLICIT_SCHED);pthread_attr_setschedpolicy(&att[i], SCHED_FIFO);mypar.sched_priority = tp[i].priority;pthread_attr_setschedparam(&att[i], &mypar);pthread_create(&tid[i], &att[i], task, &tp[i]);

}

Se noto, il WCET può essereutilizzato per il test di garanzia.

La deadline assoluta vieneimpostata online appena notol'istante di attivazione.

array di variabiliper tutti i thread

Esempio: threadvoid *task(void *arg){<local state variables>struct task_par *tp;

tp = (struct task_par *)arg;i = tp->arg;

set_period(tp);

while (1) {

<thread body>

if (deadline_miss(tp))printf("!");

wait_for_period(tp);}

}

recupera il puntatore atask_par

recuperal'argomento

set_period()

void set_period(struct task_par *tp){struct timespec t;

clock_gettime(CLOCK_MONOTONIC, &t);time_copy(&(tp->at), t);time_copy(&(tp->dl), t);time_add_ms(&(tp->at), tp->period);time_add_ms(&(tp->dl), tp->deadline);

}

Legge il tempo corrente e calcola il successivo istante diattivazione e la deadline assoluta del task.

NOTA: il timer non viene impostato per interrompere.

wait_for_period()

void wait_for_period(struct task_par *tp){

clock_nanosleep(CLOCK_MONOTONIC,TIMER_ABSTIME, &(tp->at), NULL);

time_add_ms(&(tp->at), tp->period);time_add_ms(&(tp->dl), tp->period);

}

Sospende il thread chiamante fino all'attivazione successivae, al risveglio, ricalcola l'istante di attivazione e la deadline.

NOTA: anche se il thread esegue la time_add_ms() in unistante maggiore di quello di risveglio, il calcolorisulta corretto.

14/05/2014

9

deadline_miss()

int deadline_miss(struct task_par *tp){struct timespec now;

clock_gettime(CLOCK_MONOTONIC, &now);

if (time_cmp(now, tp->dl) > 0) {tp->dmiss++;return 1;

}return 0;

}

Se il thread è in esecuzione al tempo di riattivazione,incrementa il campo dmiss e restituisce 1, altrimentirestituisce 0.

Sincronizzazione fra thread

In generale, esistono i seguenti meccanismi persincronizzare più thread:

Join consente di aspettare la terminazione di uno opiù thread.

Semaphore consente mutua esclusione e sincronizzazione.

Mutex consente di eseguire codice in mutuaesclusione

Condition consente di riattivarsi quando una variabilecondivisa raggiunge un valore prefissato.

Barrier consente a più thread di sospendersi fino ache tutti abbiano eseguito una certaoperazione.

Thread Joining

pthread_create()

pthread_join()

Main thread

thread 1

pthread_exit()

thread 2

pthread_exit()

pthread_create()

pthread_join()

consente di aspettare la terminazione di uno o più thread

Semaphores

sem_init: inizializza un semaforo.

sem_destroy: libera la memoria di un semaforo nonpiù utilizzato.

sem_wait: wait su semaforo.

sem_post: signal su semaforo.

sem_getvalue: restituisce il valore del semaforo.

I semafori generali non sono parte della libreria pthread,ma sono definiti nello standard POSIX:

Per utilizzarli occorre includere il file <semaphore.h>.

Un semaforo è una variabile di tipo sem_t

Funzioni principali:

Inizializzazione semafori

int sem_init (sem_t *sem, int pshared, unsigned int v);

Inizializza un semaforo:

sem indirizzo del semaforo;

pshared se 0, indica che il semaforo è condiviso frathread (dunque deve essere dichiarato globale);se 0, indica che è condiviso tra processi.

v valore iniziale del semaforo;

Restituisce 0 in caso di successo, ‐1 in caso di errore.

14/05/2014

10

Uso semafori

int sem_wait (sem_t *sem);

Se il valore corrente del semaforo è 0, blocca il threadchiamante finché non viene eseguita una sem_post,altrimenti decrementa il semaforo ed esce.

Entrambe le funzioni restituiscono 0 in caso di successo, ‐1in caso di errore.

int sem_post (sem_t *sem);

Se esistono thread bloccati sul semaforo, risveglia quello apiù alta priorità, altrimenti incrementa il semaforo ed esce.

Uso semafori

int sem_destroy (sem_t *sem);

Dealloca la memoria utilizzata per il semaforo.

Entrambe le funzioni restituiscono 0 in caso di successo, ‐1in caso di errore.

int sem_getvalue (sem_t *sem, int *pval);

Legge il valore del semaforo e lo scrive nella variabilepuntata da pval.

Esempio semafori

#include <semaphore.h>

sem_t sem1, sem2, sem3; /* definisce 3 semafori */

int main(){

/* inizializza sem1 per la sincronizzazione */sem_init(&sem1, 0, 0);

/* inizializza sem2 per la mutua esclusione */sem_init(&sem2, 0, 1);

/* inizializza sem3 per l'accesso simultaneo *//* di max 4 thread ad una risorsa con 4 unità */sem_init(&sem3, 0, 4);

Sincronizzazione su evento

thread 1 thread 2

sem_post(&event)

sem_wait(&event)

sem_t event;

sem_init(&event, 0, 0);

Valore iniziale = 0

struct point {int x;int y;

} p;

void *writer();void *reader();sem_t s;

int main(){pthread_t tid1, tid2;

sem_init(&s, 0, 1);

pthread_create(&tid1, NULL, writer, NULL);pthread_create(&tid2, NULL, reader, NULL);

/* do some useful work */}

Struttura globale condivisa daithread in mutua esclusione.

Mutua esclusione

Definizione del semaforo

Inizializzazione del semaforo

void *writer(){

sem_wait(&s);p.x++;p.y++;sem_post(&s);

}

Mutua esclusione

void *reader(){

sem_wait(&s);printf("(%d,%d)\n", p.x, p.y);sem_post(&s);

}

14/05/2014

11

Semafori Mutex

pthread_mutex_init: inizializza un semaforo.

pthread_mutex_destroy: libera la memoria di un mutexche non serve più.

pthread_mutex_lock: wait su semaforo.

pthread_mutex_unlock: signal su semaforo.

Un MUTEX è un tipo particolare di semaforo binario conalcune restrizioni (mirate a ridurre gli errori):

può solo essere usato per la mutua esclusione, nonper la sincronizzazione;

un mutex occupato da un thread può essere sbloccatosolo dallo stesso thread.

Funzioni principali:

Inizializzazione Mutex

Prima di essere utilizzato, un mutex dev'essere dichiarato einizializzato. Esistono 3 modi per inizializzare un mutex:

pthread_mutex_t mux; /* define a mutex */pthread_mutexattr_t matt; /* define attributes */

/* modo 1: inizializzazione diretta */pthread_mutex_t mux = PTHREAD_MUTEX_INITIALIZER;

/* modo 2: inizializzazione con valori di default */pthread_mutex_init(&mux, NULL);

/* modo 3: inizializzazione con attributi */pthread_mutexattr_init(&matt);pthread_mutex_init(&mux, &matt);

Nell'esempio riportato, i tre modi sono equivalenti einizializzano il mutex con i valori di default.

Esempio - Mutexstruct point {

int x;int y;

} p;

void *writer();void *reader();pthread_mutex_t mux = PTHREAD_MUTEX_INITIALIZER;

int main(){pthread_t tid1, tid2;

pthread_create(&tid1, NULL, writer, NULL);pthread_create(&tid2, NULL, reader, NULL);

pthread_join(tid1, NULL);pthread_join(tid2, NULL);return 0;

}

Struttura globale condivisa daithread in mutua esclusione.

Definizione e inizializzazionedel semaforo.

void *writer(){

pthread_mutex_lock(&mux);p.x++;p.y++;pthread_mutex_unlock(&mux);

}

Esempio - Mutex

void *reader(){

pthread_mutex_lock(&mux);printf("(%d,%d)\n", p.x, p.y);pthread_mutex_unlock(&mux);

}

Mutex protocols

Linux prevede tre tipologie di protocolli sui mutex:

PTHREAD_PRIO_NONE

Nessun protocollo (semafori classici).

PTHREAD_PRIO_INHERIT

Protocollo di Priority Inheritance.

PTHREAD_PRIO_PROTECT

Protocollo di Immediate Priority Ceiling(anche noto come Highest Locker Priority).

NOTA: I semafori POSIX non prevedono tali protocolli.

Priority Inheritance

#define _GNU_SOURCE

pthread_mutex_t mux1, mux2; /* define 2 mutexes */pthread_mutexattr_t matt; /* define mutex attrib. */

pthread_mutexattr_init(&matt); /* initialize attributes */pthread_mutexattr_setprotocol(&matt, PTHREAD_PRIO_INHERIT);

pthread_mutex_init(&mux1, &matt);pthread_mutex_init(&mux2, &matt);

pthread_mutexattr_destroy(&matt); /* destroy attributes */

Occorre inserire in testa al file: #define _GNU_SOURCE

Ogni semaforo può usare un protocollo diverso.

La stessa variabile matt può essere utilizzata perinizializzare semafori diversi.

14/05/2014

12

Immediate Priority Ceiling

#define _GNU_SOURCE

pthread_mutex_t mux1, mux2; /* define 2 mutexes */pthread_mutexattr_t matt; /* define mutex attrib. */

pthread_mutexattr_init(&matt);pthread_mutexattr_setprotocol(&matt, PTHREAD_PRIO_PROTECT);

pthread_mutexattr_setprioceiling(&matt, 10);pthread_mutex_init(&mux1, &matt);

pthread_mutexattr_setprioceiling(&matt, 15);pthread_mutex_init(&mux2, &matt);

pthread_mutexattr_destroy(&matt);

Il ceiling del semaforo deve essere uguale alla massimapriorità dei task che lo usano.

Condition variables

Tale meccanismo consente ad un thread di attendere unvalore prefissato di una variabile condivisa:

x != 8 sleepY

N

x = x + 1

x == 8

N

Y

Il loop è necessario in quanto il risveglio può 

essere dovuto ad altre cause

Waiting thread Signaling thread

Se più thread sono in attesa, è possibile svegliarne solo uno oppure tutti insieme

Condition variables

lock(mux);while (x != 8) {

sleep(cond_var, mux);}unlock(mux);

lock(mux);x  = x + 1;if (x == 8) {

unlock(mux);signal(cond_var);

}else unlock(mux);

Sono sempre usate insieme a un mutex:

Prima di bloccarsi, il mutex viene rilasciatoin modo atomico e riacquisito al risveglio.

Waiting thread Signaling thread

pthread_mutex_t mux; /* define a mutex */pthread_cond_t cv; /* define a cond. variable */int count = 0; /* variable to be checked */

int main(){

/* initialize variables with default attributes */pthread_mutex_init(&mux, NULL);pthread_cond_init(&cv, NULL);

/* do some useful work */

/* reclaim memory */pthread_mutex_destroy(&mux);pthread_cond_destroy(&cv);

}

Condition variables

void *watch_count(){

/* wait until count reaches the THRESHOLD */

pthread_mutex_lock(&mux);

while (count < THRESHOLD) {

pthread_cond_wait(&cv, &mux);}

pthread_mutex_unlock(&mux);

/* do some useful work */

pthread_exit(NULL);

}

Waiting thread

void *inc_count(){

/* do some useful work */

pthread_mutex_lock(&mux);

count++;

if (count == THRESHOLD) {

pthread_mutex_unlock(&mux);pthread_cond_signal(&cv);

}else pthread_mutex_unlock(&mux);

/* do some useful work */

pthread_exit(NULL);

}

Signaling thread

14/05/2014

13

void *inc_count(){

/* do some useful work */

pthread_mutex_lock(&mux);

count++;

if (count == THRESHOLD) {

pthread_mutex_unlock(&mux);pthread_cond_broadcast(&cv);

}else pthread_mutex_unlock(&mux);

/* do some useful work */

pthread_exit(NULL);

}

Brodcasting

Si possono risvegliare tutti i thread in attesa, mediante 

una broadcast

Note

int pthread_cond_signal (pthread_cond_t *cond);int pthread_cond_broadcast (pthread_cond_t *cond);

La signal risveglia uno dei thread bloccati sulla variabilecondition. Il thread svegliato dipende dallo scheduler(per scheduler real time il thread svegliato è quello apriorità più alta).

La broadcast risveglia tutti i thread bloccati sullavariabile condition.

Entrambe le funzioni non hanno effetto se non ci sonothread bloccati.

Barrier

thread 1 thread 2 thread 3

barrier_wait()

barrier_wait()

barrier_wait()

B A R R I E R

Ogni thread si blocca finché tutti abbiano eseguito labarrier_wait:

Tutti aspettano l'arrivo del 

thread più lento

#define NT 10pthread_barrier_t barr;

int main(){pthread_t tid[NT];int i;

/* initialize barrier */pthread_barrier_init(&barr, NULL, NT);

for (i=0; i<NT; i++)pthread_create(&tid[i], NULL, &task, (void*)i));

/* do some useful work */

return 0;}

Barrier

e inizializzata con il numero dithread che devono sincronizzarsi

Una barriera deve essere dichiarata

Sync at barrier

int pthread_barrier_wait (pthread_barrier_t *barr);

Blocca il thread chiamante fino a che un numero di threadpari a quello specificato in pthread_barrier_init abbiaeseguito la pthread_barrier_wait.

In caso di successo, la funzione restituisce il valorePTHREAD_BARRIER_SERIAL_THREAD ad un thread nonspecificato e 0 agli altri thread. Quindi, la barriera vieneresettata allo stato dell'ultima pthread_barrier_init.

In caso di insuccesso, viene restituito un codice d'errore.

La sincronizzazione su una barriera avviene attraverso lafunzione pthread_barrier_wait:

void *task(void *arg){int rc;int i = (int)arg;

/* do some useful work */

/* synchronization point */rc = pthread_barrier_wait(&barr);if ((rc != 0) &&

(rc != PTHREAD_BARRIER_SERIAL_THREAD)) {printf("Could not wait on barrier\n");exit(-1);

}

/* do some useful work */

pthread_exit(NULL);}

Thread sync at barrier

14/05/2014

14

struct itimerspec {

struct timespec it_interval;

struct timespec it_value;};

Timer periodici

Tali funzioni notificano gli eventi attraverso un filedescriptor (fd) e usano la seguente struttura, definita in<sys/timerfd.h>:

int timerfd_create();int timerfd_gettime();int timerfd_settime();

periodo del timer

scadenza iniziale

Funzioni disponibili

int timerfd_create(int clk_id, int flag);

int timerfd_gettime(int fd, struct itimerspec *t);

Memorizza in t il valore del timer specificato da fd.

Il campo it_value di t contiene l'intervallo mancantealla successiva scadenza del timer.

Il campo it_interval di t contiene il periodo del timer.

Crea un timer di tipo clk_id e restituisce il file descriptorcorrispondente. L'argomento flag deve essere posto a zero.

Funzioni disponibili

int timerfd_settime(int fd, int flag,struct itimerspec *new_value,struct itimerspec *old_value);

Imposta il clock specificato da fd al valore new_value.

new_value specifica periodo e scadenza iniziale deltimer. Se scadenza iniziale = 0, il timer viene disabilitato.Se periodo = 0, il timer scatta solo una volta.

old_value restituisce l'impostazione del timer almomento della chiamata.

Se flag = 0, viene creato un timer relativo, se flag =TFD_TIMER_ABSTIME, viene creato un timer assoluto.

Funzioni disponibili

size_t read(int fd, void *buf, size_t n);

Tenta di leggere n byte dal file descriptor fd nel buffer buf.Se usata sul file descriptor di un timer, sospende il threadchiamante fino alla scadenza del timer.

Se il timer è già scattatto una o più volte, tale numeroviene restituito in buf.