Difference between revisions of "Esperimenti con semafori e monitor in C"
m (Corretto il link al materiale) |
m |
||
Line 12: | Line 12: | ||
* bounded_buf.c esempio: bounded_buffer con i semafori | * bounded_buf.c esempio: bounded_buffer con i semafori | ||
* bounded_buf_mon.c esempio: bounded buffer con i monitor | * bounded_buf_mon.c esempio: bounded buffer con i monitor | ||
− | * philo.c: cena dei filosofi (la soluzione è volutamente errata e può generare deadlock | + | * philo.c: cena dei filosofi (la soluzione è volutamente errata e può generare deadlock) |
== uso dei semafori == | == uso dei semafori == | ||
Line 34: | Line 34: | ||
== produttore e consumatore == | == produttore e consumatore == | ||
− | Il file ''prod_cons.c'' implementa una soluzione del | + | Il file ''prod_cons.c'' implementa una soluzione del problema del produttore/consumatore. |
Il buffer e i semafori sono definiti come variabili condivise. | Il buffer e i semafori sono definiti come variabili condivise. | ||
Line 121: | Line 121: | ||
condition ok2read; | condition ok2read; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | oltre al buffer e alle | + | oltre al buffer e alle variabili per la gestione delle strutture dati, fra le quali il contatore ''buflen''. |
Il monitor viene creato da questa funzione: | Il monitor viene creato da questa funzione: | ||
Line 163: | Line 163: | ||
if (buflen <= 0) | if (buflen <= 0) | ||
condition_wait(ok2read); | condition_wait(ok2read); | ||
− | // dequeue value from the buffer | + | // dequeue value from the buffer into rval |
buflen--; | buflen--; | ||
condition_signal(ok2write); | condition_signal(ok2write); |
Revision as of 07:58, 27 October 2016
Ho messo a vostra disposizione alcuni sorgenti che implementano in C le astrazioni di semaforo e monitor come le abbiamo viste (o vedremo presto) a lezione.
Ho messo il materiale in questo file.
La directory comprende:
- un Makefile
- tlist.[ch] un modulo di implementazione di liste circolari di thread_id (usate per gestire i processi bloccati). Le liste possono essere usate come code o stack.
- suspend.[ch] è il modulo che implementa la sospensione e riattivazione dei pthread (usando il segnale SIGUSR1).
- semaphore.[ch] implementazione dei semafori fair
- monitor.[ch] implementazione dei monitor (con semantica signal urgent per le variabili di condizione).
- prod_cons.c esempio: produttore consumatore con i semafori
- bounded_buf.c esempio: bounded_buffer con i semafori
- bounded_buf_mon.c esempio: bounded buffer con i monitor
- philo.c: cena dei filosofi (la soluzione è volutamente errata e può generare deadlock)
uso dei semafori
Se volete fare altri esperimenti con i semafori dovete includere il file semaphore.h nei vostri sorgenti C.
Un semaforo ha tipo semaphore e viene creato in questo modo
semaphore mutex;
mutex = mutex_create(1);
(ovviamente le variabili semaphore devono essere condivise e 1 è solo l'esempio di valore iniziale per un semaforo mutex)
Le operazioni P e V su mutex si scrivono così:
semaphore_P(mutex);
semaphore_V(mutex);
produttore e consumatore
Il file prod_cons.c implementa una soluzione del problema del produttore/consumatore.
Il buffer e i semafori sono definiti come variabili condivise.
semaphore full;
semaphore empty;
volatile int buffer;
Il main inizializza i semafori e crea i thread.
int main(int argc, char *argv[]) {
pthread_t prod_t;
pthread_t cons_t;
full=semaphore_create(0);
empty=semaphore_create(1);
pthread_create(&prod_t, NULL, producer, NULL);
pthread_create(&cons_t, NULL, consumer, NULL);
pthread_join(prod_t, NULL);
pthread_join(cons_t, NULL);
}
La sincronizzazione fra produttore e il consumatore viene realizzata come segue:
void *producer(void *arg) {
while (1) {
int value;
// produce value
semaphore_P(empty);
buffer = value;
semaphore_V(full);
}
}
void *consumer(void *arg) {
while (1) {
int value;
semaphore_P(full);
value = buffer;
semaphore_V(empty);
// consume value
}
}
Uso dei monitor
Un monitor viene creato con la funzione:
monitor mymon;
mymon = monitor_create()
Le variabili di condizione vengono create nel seguente modo:
condition oktoproceed;
oktoproceed = condition_create(mymon);
(NB: le variabili di condizione sono sempre riferite ad uno specifico monitor).
Le procedure entry sono normali funzioni C ma occorre mettere all'entrata e all'uscita le chiamate monitor_{enter,exit}':
int mymon_dosomething(/* params */) {
rval int;
monitor_enter(mymon);
....
monitor_exit(mymon);
return rval;
}
Le operazioni wait e signal sulle variabili di condizione si scrivono così:
condition_wait(oktoproceed);
condition_signal(oktoproceed);
Bounded buffer con i monitor
Vengono definite come variabili condivise il monitor e le variabili di condizione
monitor bb;
condition ok2write;
condition ok2read;
oltre al buffer e alle variabili per la gestione delle strutture dati, fra le quali il contatore buflen.
Il monitor viene creato da questa funzione:
void bb_create(void) {
bb = monitor_create();
ok2write = condition_create(bb);
ok2read = condition_create(bb);
}
che viene richiamata dal main prima di far partire i thread:
int main(int argc, char *argv[]) {
pthread_t prod_t;
pthread_t cons_t;
bb_create();
pthread_create(&prod_t, NULL, producer, NULL);
pthread_create(&cons_t, NULL, consumer, NULL);
pthread_join(prod_t, NULL);
pthread_join(cons_t, NULL);
}
Il monitor implementa due procedure entry put e get:
void bb_put(int value) {
monitor_enter(bb);
if (buflen >= SIZE)
condition_wait(ok2write);
// enqueue value in the buffer
buflen++;
condition_signal(ok2read);
monitor_exit(bb);
}
int bb_get(void) {
int rval;
monitor_enter(bb);
if (buflen <= 0)
condition_wait(ok2read);
// dequeue value from the buffer into rval
buflen--;
condition_signal(ok2write);
monitor_exit(bb);
return rval;
}
Il codice dei processi produttore e del consumatore usa le procedure entry del monitor bb in questo modo:
void *producer(void *arg) {
while (1) {
int value;
// produce value
bb_put(value);
}
}
void *consumer(void *arg) {
while (1) {
int value;
value = bb_get();
// consume value
}
}