Sistemi operativi, A.A. 2011/2012 (M. Cesati) Scaletta esercitazione #15, 28.06.2012 1 Semafori POSIX anonimi 1.1 Si appoggiano ad una variabile in memoria condivisa di tipo sem_t 1.1.1 Variabile globale o heap di thread 1.1.2 Memoria condivisa System V 1.1.3 Memoria condivisa POSIX 1.1.4 Regione di memoria (anonima) condivisa 1.2 Creazione del semaforo: +----------------------------------------------------------+ |int sem_init(sem_t *sem, int pshared, unsigned int value);| +----------------------------------------------------------+ 1.2.1 sem: puntatore alla variabile semaforo 1.2.2 pshared: 0 == threads, 1 == processes 1.2.3 value: valore iniziale (niente piu' race condition!) 1.3 Distruzione del semaforo: +----------------------------+ |int sem_destroy(sem_t *sem);| +----------------------------+ 1.4 Decremento di 1 (allocazione): +-------------------------+ |int sem_wait(sem_t *sem);| +-------------------------+ 1.5 Incremento di 1 (rilascio): +-------------------------+ |int sem_post(sem_t *sem);| +-------------------------+ 1.6 Lettura del valore del semaforo: +----------------------------------------+ |int sem_getvalue(sem_t *sem, int *sval);| +----------------------------------------+ 2 Esercizio: modificare i programmi consumatore_bsem e produttore_bsem in modo da utilizzare un semaforo POSIX anonimo invece di quello IPC System V [consumatore_psem.c, produttore_psem.c] 3 Esercizio proposto: l'inizializzazione dei semafori negli esercizi precedenti non e' sicura. Modificare i programmi dell'esercizio 2 per evitare ogni "race condition". 4 Sincronizzazione in programmi multithread tramite mutex 4.1 Creare un mutex 4.1.1 Staticamente: allocare una variabile di tipo pthread_mutex_t e inizializzarla con la macro PTHREAD_MUTEX_INITIALIZER: +-------------------------------------------------------+ |static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;| +-------------------------------------------------------+ 4.1.1.1 Il mutex e' inizialmente libero 4.1.1.2 *Non* puo' essere utilizzato per mutex allocati in modo automatico (sulla porzione di stack di una funzione) 4.1.2 Dinamicamente: sempre necessario quando - mutex allocato dinamicamente sullo heap - mutex allocato sullo stack (variabile automatica di una funzione) - mutex ha attributi diversi da quelli standard +-----------------------------------------------+ |int pthread_mutex_init(pthread_mutex_t *mutex, | | const pthread_mutexattr_t *strict attr); | +-----------------------------------------------+ 4.2 Distruggere un mutex dinamico - sempre necessario se inizializzato dinamicamente +---------------------------------------------------+ |int pthread_mutex_destroy(pthread_mutex_t *mutex); | +---------------------------------------------------+ 4.3 Ottenere il mutex: +-----------------------------------------------+ |int pthread_mutex_lock(pthread_mutex_t *mutex);| +-----------------------------------------------+ 4.4 Rilasciare il mutex +-------------------------------------------------+ |int pthread_mutex_unlock(pthread_mutex_t *mutex);| +-------------------------------------------------+ 5 Esercizio: modificare i programmi produttore / consumatore in modo che siano basati su POSIX thread (1 produttore, 2 consumatori) [prodcons_thread.c] 6 Sincronizzazione in programmi multithread tramite barriera 6.1 Creare una barriera +----------------------------------------------------------+ |int pthread_barrier_init(pthread_barrier_t *barrier, | | const pthread_barrierattr_t *attr, unsigned count);| +----------------------------------------------------------+ 6.1.1 barrier: indirizzo della variabile barriera 6.1.2 attr: attributi (generalmente NULL) 6.1.3 count: quanti thread devono arrivare alla barriera perche' tutti possano procedere 6.2 Distruggere una barriera +---------------------------------------------------------+ |int pthread_barrier_destroy(pthread_barrier_t *barrier); | +---------------------------------------------------------+ 6.3 Aspettare su una barriera +-----------------------------------------------------+ |int pthread_barrier_wait(pthread_barrier_t *barrier);| +-----------------------------------------------------+ 6.3.1 Uno dei thread in attesa riceve il valore PTHREAD_BARRIER_SERIAL_THREAD, gli altri ricevono il valore zero 7 Esempio di un programma multithread che fa uso di barriera per sincronizzare l'esecuzione dei thread [thread_barrier.c] 8 Sincronizzazione in programmi multithread tramite variabile condizione 8.1 Creare una variabile condizione +-------------------------------------------+ |int pthread_cond_init(pthread_cond_t *cond,| | const pthread_condattr_t *attr); | +-------------------------------------------+ 8.2 Distruggere una variabile condizione +-----------------------------------------------+ |int pthread_cond_destroy(pthread_cond_t *cond);| +-----------------------------------------------+ 8.3 Attendere la condizione +-------------------------------------------+ |int pthread_cond_wait(pthread_cond_t *cond,| | pthread_mutex_t *mutex); | +-------------------------------------------+ 8.3.1 mutex: mutex associato con la variabile condizione. La funzione deve essere invocata con il mutex acquisito dal thread invocante. La funzione rilascia atomicamente il mutex e blocca il processo invocante. Quando il thread viene sbloccato, la funzione acquisisce nuovamente il mutex per il thread invocante. 8.4 Segnalare la condizione 8.4.1 Risveglia almeno un thread in attesa della condizione +----------------------------------------------+ |int pthread_cond_signal(pthread_cond_t *cond);| +----------------------------------------------+ 8.4.2 Risveglia tutti i thread in attesa della condizione +--------------------------------------------------+ |int pthread_cond_broadcast(pthread_cond_t *cond); | +--------------------------------------------------+ 9 Esercizio: modificare l'esercizio 5 in modo da utilizzare due variabili condizione [prodcons_cond.c] ==========