95
1 Il semaforo

Il semaforo - LIAlia.disi.unibo.it/Courses/SistOpM0910/materiale/06... · 2009. 10. 20. · Il semaforo •Strumento linguistico di basso livello per risolvere problemi di sincronizzazione

  • Upload
    others

  • View
    0

  • Download
    0

Embed Size (px)

Citation preview

  • 1

    Il semaforo

  • 2

    Il semaforo• Strumento linguistico di basso livello per risolvere

    problemi di sincronizzazione.

    • Il meccanismo semaforico e` normalmente utilizzato alivello di macchina concorrente per realizzarestrumenti di sincronizzazione di “più alto livello”.

    • Disponibile in librerie standard per la realizzazione diprogrammi concorrenti con linguaggi sequenziali (es. C).

    Esempio: libreria LinuxThreads (standard Posix)

  • 3

    Un semaforo e` rappresentato da una variabile di tiposemaphore, definita come una variabile intera nonnegativa, cui è possibile accedere solo tramite le dueoperazioni P e V .

    Dichiarazione di un oggetto di tipo semaphore:semaphore s=i;

    dove i (i≥0) è il valore iniziale.

    Al tipo semaphore sono associati:Insieme di valori= { X | X ∈ N}Insieme delle operazioni= {P,V}

    Semaforo: tipo di dati semaphore

  • 4

    Operazioni sul semaforo• Un oggetto di tipo semaphore è condivisibile da due o più

    processi, che operano su di esso attraverso le operazioni P eV.

    Semantica delle due operazioni: void P(semaphore s):

    region s 0) vals--;>>

    void V(semaphore s):region s >

    dove vals rappresenta il valore del semaforo.

    Essendo l’oggetto s condiviso, le due operazioni P e V vengonodefinite come sezioni critiche da eseguire in mutuaesclusione. Le due operazioni devono essere eseguite informa atomica.

  • 5

    vals ++ vals>0

    vals --

    sino

    V(s)P(s)

    Operazioni sul semaforo

    Il semaforo viene utilizzato come strumento disincronizzazione tra processi concorrenti:

    •sospensione: P(s), vals==0•risveglio: V(s), se vi e` almeno un processosospeso

  • 6

    Relazione di invarianza:

    Siano:• Is: valore intero≥0 con cui il semaforo s viene

    inizializzato;• nvs: numero di volte che l’operazione V(s) è stata

    eseguita;• nis: numero di volte che l’operazione P(s) è stata iniziata;• nps: numero di volte che l’operazione P(s) è stata

    completata;• bloccatis : numero dei processi sospesi sul semaforo

    Semaforo

  • 7

    Semaforo: relazione di Invarianza

    vals = Is + nvs – nps

    da cui (vals ≥0):nps ≤ Is + nvs relazione di invarianza

    Inoltre osserviamo che:bloccatis = nis – npsvals > 0 => bloccatis == 0bloccatis > 0 => vals == 0

  • 8

    Uso dei semafori

    • Il semaforo viene utilizzato come strumento disincronizzazione tra processi concorrenti:– sospensione: P(s), s==0– risveglio: V(s), se vi e` almeno un processo sospeso

    • Nel seguito verranno illustrati alcuni esempi notevoli di usodel meccanismo semaforico:– semafori di mutua esclusione– semafori evento– semafori binari composti– semafori condizione– semafori risorsa– semafori privati

  • 9

    Semafori di mutua esclusioneSemaforo binario. Può assumere solo i valori 0 e 1

    class tipo_risorsa {;semaphore mutex = 1;public void op1( ){ P(mutex); /*prologo*/

    ;V(mutex); /*epilogo*/

    }

    ...public void opn( ){ P(mutex);

    ;V(mutex);

    }}...tipo_risorsa ris;ris.opi();

  • 10

    • In alcuni casi è consentito a più processi di eseguirecontemporaneamente la stessa operazione su unarisorsa, ma non operazioni diverse.

    • Data la risorsa condivisa ris e indicate con op1, …,opn le n operazioni previste per operare su ris,vogliamo garantire che più processi possano eseguireconcorrentemente la stessa operazione opi mentrenon devono essere consentite esecuzionicontemporanee di operazioni diverse.

    Mutua esclusione tra gruppi di processi

  • 11

    Anche in questo caso lo schema è:

    public void opi() { ; ; ;

    }

    • prologoi deve sospendere il processo che ha chiamatol’operazione opi se sulla risorsa sono in esecuzione operazionidiverse da opi; diversamente deve consentire al processo dieseguire opi .

    • epilogoi deve liberare la mutua esclusione solo se il processoche lo esegue è l’unico processo in esecuzione sulla risorsa o èl’ultimo di un gruppo di processi che hanno eseguito la stessa opi .

  • 12

    public void opi( ) {P(mi);conti++;if (conti==1) P(mutex); V(mi);;P(mi);conti--;if (conti==0) V(mutex);V(mi);

    }

  • 13

    semaphore mutex = 1;semaphore ml = 1int contl = 0;public void lettura(…) {

    P(ml);contl++;if (contl==1) P(mutex);V(ml);;P(ml);contl--;if (contl==0) V(mutex);V(ml);

    }

    public void scrittura(…) {P(mutex);;V(mutex);

    }

    Esempio: Problema dei lettori/scrittori

  • 14

    Semafori evento:scambio di segnali temporali

    • semafori binari utilizzati per imporre un vincolo diprecedenza tra le operazioni dei processi.

    Esempio: opa deve essere eseguita da P1 solo dopoche P2 ha eseguito opb

    Introduciamo un semaforo sem inizializzato azero:• prima di eseguire opa, P1 esegue P(sem);• dopo aver eseguito opb, P2 esegue V(sem).

    a

  • 15

    P2 P2

    a) b)

    P(sem)

    opaopb

    P1 P2

    opaopb

    V(sem)

  • 16

    Due processi P1 e P2 eseguono ciascuno due operazionipa e pb il primo e qa e qb il secondo.

    • vincolo di rendez-vous : l’esecuzione di pb da partedi P1 e qb da parte di P2 possono iniziare solo dopo cheentrambi i processi hanno completato la loro primaoperazione.

    Scambio di segnali temporali in modo simmetrico: ogniprocesso quando arriva all’appuntamento segnala diesserci arrivato e attende l’altro.

    Introduciamo due semafori evento sem1 e sem2

    Problema del rendez-vous

  • 17

    P2

    P1 P(sem2)papb

    V(sem1)

    P(sem1)qa qbV(sem2)

  • 18

    Semafori binari composti:scambio di dati

    • Due processi P1 e P2 si scambiano dati di tipo Tutilizzando una memoria (buffer) condivisa .

    Vincoli di sincronizzazione:• Accessi al buffer mutuamente esclusivi.• P2 può prelevare un dato solo dopo che P1 lo ha

    inserito.• P1, prima di inserire un dato, deve attendere che

    P2 abbia estratto il precedente.

  • 19

    Scambio di dati: vincoli di precedenza

    P1

    P2

    ini+1ini

    esi+1esi

    • Utilizziamo due semafori:• vu, per realizzare l'attesa di P1, in caso di buffer pieno;• pn, per realizzare l'attesa di P2, in caso di buffer vuoto;

  • 20

    void invio(T dato) { T ricezione( ) {P(vu); T dato;inserisci(dato); P(pn);V(pn); dato=estrai();

    V(vu);} return dato;

    }

    buffer inizialmente vuotovalore iniziale vu= 1valore iniziale pn= 0

  • 21

    • pn e vn garantiscono da soli la mutua esclusione delleoperazioni estrai ed inserisci.

    • La coppia di semafori si comporta nel suo insiemecome se fosse un unico semaforo binario di mutuaesclusione.

    Semaforo binario composto: un insieme di semaforiusato in modo tale che:

    • uno solo di essi sia inizializzato a 1 e tutti glialtri a zero.

    • ogni processo che usa questi semafori eseguesempre sequenze che iniziano con la P su uno diquesti e termina con la V su un altro.

  • 22

    Semafori condizione In alcuni casi, l'esecuzione di un'istruzione S1 su una risorsa R e`

    subordinata al verificarsi di una condizione C:void op1( ): region R >

    • op1( ) è una regione critica, dovendo operare su una risorsacondivisa R.

    • S1 ha come precondizione la validità della condizione logica C.

    Il processo deve sospendersi se la condizione non è verificata edeve uscire dalla regione per consentire ad altri processi dieseguire altre operazioni su R per rendere vera la condizione C.

  • 23

    C

    S

    falsetrue

    C

    S

    true

    P(sem)

    V(sem)

    false

    (a) (b)

  • 24

    • Lo schema (a) presuppone una forma di attesa attivada parte del processo che non trova soddisfatta lacondizione.

    • Nello schema (b) si realizza la region sospendendo ilprocesso sul semaforo sem da associare allacondizione.(semaforo condizione)

    • E’ evidentemente necessaria un’altra operazione op2che, chiamata da un altro processo, modifichi lostato interno di R in modo che C diventi vera (C e`una post condizione di op2).

    • Nell’ambito di op2 viene eseguita la V(sem) perrisvegliare il processo.

  • 25

    Struttura dati della risorsa Rsemaphore mutex = 1;semaphore sem = 0;int csem=0

    public void op1( ){ P(mutex); while (!C) { csem ++;

    V(mutex);P(sem);P(mutex);

    } S1; V(mutex);}

    public void op2( ){ P(mutex); S2 ; if (csem>0) { csem --;

    V(sem) } V(mutex);}

    Schema con attesa circolare

  • 26

    Struttura dati della risorsa Rsemaphore mutex = 1;semaphore sem = 0;int csem = 0;

    public void op1( ){ P(mutex);

    if (!C) {csem ++;

    V(mutex);P(sem);csem --;

    }S1;V(mutex);

    }

    public void op2( ){ P(mutex);

    S2;if(csem>0)

    V(sem); else V(mutex);}

    Schema con passaggio di testimone

  • 27

    • Il secondo schema è più efficiente del primo, maconsente di risvegliare un solo processo alla voltapoichè ad uno solo può passare il diritto di operarein mutua esclusione.

    • La condizione C verificata all’interno di op1 deveessere verificabile anche all’interno di op2. Ciòsignifica che non deve contenere variabili locali oparametri della funzione op1.

    Semafori condizione:considerazioni sulle soluzioni

  • 28

    Esempio di uso di semafori condizione:Gestione di un pool di risorse equivalenti

    Si consideri un insieme (pool) di N risorse tutte uguali:• Ciascun processo può operare su una qualsiasi risorsa

    del pool purché libera.Necessità di un gestore che mantenga aggiornato lo stato

    delle risorse:1. Ciascun processo quando deve operare su una risorsa

    chiede al gestore l’allocazione di una di esse.2. Il gestore assegna al processo una risorsa libera (se

    esiste), in modo dedicato, passandogli l’indice relativo.3. Il processo opera sulla risorsa senza preoccuparsi

    della mutua esclusione.4. Al termine il processo rilascia la risorsa al gestore.

  • 29

    int richiesta(): region G >

    void rilascio(int r): region G >

    Gestione di un pool di risorse equivalenti:operazioni del gestore

  • 30

    class tipo_gestore{ semaphore mutex = 1; /*sem. di mutua esclusione*/

    semaphore sem = 0; /*semaforo condizione*/int csem = 0; /*contatore dei proc. sospesi su sem */boolean libera[N]; /*indicatori di risorsa libera*/int disponibili = N; /*contatore risorse libere*//*inizializzazione:*/{for(int i=0; i < N; i++)libera[i]=true;}public int richiesta(){ int i=0;

    P(mutex); if (disponibili == 0)

    { csem ++;V(mutex);P(sem):csem --; }

    while(!libero[i]) i++; libero[i]=false;

    disponibili --;V(mutex);return i;

    } /* continua..*/

    Realizzazione:

  • 31

    public void rilascio (int r){ P(mutex);

    libero[r]=true;disponibili ++;

    if (csem>0) V(sem); else V(mutex);

    } } /* fine classe tipo_gestore */

  • 32

    tipo_gestore G; /* def. gestore*/

    /*struttura del generico processo che vuole accedere auna risorsa del pool: */process P{

    int ris;...ris = G.richiesta( );

    < uso della risorsa di indice ris>G.rilascio (ris) ;...

    }

  • 33

    Semafori risorsa

    • Semafori generali. Possono assumere qualunquevalore ≥ 0.

    • Vengono impiegati per realizzare l'allocazione dirisorse equivalenti: il valore del semafororappresenta il numero di risorse libere.

    Esempio: gestione di un pool di risorse equivalenti.• Unico semaforo n_ris inizializzato con un valore

    uguale al numero di risorse da allocare.

    • Esecuzione di P(n_ris) in fase di allocazione e diV(n_ris) in fase di rilascio

  • 34

    class tipo_gestore {semaphore mutex = 1; /*semaforo di mutua esclusione*/semaphore n_ris = N; /*semaforo risorsa*/boolean libero[N]; /*indicatori di risorsa libera*/

    {for(int i=0; i < N; i++)libera[i]=true;} /*inizializzazione*/

    public int richiesta() { int i=0;

    P(n_ris); P(mutex);

    while(libero[i]==false) i++; libero[i]=false;

    V(mutex);return i; }

    public void rilascio (int r) { P(mutex); libero[r]=true;

    V(mutex);V(n_ris); }

    }

  • 35

    Buffer di n elementi (di tipo T), strutturato come una coda:

    coda_di_n_T buffer;semaphore pn = 0;semaphore vu = n;semaphore mutex = 1;

    void invio(T dato) { T ricezione() {P(vu); T dato;P(mutex); P(pn);buffer.inserisci(dato); P(mutex);V(mutex); dato = buffer.estrai();V(pn); V(mutex);

    } V(vu);return dato; }

    •il produttore richiede l'allocazione di una risorsa "elemento vuoto"•il consumatore richiede l'allocazione di una risorsa "elemento pieno"

    Semafori risorsa: problema deiproduttori/consumatori

  • 36

    Realizzazione del buffer: coda circolare.

    class coda_di_n_T {T vettore[n];int primo = 0;ultimo = 0;

    public void inserisci(T dato) {vettore[ultimo] = dato;ultimo = (ultimo+1)%n;

    }

    public T estrai( ) {T dato= vettore[primo];primo = (primo+1)%n;return dato;

    }}

  • 37

    Semafori privati: specifica di strategie diallocazione

    Condizione di sincronizzazione: Qualora si voglia realizzare una determinata politica

    di gestione delle risorse, la decisione se ad un datoprocesso sia consentito proseguire l'esecuzionedipende dal verificarsi di una condizione, dettacondizione di sincronizzazione.

    • La condizione è espressa in termini di variabili cherappresentano lo stato della risorsa e di variabililocali ai singoli processi.

    • Più processi possono essere bloccati durante l’accessoad una risorsa condivisa, ciascuno in attesa che lapropria condizione di sincronizzazione sia verificata.

  • 38

    • In seguito alla modifica dello stato della risorsa daparte di un processo, le condizioni di sincronizzazione dialcuni processi bloccati possono esserecontemporaneamente verificate.

    Problema: quale processo mettere in esecuzione (accessoalla risorsa mutuamente esclusivo)? Definizione di una politica per il risveglio dei

    processi bloccati.

    • Nei casi precedenti la condizione di sincronizzazione eraparticolarmente semplificata (vedi mutua esclusione) ela scelta di quale processo riattivare veniva effettuatatramite l’algoritmo implementato nella V.

    • Normalmente questo algoritmo, dovendo esseresufficientemente generale ed il più possibile efficiente,coincide con quello FIFO.

  • 39

    Su un buffer da N celle di memoria più produttoripossono depositare messaggi di dimensione diversa.

    Politica di gestione: tra più produttori ha priorità diaccesso quello che fornisce il messaggio di dimensionemaggiore. finché un produttore il cui messaggio ha dimensioni

    maggiori dello spazio disponibile nel buffer rimanesospeso, nessun altro produttore può depositare unmessaggio anche se la sua dimensione potrebbeessere contenuta nello spazio libero del buffer.

    Condizione di sincronizzazione: il deposito può avvenire sec’è sufficiente spazio per memorizzare il messaggio enon ci sono produttori in attesa.

    Esempio 1

  • 40

    Il prelievo di un messaggio da parte di un consumatoreprevede la riattivazione tra i produttori sospesi, diquello il cui messaggio ha la dimensione maggiore,semprechè esista sufficiente spazio nel buffer.

    Se lo spazio disponibile non è sufficiente nessunproduttore viene riattivato.

  • 41

    Un insieme di processi utilizza un insieme di risorsecomuni R1,R2,..Rn. Ogni processo può utilizzare unaqualunque delle risorse.

    Condizione di sincronizzazione: l'accesso e` consentitose esiste una risorsa libera.

    • A ciascun processo è assegnata una priorità.

    • In fase di riattivazione dei processi sospesi viene sceltoquello cui corrisponde la massima priorità

    Esempio 2

  • 42

    • Con riferimento al problema dei lettori & scrittori, siintende realizzare una politica di gestione che eviticondizioni di attesa indefinita (starvation) perentrambe le classi di processi.

    Esempio 3

  • 43

    SEMAFORO PRIVATO

    Un semaforo s si dice privato per un processo quandosolo tale processo può eseguire la primitiva P sulsemaforo s.

    La primitiva V sul semaforo può essere invece eseguitaanche da altri processi.

    Un semaforo privato viene inizializzato con il valorezero.

  • 44

    Uso dei semafori privati

    I semafori privati possono essere utilizzati perrealizzare particolari politiche di allocazione dirisorse:– il processo che acquisisce la risorsa puo` (se la

    condizione di sincronizzazione non e`soddisfatta) eventualmente sospendersi sul suosemaforo privato

    – chi rilascia la risorsa, risvegliera` uno tra iprocessi sospesi (in base alla politica scelta)mediante una V sul semaforo privato delprocesso prescelto.

  • 45

    class tipo_gestore_risorsa{;semaphore mutex =1;semaphore priv[n] = {0,0,..0}; /*semafori privati*/

    public void acquisizione (int i){ P(mutex);

    if(){;V(priv[i]);

    }else

    ;V(mutex);P(priv[i]);

    }

    Allocazione di risorse con particolari strategie: primo schema

  • 46

    public void rilascio( ){ int i;

    P(mutex);;if (){ ; ; ; V(priv[i]);

    }V (mutex);

    }}

  • 47

    Proprieta` della soluzione:

    a) La sospensione del processo, nel caso in cui lacondizione di sincronizzazione non sia soddisfatta, nonpuò avvenire entro la sezione critica in quanto ciòimpedirebbe ad un processo che rilascia la risorsa diaccedere a sua volta alla sezione critica e di riattivareil processo sospeso.

    La sospensione avviene al di fuori della sezione critica.

    b) La specifica del particolare algoritmo di assegnazionedella risorsa non è opportuno che sia realizzata nellaprimitiva V. Nella soluzione proposta è possibileprogrammare esplicitamente tale algoritmo scegliendoin base ad esso il processo da attivare ed eseguendo Vsul suo semaforo privato.

    Allocazione di risorse

  • 48

    Lo schema presentato può, in certi casi, presentaredegli inconvenienti.

    1. l’operazione P sul semaforo privato viene sempreeseguita anche quando il processo richiedente nondeve essere bloccato.

    2. Il codice relativo all’assegnazione della risorsa vieneduplicato nelle procedure acquisizione e rilascio

    Si può definire uno schema che non ha questiinconvenienti.

  • 49

    class tipo_gestore_risorsa{;semaphore mutex = 1;semaphore priv[n] = {0,0,..0}; /*semafori privati */

    public void acquisizione (int i){ P(mutex);

    if(! ){ ; V(mutex);

    P(priv[i]); ;};V(mutex);

    }

    Allocazione di risorse: secondo schema

  • 50

    public void rilascio( ){ int i;

    P(mutex);;if (){ ;

    V(priv[i]);}else V(mutex);

    }}

    il risveglio segue lo schema del passaggio di testimone

  • 51

    A differenza della soluzione precedente, tuttavia, inquesto caso risulta più complesso realizzare lariattivazione di più processi per i quali risulti veracontemporaneamente la condizione di sincronizzazione.

    Lo schema prevede infatti che il processo che rilascia larisorsa attivi al più un processo sospeso, il quale dovrà asua volta provvedere alla riattivazione di eventuali altriprocessi.

    Commento

  • 52

    class buffer {int richiesta[num_proc]=0;/*richiesta[i]= numero di

    celle richieste da Pi*/int sospesi=0; /*numero dei processi prod.sospesi*/int vuote=n; /*numero di celle vuote del buffer*/semaphore mutex=1;semaphore priv[num_proc]={0,0,..0};

    public void acquisizione(int m, int i)/* m dim. messaggio, i nome del processo chiamante */{ P(mutex);

    if (sospesi==0 && vuote>=m){ vuote=vuote-m;

    ;V(priv[i]);

    }else{ sospesi++;

    richiesta[i]=m;}V(mutex);P(priv[i]);

    }

    Soluzione ai problemi precedenti: esempio 1

  • 53

    public void rilascio(int m) /* m num. celle rilasciate*/{ int k;

    P(mutex);vuote+=m;while (sospesi!=0){ ;if (richiesta[k]

  • 54

    Soluzione esempio 2Soluzione: introduzione delle seguenti variabili:

    • PS[i]: variabile logica che assume il valore vero se ilprocesso Pi è sospeso; il valore falso diversamente.

    • libera[j]: variabile logica che assume il valore falso se larisorsa j-esima è occupata; il valore vero diversamente.

    • disponibili: esprime il numero delle risorse non occupate;• sospesi: e` il numero dei processi sospesi;• mutex: semaforo di mutua esclusione• priv[i]: il semaforo privato del processo Pi.

  • 55

    class tipo_gestore {semaphore mutex=1;semaphore priv[num_proc]={0,0,…0} /*sem. privati */int sospesi=0; /*numero dei processi sospesi*/boolean PS[num_proc]={false, false,…, false}; int disponibili=num_ris; /*numero di risorse disp.*/boolean libera[num_ris]={true, true,…, true};

    public int richiesta(int proc){ int i =0;

    P(mutex); if (disponibili==0)

    { sospesi ++;PS[proc]=true;V(mutex);P(priv[proc]);PS[proc]=false;sospesi -- ;

    } while (! libera[i]) i++; libera[i]=false; disponibili--; V(mutex ); return i; }

  • 56

    public void rilascio (int r)/* r indice risorsa ril. */ { P(mutex);

    libera[r]=true;disponibili++;if (sospesi>0){ ; V(priv[j]);}else V(mutex);

    }}

  • 57

    • Ogni processo che nella fase di acquisizione della risorsatrova la condizione di sincronizzazione non soddisfatta,deve lasciare traccia in modo esplicito della suasospensione entro la sezione critica.

    • Il processo che libera la risorsa deve infatti eseguire laprimitiva V(priv(i)) solo se esistono processi sospesi. Intutte le soluzioni è stata introdotta un’apposita variabileper indicare il numero dei processi sospesi.

    • La fase di assegnazione di una risorsa ad un processo èseparata dalla fase di uso della risorsa stessa.

    • Occorre quindi lasciare traccia in modo esplicito entro lasezione critica della assegnazione e quindi della nondisponibilità della risorsa. Nel primo esempio vienedecrementata la variabile vuote; nel secondo vienemodificata la variabile R.

    Considerazioni sulle soluzioni presentate

  • 58

    Realizzazione dei semafori

    vals ==0

    vals --

    no

    si

    P(s)

    bloccatis>0

    vals ++

    si

    no

    V(s)

    Introduciamo una possibile implementazione deisemafori, caratterizzata dall'assenza di attesaattiva.

    E` possibile definire la P e la V nel modo seguente,garantendo comunque la validita` delle proprieta`del semaforo (s.p.d).

  • 59

    Descrittore di un semaforo:typedef struct{

    int contatore;coda_a_livelli coda;}des_semaforo;

    Insieme di tutti i descrittori: des_semaforo semafori[num_max_sem];

    Identificazione di un semaforo:typedef int semaphore;

  • 60

    Hp: interruzioni disabilitate

    Implementazione dell'operazione P:

    void P(semaphore s){ p_des esec = processo_in_esecuzione;

    int pri= esec -> servizio.priorità;if(semafori[s].contatore==0){ esec -> stato = < “sospeso sul semaforo s”>; inserimento (esec,semafori[s].coda[pri]); assegnazione_CPU;}else contatore--;

    }

    Ambiente monoprocessore

  • 61

    Implementazione dell'operazione V:

    void V (semaphore s){ p_des esec = processo_in_esecuzione; p_des p; int pri=0; while(semafori[s].coda[pri].primo==NULL &&

    pri < min_priorità)pri++;

    if(semafori[s].coda[pri].primo!=NULL) { p=prelievo(semafori[s].coda[pri])

    attiva(p); } else semafori[s].contatore ++;}

  • 62

    boolean test_and_set( boolean *a){ boolean test=*a;

    *a=true;return test;

    }

    void lock(boolean * x){ while (test_and-set(x));}

    void unlock(boolean * x){ x=false;}

    Ambiente multiprocessore

  • 63

    x=false x ==true

    x=true

    no

    si

    unlock(x) lock(x)

  • 64

    Strumenti di sincronizzazione nella libreriaLinuxThread e nel linguaggio Java

  • 65

    I semafori nelle librerie pthread eLinuxThreads

    • La libreria pthread definisce soltanto il semaforobinario (mutex).

    • La Libreria Linuxthread, implementa comunque ilsemaforo esternamente alla libreria pthread,conformemente allo standard POSIX 1003.1b

  • 66

    pthread: MUTEX

    • Lo standard POSIX 1003.1c (libreria ) definisce isemafori binari (o lock, mutex, etc.)– sono semafori il cui valore puo` essere 0 oppure 1 (occupato o

    libero);– vengono utilizzati tipicamente per risolvere problemi di mutua

    esclusione– operazioni fondamentali:

    • inizializzazione: pthread_mutex_init• locking: pthread_mutex_lock• unlocking: pthread_mutex_unlock

    – Per operare sui mutex:pthread_mutex_t : tipo di dato associato al mutex; esempio:

    pthread_mutex_t mux;

  • 67

    • L'inizializzazione di un mutex si puo`realizzare con:

    int pthread_mutex_init(pthread_mutex_t* mutex, constpthread_mutexattr_t* attr)

    attribuisce un valore iniziale all'intero associato alsemaforo (default: libero):• mutex : individua il mutex da inizializzare• attr : punta a una struttura che contiene gli attributi del mutex;

    se NULL, il mutex viene inizializzato a libero (default).

    – in alternativa , si puo` inizializzare il mutex a default con la macro:PTHREAD_MUTEX_INIZIALIZER

    – esempio: pthread_mutex_t mux= PTHREAD_MUTEX_INIZIALIZER ;

    MUTEX: inizializzazione

  • 68

    • Locking/unlocking si realizzano con:

    int pthread_mutex_lock(pthread_mutex_t* mux)int pthread_mutex_unlock(pthread_mutex_t* mux)

    – lock: se il mutex mux e` occupato,il threadchiamante si sospende; altrimenti occupa ilmutex.

    – unlock: se vi sono processi in attesa del mutex,ne risveglia uno; altrimenti libera il mutex.

    MUTEX: lock/unlock

  • 69

    Esempio#include #include #include #define MAX 10pthread_mutex_t M; /* def.mutex condiviso tra threads */int DATA=0; /* variabile condivisa */int accessi1=0; /*num. di accessi del thread 1 alla sez critica */int accessi2=0; /*num. di accessi del thread 2 alla sez critica */

    void *thread1_process (void * arg){ int k=1; while(k) { pthread_mutex_lock(&M); /*prologo */ accessi1++;

    DATA++; k=(DATA>=MAX?0:1); printf("accessi di T1: %d\n", accessi1); pthread_mutex_unlock(&M); /*epilogo */ } pthread_exit (0);}

  • 70

    Esempiovoid *thread2_process (void * arg){ int k=1; while(k) { pthread_mutex_lock(&M); /*prologo sez. critica */ accessi2++; DATA++; k=(DATA>=MAX?0:1); printf("accessi di T2: %d\n", accessi2);

    pthread_mutex_unlock(&M); /*epilogo sez. critica*/ } pthread_exit (0);}

  • 71

    Esempio:

    main(){ pthread_t th1, th2; /* il mutex e` inizialmente libero: */ pthread_mutex_init (&M, NULL);

    if (pthread_create(&th1, NULL, thread1_process, NULL) <0)

    { fprintf (stderr, "create error for thread 1\n"); exit (1); } if (pthread_create(&th2, NULL,thread2_process,NULL) < 0) { fprintf (stderr, "create error for thread 2\n"); exit (1); }

    pthread_join (th1, NULL); pthread_join (th2, NULL);}

  • 72

    Test

    $$ gcc -D_REENTRANT -o tlock lock.c -lpthread$ ./tlockaccessi di T2: 1accessi di T1: 1accessi di T2: 2accessi di T1: 2accessi di T1: 3accessi di T1: 4accessi di T1: 5accessi di T1: 6accessi di T1: 7accessi di T1: 8accessi di T2: 3$

  • 73

    LinuxThreads: Semafori

    Memoria condivisa: uso dei semafori (POSIX.1003.1b)– Semafori: libreria

    • sem_init: inizializzazione di un semaforo• sem_wait: implementazione di P• sem_post: implementazione di V

    – sem_t : tipo di dato associato al semaforo; esempio:

    static sem_t my_sem;

  • 74

    Operazioni sui semafori– sem_init: inizializzazione di un semaforo int sem_init(sem_t *sem, int pshared, unsigned int value)

    attribuisce un valore iniziale all'intero associato al semaforo:

    • sem: individua il semaforo da inizializzare• pshared : 0, se il semaforo non e` condiviso tra task, oppure non zero

    (sempre zero).• value : e` il valore iniziale da assegnare al semaforo.

    – sem_t : tipo di dato associato al semaforo; esempio:

    static sem_t my_sem;

    ritorna sempre 0.

  • 75

    Operazioni sui semafori: sem_wait– P su un semaforo

    int sem_wait(sem_t *sem)

    dove:• sem: individua il semaforo sul quale operare.

    e` la P di Dijkstra: se il valore del semaforo e` uguale a zero, sospende il thread chiamante nella

    coda associata al semaforo; altrimenti ne decrementa il valore.

  • 76

    Operazioni sui semafori: sem_post

    – V su un semaforo: int sem_post(sem_t *sem)

    dove:• sem: individua il semaforo sul quale operare.

    e` la V di Dijkstra: se c'e` almeno un thread sospeso nella coda associata al semaforo sem, viene

    risvegliato; altrimenti il valore del semaforo viene incrementato.

  • 77

    Esempio: Semaforo Evento

    Imposizione di un vincolo temporale: la FASE2 nel thread 1 va eseguitadopo la FASE1 nel thread2.

    Thread 1 Thread 2

    Fase 1

    Fase 2

  • 78

    Esempio: sincronizzazione/* la FASE2 nel thread 1 va eseguita dopo la FASE1 nel thread 2*/#include #include #include #include

    sem_t my_sem;int V=0;

    void *thread1_process (void * arg){ printf ("Thread 1: partito!...\n");

    /* inizio Fase 2: */ sem_wait (&my_sem); printf ("FASE2: Thread 1: V=%d\n", V); pthread_exit (0);}

  • 79

    void *thread2_process (void * arg){ int i;

    V=99; printf ("Thread 2: partito!...\n); /* inizio fase 1: */ printf (“FASE1: Thread 2: V=%d\n", V); /* … termine Fase 1: sblocco il thread 1*/ sem_post (&my_sem); sleep (1);

    pthread_exit (0);}

  • 80

    main (){ pthread_t th1, th2; void *ret; sem_init (&my_sem, 0, 0); /* semaforo a 0 */

    if (pthread_create (&th1, NULL, thread1_process, NULL) < 0) {fprintf (stderr, "pthread_create error for thread 1\n");

    exit (1); }

    if (pthread_create(&th2,NULL, thread2_process, NULL) < 0){fprintf (stderr, "pthread_create error for thread \n");

    exit (1); }

    pthread_join (th1, &ret); pthread_join (th2, &ret);}

  • 81

    Esempio:

    • gcc -D_REENTRANT -o sem sem.c –lpthread

    • Esecuzione:[aciampolini@ccib48 threads]$ semThread 1: partito!...Thread 2: partito!...FASE1: Thread 2: V=99FASE2: Thread 1: V=99[aciampolini@ccib48 threads]$

  • 82

    Semafori: esempio/* tre processi che, ciclicamente, incrementano a turno (in

    ordine P1,P2,P3) la variabile V*/

    #include #include #include #include #define MAX 13static sem_t m; /* semaforo per la mutua esclusione

    nell’accesso alla sezione critica */static sem_t s1,s2,s3; /* semafori per imporre

    l’ordine di accesso (P1,P2,P3) allavariabile V */

    int V=0,F=0;

  • 83

    void *thread1_process (void * arg){ int k=1; while(k) { sem_wait (&s1); sem_wait(&m); if (V

  • 84

    void *thread2_process (void * arg){ int k=1; while(k) { sem_wait (&s2); sem_wait(&m); if (V

  • 85

    void *thread3_process (void * arg){ int k=1; while(k) { sem_wait (&s3); sem_wait(&m); if (V

  • 86

    main (){ pthread_t th1, th2,th3;

    sem_init (&m, 0, 1); sem_init(&s1,0,1); sem_init(&s2,0,0); sem_init(&s3,0,0); if (pthread_create(&th1, NULL, thread1_process, NULL) < 0)

    { fprintf (stderr, "pthread_create error for thread 1\n"); exit (1); } if (pthread_create(&th2, NULL,thread2_process,NULL) < 0) { fprintf (stderr, "pthread_create error for thread 2\n"); exit (1); } if (pthread_create(&th3,NULL,thread3_process, NULL) < 0)

    { fprintf (stderr, "pthread_create error for thread 3\n"); exit (1); }

  • 87

    pthread_join (th1, NULL);pthread_join (th2, NULL);pthread_join (th3, NULL);

    }

  • 88

    Sincronizzazione in Java

    Modello a memoria comune:I threads di una applicazione condividono lo spaziodi indirizzamento.

    Ogni tipo di interazione tra thread avviene tramiteoggetti comuni:– Interazione di tipo competitivo (mutua

    esclusione): meccanismo degli objects locks.– Interazione di tipo cooperativo:

    • meccanismo wait-notify.• variabili condizione

  • 89

    Semafori in Java

    • Non esiste una classe che implementi i semafori.E` possibile implementarli sfruttando altristrumenti di sincronizzazione (ad es. wait/notify,Variabili Condizione).

    • Il linguaggio prevede costrutti specifici perrisolvere il problema della mutua esclusione:– statement syncronized

  • 90

    Mutua esclusione

    • Ad ogni oggetto viene associato dalla JVM un lock(analogo ad un semaforo binario).

    • E’ possibile denotare alcune sezioni di codice cheoperano su un oggetto come sezioni critichetramite la parola chiave synchronized.

    Il compilatore inserisce :– un prologo in testa alla sezione critica per

    l’acquisizione del lock associato all’oggetto.– un epilogo alla fine della sezione critica per

    rilasciare il lock.

  • 91

    Blocchi synchronizedCon riferimento ad un oggetto x si può definire unblocco di statement come una sezione critica nelseguente modo (synchronized blocks):

    synchronized (oggetto x) {;}

    Esempio:Object mutexLock= new Object;

    …..public void M( ) { ; synchronized (mutexlock){ < sezione di codice critica>;

    } ;}

  • 92

    • all'oggetto mutexLock viene implicitamenteassociato un lock, il cui valore puo` essere:– libero: il thread può eseguire la sezione critica– occupato: il thread viene sospeso dalla JVM in

    una coda associata a mutexLock (entry set).

    Al termine della sezione critica:– se non ci sono thread in attesa: il lock viene

    reso libero .– se ci sono thread in attesa: il lock rimane

    occupato e viene scelto uno di questi .

  • 93

    synchronized block

    • esecuzione del blocco mutuamente esclusivarispetto:– ad altre esecuzioni dello stesso blocco– all’esecuzione di altri blocchi sincronizzati sullo

    stesso oggetto

  • 94

    lock

    Object ob

    synchronized (ob){ ………… ;}t1

    a) lock libero

    lock

    Object ob

    synchronized (ob){ ………… ;} t1

    a) lock occupato: t2e t3 vengono inseritinell'entry set di ob

    t2

    t3

    Entry set di un oggetto

  • 95

    Metodi synchronized• Mutua esclusione tra i metodi di una classe

    public class intVar {private int i=0;public synchronized void incrementa(){ i ++; }public synchronized void decrementa(){i--; }}

    • Quando un metodo viene invocato per operare suun oggetto della classe, l’esecuzione del metodoavviene in mutua esclusione utilizzando il lockdell’oggetto.