Semafori POSIX

I semafori POSIX sono semafori contatori che permettono di gestire la sincronizzazione dei thread POSIX. Esistono altri meccanismi che, per mancanza di tempo, menzionano solamente: I Pthread Mutex sono semafori binari mentre le Pthread Condition vengono utilizzate per ‘simulare’ il costrutto dei monitor che vedremo invece nel linguaggio Java.

I semafori POSIX si utilizzano tramite le seguenti strutture dati e funzioni, definite nella libreria semaphore.h:

  • sem_t sem_name: dichiara una variabile di tipo semaforo;
  • int sem_init(sem_t *sem, int pshared, unsigned int value) inizializza il semaforo sem al valore value. la variabile pshared indica se il semaforo è condiviso tra thread (uguale a 0) o processi (diverso da 0), lo useremo quindi sempre con 0.
  • int sem_wait(sem_t *sem) esegue una P(sem);
  • int sem_post(sem_t *sem) esegue una V(sem);
  • int sem_getvalue(sem_t *sem, int *val) legge il valore del semaforo e lo copia in val;
    ATTENZIONE: in alcune implementazioni il semaforo rosso è 0, in altre è negativo (e indica il numero di processi in attesa);
  • sem_destroy(sem_t *sem) elimina il semaforo. Da NON usare se ci sono processi in attesa sul semaforo (comportamento non specificato).


NOTA PER GLI UTENTI MAC: macOS non supporta i semafori ‘unnamed’ descritti qui sopra. Per poter utilizzare i semafori POSIX si devono usare i semafori con nome (che tra l’altro sono facilmente condivisibili tra processi, un po’ come le pipe con nome). Al posto della sem_init si deve usare la sem_open e al posto della sem_destroy si usano sem_close e sem_unlink. Ovviamente i semafori con nome si possono utilizzare anche in Linux.

Esercizio 1: Sezione Critica

Riprendiamo l’ultimo esercizio della volta scorsa:

Creare 2 thread che aggiornano ripetutamente (in un ciclo for) una variabile condivisa count per un numero elevato di volte (ad esempio 1000000). Stampare il valore finale per osservare eventuali incrementi perduti…

(Seguiva una nota sulle ottimizzazioni del compilatore. Vedere le dispense della volta scorsa per maggiori dettagli)

Aggiungere un semaforo mutex per risolvere le interferenze. È sufficiente aggiungere una variabile globale sem_t sem e, all’inizio e alla fine del main, l’inizializzazione e la rimozione del semaforo: sem_init(&sem,0,1) e sem_destroy(&sem). Infine, per proteggere la sezione critica, aggiungere sem_wait(&sem) e sem_post(&sem) prima e dopo la lettura/modifica di count. Notare l’esecuzione corretta al prezzo di una più bassa performance.

NOTA: per OS X si useranno sem=sem_open("mymutex",O_CREAT,0700,1) e sem_close(sem); sem_unlink("mymutex"); facendo attenzione che in questo caso sem sarà un puntatore a sem_t (quindi anche la wait e la post andranno fatte su sem e non su &sem).

Esercizio 2: attesa tra 2 thread

Realizzare la sincronizzazione tra 2 thread vista a lezione in cui T2, prima di eseguire la porzione di codice “D” deve attendere che T1 abbia eseguito il codice “A”.

ESERCIZIO 3: Produttore-Consumatore

Implementare il Produttore-Consumatore con buffer circolare visto a lezione, utilizzando due semafori contatori più un mutex. Testarlo in presenza di più produttori e più consumatori, verificando che la presenza del mutex è fondamentale per la coerenza dei dati.

Inventarsi a tale scopo un test di qualche genere. Ad esempio si può tenere nelle celle vuote un valore speciale (-1) e testare se si sta leggendo una cella vuota o scrivendo in una piena. Ovviamente il consumatore deve scrivere nella cella il valore speciale, dopo che ha letto. Il test dovrebbe segnalare problemi di sincronizzazione nel caso non si utilizzino appropriatamente i semafori.

NOTA: l’output a video talvolta riduce le interferenze in quanto crea una alternanza molto forte nell’esecuzione dei thread. In tale caso, provare a fare la ridirezione (tramite il simbolo >) su un file.

BONUS: Le tre migliori soluzioni con test, e esaurientemente commentate, che verrano postate sul sito, avranno un bonus aggiuntivo sul voto finale.
(istruzioni per pubblicare il codice).

10 thoughts on “Semafori POSIX”

  1. Es1 )

    Es2)

    Es3)

    1. il printf printf(“Sono il thread Consumatore %lu ho consumato il valore %d in posizione %d\n”,tid,d,preleva);
      va messo prima di incrementare preleva per stampare il messaggio esatto una svista :)

Leave a Reply

Your email address will not be published. Required fields are marked *