Operating Systems and Distributed Systems
Sincronizzazione di processi e thread (1)
#define NITER 1000000
int count = 0;
void *ThreadAdd(void *); /*thread’s function prototype */
int main(int argc, char * argv[])
{
pthread_t tid1, tid2;!
pthread_create(&tid1, NULL, ThreadAdd, NULL); /* creates thread 1 */!
pthread_create(&tid2, NULL, ThreadAdd, NULL); /* creates thread 2 */ !
pthread_join(tid1, NULL);! /* wait for the thread 1 to finish */ !
pthread_join(tid2, NULL); /* wait for the thread 2 to finish */
!
if (count < 2 * NITER) printf("\n BOOM! count is [%d], should be %d\n", count, 2*NITER);
else printf("\n OK! count is [%d]\n", count);
!
pthread_exit(NULL);
}
void *ThreadAdd(void * a) /*thread’s function implementation */
{
int i, tmp;
for(i = 0; i < NITER; i++){
tmp = count; /* copy the global count locally */
tmp = tmp+1; /* increment the local copy */
count = tmp; /* store the local value into the global count */
}
}
Operating Systems and Distributed Systems 2
Problemi con la concorrenza
host:pthreadsync Bebo$ ./p_thradd
BOOM! il conteggio e! [1008904], invece di 2000000
host:pthreadsync Bebo$ ./p_thradd
BOOM! il conteggio e! [1050931], invece di 2000000
host:pthreadsync Bebo$ ./p_thradd
BOOM! il conteggio e! [1014420], invece di 2000000
host:pthreadsync Bebo$ ./p_thradd
BOOM! il conteggio e! [1001119], invece di 2000000
host:pthreadsync Bebo$ ./p_thradd
BOOM! il conteggio e! [1009707], invece di 2000000
Operating Systems and Distributed Systems 3
Compile & run p_thradd.c!
Risultati
aleatori?
Problemi con la concorrenza
Operating Systems and Distributed Systems
Il problema della concorrenza
4
• Sincronizzazione: necessaria in condizioni di concorrenza.
• Concorrenza: due o più processi/thread sono in esecuzione nello stesso intervallo di tempo e potenzialmente interagiscono l’uno con l’altro (e.g., condivisione di risorse).
Race condition
Criticalsection
Operating Systems and Distributed Systems 5
• Race condition: La manipolazione simultanea della stessa risorsa da parte di due o più P/T causa risultati inconsistenti.
• Sezione critica (Critical section): Segmento di codice che coordina l’accesso a una risorsa condivisa.
• Mutua esclusione (Mutual exclusion): proprietà del software che assicura accesso esclusivo a una risorsa condivisa.
• Deadlock: condizione speciale di stallo creata da due o più P/T e due o più locks su risorse che bloccano il lavoro.
Concetti importanti
Thread 1 Thread 2 count
tmp = count; --- 0
tmp = tmp +1; --- 0
--- tmp = count;!!// 0 0
--- tmp = tmp + 1; 0
count = tmp;!!// 1 --- 1
--- count = tmp;!!// 1 1
Operating Systems and Distributed Systems 6
tmp = count;
tmp = tmp+1;
count = tmp;
Thread 1 Thread 2
tmp = count;
tmp = tmp+1;
count = tmp;
Race condition: esempio con pthread
Thread 1 Thread 2 count
tmp = count; --- 0
tmp = tmp +1; --- 0
--- tmp = count;!!// 0 0
--- tmp = tmp + 1; 0
count = tmp;!!// 1 --- 1
--- count = tmp;!!// 1 1
Operating Systems and Distributed Systems 7
tmp = count;
tmp = tmp+1;
count = tmp;
Thread 1 Thread 2
tmp = count;
tmp = tmp+1;
count = tmp;
Race condition: esempio con pthread
Thread 1 Thread 2 count
tmp = count; --- 0
tmp = tmp +1; --- 0
--- tmp = count;!!// 0 0
--- tmp = tmp + 1; 0
count = tmp;!!// 1 --- 1
--- count = tmp;!!// 1 1
Operating Systems and Distributed Systems 8
tmp = count;
tmp = tmp+1;
count = tmp;
Thread 1 Thread 2
tmp = count;
tmp = tmp+1;
count = tmp;
Race condition: esempio con pthread
Thread 1 Thread 2 count
tmp = count; --- 0
tmp = tmp +1; --- 0
--- tmp = count;!!// 0 0
--- tmp = tmp + 1; 0
count = tmp;!!// 1 --- 1
--- count = tmp;!!// 1 1
Operating Systems and Distributed Systems 9
tmp = count;
tmp = tmp+1;
count = tmp;
Thread 1 Thread 2
tmp = count;
tmp = tmp+1;
count = tmp;
Race condition: esempio con pthread
Thread 1 Thread 2 count
tmp = count; --- 0
tmp = tmp +1; --- 0
--- tmp = count;!!// 0 0
--- tmp = tmp + 1; 0
count = tmp;!!// 1 --- 1
--- count = tmp;!!// 1 1
Operating Systems and Distributed Systems 10
tmp = count;
tmp = tmp+1;
count = tmp;
Thread 1 Thread 2
tmp = count;
tmp = tmp+1;
count = tmp;
Race condition: esempio con pthread
Thread 1 Thread 2 count
tmp = count; --- 0
tmp = tmp +1; --- 0
--- tmp = count;!!// 0 0
--- tmp = tmp + 1; 0
count = tmp;!!// 1 --- 1
--- count = tmp;!!// 1 1
Operating Systems and Distributed Systems 11
tmp = count;
tmp = tmp+1;
count = tmp;
Thread 1 Thread 2
tmp = count;
tmp = tmp+1;
count = tmp;
Race condition: esempio con pthread
Operating Systems and Distributed Systems
Meccanismi di sincronizzazione
Operating Systems and Distributed Systems13
Problema: race condition
• I thread P1 e P2 scrivono nella memoria condivisa
• Problema: non è possibile garantire che la read seguita da una write sia atomica
– L’ordine di esecuzione ha importanza!
• Necessario eliminare le race condition…
R1 <= x
R1 = R1+1
R1 => x
R3 <= x
R3 = R3+1
R3 => x
P1 P2x=3
x=5R1 <= x
R1 = R1+1
R1 => x
R3 <= x
R3 = R3+1
R3 => x
x=6!
LOAD
LOAD
STORE
STORE
STORE
LOAD
LOAD
STORE
Operating Systems and Distributed Systems
Il problema del latte
Arriva a casa
Frigorifero: niente latte
Va a comprare in latteria
arriva in latteria
Compra latte
Arriva a casa: mette in frigo
Arriva a casa
Frigorifero: niente latte
Va a comprare in latteria
Compra latte
Arriva a casa: mette in frigo
Primo studente Secondo studenteOra
Operating Systems and Distributed Systems
• Si assume di avere delle load/store atomiche
• Lascia un biglietto (lock)
• Togli il biglietto (unlock)
• Non comprare latte se c’è un biglietto (wait)
Il problema del latte
Operating Systems and Distributed Systems
Il problema del latte: soluzione 1
troppo latte!Compile & run p_milknota.c!
Operating Systems and Distributed Systems
Il problema del latte: soluzione 2
niente latte! (starvation)
usiamo bigliettini etichettati
Compile & run p_milknota2.c!
Operating Systems and Distributed Systems
Il problema del latte: soluzione 3aspetta il biglietto giusto
Operating Systems and Distributed Systems
Il problema del latte: soluzione 3aspetta il biglietto giusto
prima A e poi B
Operating Systems and Distributed Systems
Il problema del latte: soluzione 3aspetta il biglietto giusto
prima B e poi A
Operating Systems and Distributed Systems
Il problema del latte: soluzione 3aspetta il biglietto giusto
interleaving: A aspetta e poi compra
Operating Systems and Distributed Systems
Il problema del latte: soluzione 3aspetta il biglietto giusto
interleaving: A aspetta, B compra
Compile & run p_milknota3.c!
• Si definiscono delle regioni critiche (RC) a cui si applica la mutual exclusion per governare la corsa
Operating Systems and Distributed Systems
non_critical_sectionENTER_REGIONcritical_sectionLEAVE_REGIONnon_critical_section
{ }
Protezione regioni critiche
Operating Systems and Distributed Systems24
Process A
Process B B blocked
A enterscritical region
B tries to entercritical region
B enterscritical region
A leavescritical region
B leavescritical region
Time
Protezione regioni critiche • Quattro condizioni da soddisfare
1. Non ci devono essere 2 processi simultaneamente in RC
2. Non si devono fare ipotesi su velocità e numero CPU
3. Nessun processo che esegue fuori dalla sua RC può bloccare un altro processo
4. Un processo non dovrebbe attendere indefinitamente per entrare nella sua RC
Operating Systems and Distributed Systems
Meccanismi di sincronizzazione: Locks
Operating Systems and Distributed Systems26
Locks a livello pthread: Mutex• Mutual exclusion lock:
– Blocca l’accesso a variabili da parte di threads:
• I Threads che vogliono l’accesso a risorse protette da un mutex devono attendere finchè il thread attivo finisca ed esegua l’ unlock del mutex.
• Usati per serializzare l’accesso a una risorsa condivisa..
• Possono essere applicati a threads in un single processo e non fra processi (devo usare semafori).
non_critical_sectionpthread_mutex_lock( &mutex1 )
critical_sectionpthread_mutex_unlock( &mutex1 )
non_critical_section
{ }
int counter=0;
/* Function C */
void functionC(){
! counter++!}
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
int counter=0;
/* Function C */
void functionC()
{
! pthread_mutex_lock( &mutex1 );
! counter++
! pthread_mutex_unlock( &mutex1 );
}
Operating Systems and Distributed Systems27
senza Mutex con Mutex
Thread 1 Thread 2
counter = 0
counter = 1
counter = 0
counter = 1
Thread 1 Thread 2
counter = 0
counter = 1
counter = 0
counter = 2
Thread 2 locked out.Thread 1 ha uso esclusivo di counter
Locks a livello pthread: Mutex
Operating Systems and Distributed Systems 28
pthread_mutex_t myMutex; //dichiarazione
myMutex = PTHREAD_MUTEX_INITIALIZER; //inizializzazione
//statica
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutex_attr_t *attr);
//inizializzazione dinamica
// attr=NULL se si usano attributi // di default
Locks a livello pthread: Mutex
Operating Systems and Distributed Systems 29
• lock / unlock
– pthread_mutex_lock: blocca la calling function fino a pthread_mutex_unlock.
– più di un thread waiting sullo stesso mutex: la pthread library usa la scheduling priority dei thread in attesa
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
Locks a livello pthread: Mutex
• lock without blocking,
int pthread_mutex_trylock(pthread_mutex_t *mutex);
Operating Systems and Distributed Systems 30
• deallocate
int pthread_mutex_destroy(pthread_mutex_t *mutex);
Locks a livello pthread: Mutex
#define NITER 1000000
int count = 0;
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; //initialize a mutex object statically
void * ThreadAdd(void *); //function executed by threads
int main(int argc, char * argv[]){
pthread_t tid1, tid2;
!
pthread_create(&tid1, NULL, ThreadAdd, NULL))!
pthread_create(&tid2, NULL, ThreadAdd, NULL))
pthread_join(tid1, NULL))! /* wait for the thread 1 to finish */
pthread_join(tid2, NULL)) /* wait for the thread 2 to finish */!
if (count < 2 * NITER) printf("\n BOOM! count is [%d], should be %d\n", count, 2*NITER);
else printf("\n OK! count is [%d]\n", count);!
! // Clean up the mutex
! pthread_mutex_destroy(&mutex1);
pthread_exit(NULL);
}
void * ThreadAdd(void * a){
int i, tmp;
for(i = 0; i < NITER; i++){
! pthread_mutex_lock( &mutex1 );
/*****************************************************************/
tmp = count; /* copy the global count locally */
tmp = tmp+1; /* increment the local copy */
count = tmp; /* store the local value into the global count */
/*****************************************************************/
! pthread_mutex_unlock( &mutex1 );
}
}
Operating Systems and Distributed Systems 31
Mutex p_thread: Esempio 1
host:Lez20_ex Bebo$ ./p_thraddmx
OK! count is [2000000]
host:Lez20_ex Bebo$ ./p_thraddmx
OK! count is [2000000]
host:Lez20_ex Bebo$ ./p_thraddmx
OK! count is [2000000]
host:Lez20_ex Bebo$
Operating Systems and Distributed Systems 32
Compile and run p_thraddmx.c!
Mutex p_thread: Esempio 1
pthread_mutex_t mymutex;
long counter; //shared data
void *thread_fun(void *);
int main()
{
! pthread_t t1;
! void *exit_status;
! int i;
! //Initialize the mutex before trying to use it.
! pthread_mutex_init(&mymutex, NULL);!
! pthread_create(&t1, NULL, thread_fun, NULL);!
! for (i = 0 ; i < 10 ; ++i ){ // Try to use the shared data.
! ! sleep(1) ;
! ! pthread_mutex_lock(&mymutex);
! ! printf( "\rShared integer's value = %ld\n", counter);
! ! pthread_mutex_unlock(&mymutex);! !
! }
! printf("\n");
! pthread_join(t1, &exit_status);
! pthread_mutex_destroy(&mymutex);
! printf( "\rShared integer's final value = %ld\n", counter);
! return 0;
}
void *thread_fun(void *arg){
! long i;!!
! for (i=0; i<10000*10000; i++){
! ! //accessing the shared data
! ! pthread_mutex_lock(&mymutex);
! ! counter++;
! ! pthread_mutex_unlock(&mymutex);
! }
!
! return NULL;
} Operating Systems and Distributed Systems 33
dynamicinitialization
Compile and run p_thraddmx2.c!
Mutex p_thread: Esempio 2
void* compratore(void *arg){ pthread_mutex_lock(&VadoComprareLatte);! /*************** Entra regione critica**************/! thdata *data; data = (thdata *) arg; /* type cast */!
! printf("Thread %d \n", data->thread_no);! printf("\nLatte disponibile ora %d\n", latteDisponibile); if(!latteDisponibile) { // compra ++latteDisponibile; }!
! /*************** Fine regione critica**************/ pthread_mutex_unlock(&VadoComprareLatte);!
return NULL;}
Operating Systems and Distributed Systems
Mutex p_thread: Il problema del latte
Compile & run p_milkmutex.c!
void *function2()
{
! ...
! pthread_mutex_lock(&lock2); - Execution step 2
! pthread_mutex_lock(&lock1);
! ...
! pthread_mutex_lock(&lock1);
! pthread_mutex_lock(&lock2);
! ...
}
Operating Systems and Distributed Systems 35
Attenzione ai DEADLOCK• L’ordine è importante: deadlock:
main()
{
! ...
! pthread_create(&thread1, NULL, function1, NULL);
! pthread_create(&thread2, NULL, function2, NULL);
! ...
}
void *function1()
{
! ...
! pthread_mutex_lock(&lock1); - Execution step 1
! pthread_mutex_lock(&lock2); - Execution step 3 DEADLOCK!!!
! ...
! pthread_mutex_lock(&lock2);
! pthread_mutex_lock(&lock1);
! ...
}
se function1 acquisisce il primo mutex and function2 acquisisce il secondo, tutte le risorse sono bloccate.
Operating Systems and Distributed Systems
Meccanismi di sincronizzazione:come implementare i lock
Operating Systems and Distributed Systems
Soluzione hardware (1):Disabilitazione delle interruzioni
int count; //condivisa
Codice p1 Codice p2
disableInterrupts(); disableInterrupts();
count++; count++;
enableInterrupts(); enableInterrupts();
• Va bene per il kernel
• Non adeguato per user process
• Inutile per architettura multiprocessor
2. Non si devono fare ipotesi su velocità e numero CPU
Operating Systems and Distributed Systems
• Interrupts potrebbero essere disabilitati per un tempo arbitrario
• Vogliamo solo che p1 e p2 non interferiscano;
così si bloccano tutti i pi
• Possibilità: usare una variabile condivisa di “lock”
Soluzione hardware (1):Disabilitazione delle interruzioni
int count; //condivisa
Codice p1 Codice p2
disableInterrupts(); disableInterrupts();
count++; count++;
enableInterrupts(); enableInterrupts();
Operating Systems and Distributed Systems
Condizione di Busy Waitboolean lock = FALSE; //condivisa
int count; //condivisa
Codice p1 Codice p2
/* Acquisire lock */ /* Acquisire lock */
while(lock) ; while(lock) ;
lock = TRUE; lock = TRUE;
/* Sezione critica */ /* Sezione critica */
count++; count++;
/* Rilasciare lock */ /* Rilasciare lock */
lock = FALSE; lock = FALSE;
p1
p2
Blo
cked
at while
lock = TRUE
lock = FALSE
Inte
rrupt
Inte
rrupt
Inte
rrupt
Operating Systems and Distributed Systems
Problemi....
• Di male in peggio … abbiamo una nuova race condition …
•
1. Non ci devono essere 2 processi simultaneamente in RC
boolean lock = FALSE; //condivisa
int count; //condivisa
Codice p1 Codice p2
/* Acquisire lock */ /* Acquisire lock */
while(lock) ; while(lock) ;
lock = TRUE; lock = TRUE;
/* Sezione critica */ /* Sezione critica */
count++; count++;
/* Rilasciare lock */ /* Rilasciare lock */
lock = FALSE; lock = FALSE;
Operating Systems and Distributed Systems
Gestione Atomica del Lock(1)
enter(lock) { exit(lock) {
disableInterrupts(); disableInterrupts();
/* Loop finchè lock è TRUE */ lock = FALSE;
while(lock) { enableInterrupts();
/* Garantisce gli interrupts */ }
enableInterrupts();
disableInterrupts();
}
lock = TRUE;
enableInterrupts();
}
• Limita il tempo di disabilitazione degli interrupts • Ancora costosa
Operating Systems and Distributed Systems
Meccanismi di sincronizzazione:test &set
Operating Systems and Distributed Systems
• Evitare l’ interrupt disabling
• CPU ha nell’ instruction set istruzioni atomiche per il one-shot testing and setting del lock
– OS/360: Test and Set Lock (TSL)
– x86: LOCK BTS
non_critical_sectionENTER_REGIONcritical_sectionLEAVE_REGIONnon_critical_section
{ }
Meccanismi di sincronizzazione:test &set
Operating Systems and Distributed Systems
• Una variabile di lock
• Ancora busy waiting, ma codice più semplice
• non c’e’ limite al numero di processi
– Ci potrebbe essere un waiting illimitato
3. Nessun processo che esegue fuori dalla sua RC può bloccare un altro processo
Meccanismi di sincronizzazione:test &set
Operating Systems and Distributed Systems45
Eliminare il busy waiting• Obbiettivo: risparmiare CPU time
• Problema: priority inversion:
– H = high priority process, L = low priority process
• L è in RC, H diventa ready to run
• H diventa running in busy waiting
• L mai schedulato quando H running: L non può terminare e uscire dalla RC
• H aspetta all’inifinito....
• Approccio sleep/wakeup
4. Un processo non dovrebbe attendere indefinitamente per entrare nella sua RC
Operating Systems and Distributed Systems46
Eliminare il busy waiting
Operating Systems and Distributed Systems47
Eliminare il busy waiting
Operating Systems and Distributed Systems
Meccanismi di sincronizzazione