Esperimenti con semafori e monitor in C

From Sistemi Operativi
Revision as of 10:54, 15 November 2016 by FedericoB (talk | contribs) (Aggiunti semafori binari implementati con counting semaphores, sistemati semafori binari implementati senza counting semaphores)
Jump to navigation Jump to search

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.

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
  }
}