Difference between revisions of "Esperimenti con semafori e monitor in C"

From Sistemi Operativi
Jump to navigation Jump to search
(Aggiunti semafori binari implementati con counting semaphores, sistemati semafori binari implementati senza counting semaphores)
m
Line 32: Line 32:
 
Ho sistemato creando prima i semafori binari implementati con i semafori normali. <br>
 
Ho sistemato creando prima i semafori binari implementati con i semafori normali. <br>
 
Dopo sono riuscito a sistemare anche i semafori binari senza semafori normali. L'unico difetto è che usano 2 value invece che 1.
 
Dopo sono riuscito a sistemare anche i semafori binari senza semafori normali. L'unico difetto è che usano 2 value invece che 1.
 
+
----
 +
[[User:Renzo|Renzo]] ([[User talk:Renzo|talk]]) 12:03, 15 November 2016 (CET)<br>
 +
Ancora ottimi spunti di discussione, ma non ancora perfetti.... Prendiamo l'implementazione dei semafori binari con i semafori ordinari e il caso Init 1, V V P P.
 +
Per effetto dell'Init sem0 vale 1 e sem 1 vale 0.
 +
Il primo V porta sem0 a 2 e il chiamante si ferma su sem1. Il secondo V porta sem0 a 3 e ferma anch'esso il chiamante su sem1. La prima P supera la P su sem0 (che viene portato a 2) fa la V su sem1 (che ora vale 1) ed esce. La seconda P porta sem0 a 1, sem1 a 2 ed esce. Abbiamo lasciato I due processi fermi sulla P(sem1).
 +
Prima o poi si sbloccheranno.... ma in questo momento nP (il numero di processi che hanno superato la P) vale 2, nV (il numero di processi che hanno superato la V) e' zero, Init era 1. quindi nV + Init - nP = 0 + 1 - 2 = -1. Ahime'! Come si risolve?
 
== uso dei semafori ==
 
== uso dei semafori ==
  

Revision as of 12:03, 15 November 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)

--FedericoB (talk) 20:51, 14 November 2016 (CET)
Ho aggiunto in questo file.

  • binsemaphore.[ch] semafori binari implementati senza semafori normali
  • dirtybinsemaphore.c semafori binari implementati con semafori normali
  • prod_consBin.c problema del produttore-consumatore risolto con solo un semaforo(binario).

Nel makefile trovate sia il target prod_consBin che dirtyprod_consBin in cui cambia solo il tipo di semaforo binario linkato.


Renzo (talk) 09:01, 15 November 2016 (CET)
attenzione: l'implementazione di FedericoB e' un ottimo punto di partenza per la discussione ma non e' corretta, Infatti non rispetta l'invariante dei semafori binari 0 <= nV + Init - nP <= 1. Poniamo infatti Init = 0, e sottoponiamo l'implementazione di Federico alla sequenza V V P V. La prima V porta il semaforo al valore 1, la seconda blocca il chiamante, la P successiva trova il semaforo a 1 quindi non e' bloccante e quindi (codice alla mano) decrementa il semaforo (s->value--) e risveglia il processo bloccato per la seconda V. Il semaforo ora vale zero. L'ultima V non e' quindi bloccante. quindi Init=0, nV=3, nP=1. (Nv + Init - Np) vale quindi 0 + 3 - 1 cioe' 2. Ahi! Dov'e' l'errore?


--FedericoB (talk) 10:54, 15 November 2016 (CET)
Ho sistemato creando prima i semafori binari implementati con i semafori normali.
Dopo sono riuscito a sistemare anche i semafori binari senza semafori normali. L'unico difetto è che usano 2 value invece che 1.


Renzo (talk) 12:03, 15 November 2016 (CET)
Ancora ottimi spunti di discussione, ma non ancora perfetti.... Prendiamo l'implementazione dei semafori binari con i semafori ordinari e il caso Init 1, V V P P. Per effetto dell'Init sem0 vale 1 e sem 1 vale 0. Il primo V porta sem0 a 2 e il chiamante si ferma su sem1. Il secondo V porta sem0 a 3 e ferma anch'esso il chiamante su sem1. La prima P supera la P su sem0 (che viene portato a 2) fa la V su sem1 (che ora vale 1) ed esce. La seconda P porta sem0 a 1, sem1 a 2 ed esce. Abbiamo lasciato I due processi fermi sulla P(sem1). Prima o poi si sbloccheranno.... ma in questo momento nP (il numero di processi che hanno superato la P) vale 2, nV (il numero di processi che hanno superato la V) e' zero, Init era 1. quindi nV + Init - nP = 0 + 1 - 2 = -1. Ahime'! Come si risolve?

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 rval from the buffer
  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
  }
}