Vai alla Home Page About me Courseware Federica Living Library Federica Federica Podstudio Virtual Campus 3D Le Miniguide all'orientamento Gli eBook di Federica La Corte in Rete
 
 
Il Corso Le lezioni del Corso La Cattedra
 
Materiali di approfondimento Risorse Web Il Podcast di questa lezione

Domenico Cotroneo » 25.Esercitazione: Problema dei Produttori e dei Consumatori


Definizione

Due categorie di processi

  • Produttori, che scrivono un messaggio su di una risorsa condivisa.
  • Consumatori, che prelevano il messaggio dalla risorsa condivisa.

Vincoli:

  • Il produttore non può produrre un messaggio prima che qualche consumatore abbia letto il messaggio precedente.
  • Il consumatore non può prelevare alcun messaggio fino a che un produttore non l’abbia depositato.

Prod-Cons con un unico buffer

Scrivere un’applicazione concorrente che implementi il problema dei Produttori/Consumatori.

Il programma crei dei processi che agiscano da produttore e consumatore utilizzando un unico buffer di memoria.

Prod-Cons con un unico buffer

I vincoli imposti da un problema di tipo Produttore-Consumatore,nel caso che il buffer sia unico e che vi siano un solo produttore ed un solo consumatore, sono i seguenti:

  • il produttore non può produrre un messaggio se il consumatore non ha consumato il messaggio precedente;
  • in consumatore non può prelevare un messaggio se prima il consumatore non l’ha depositato.

Prod-Cons con un unico buffer

Per la sincronizzazione dei processi produttore e consumatore si utilizzano due semafori:

1. SPAZIO_DISP: semaforo bloccato da un produttore prima di una produzione sbloccato da un consumatore in seguito ad un consumo;
2. MSG_DISP: semaforo sbloccato da un produttore in seguito ad una produzione e bloccato da un consumatore prima del consumo.

Prod-Cons con un unico buffer

Il messaggio prodotto consiste di un valore di tipo long, rappresentativo della data di sistema (ottenuto mediante la chiamata gettimeofday(2)) . Una siffatta soluzione permette di differenziare i valori prodotti dai vari processi produttori.
La produzione ed il consumo avvengono rispettivamente all’interno delle procedure

void Produttore(msg*,int);
void Consumatore(msg *,int);

all’interno delle quali si effettuano anche le operazioni di Wait_Sem e Signal_Sem necessarie.

Prod-Cons con un unico buffer

/*--------HEADER FILE-------*/

#define MSG_DISP 0
#define SPAZIO_DISP 1
#define DIM 1

typedef long msg;

void Wait_Sem(int, int );
void Signal_Sem (int, int );

void Produttore(msg*,int);
void Consumatore(msg *,int);

Prod-Cons con un unico buffer

//Procedure Consumatore

void Consumatore(msg *ptr_sh,int mutex){

msg mess;

Wait_Sem(mutex,MSG_DISP);
mess=*ptr_sh;//memcpy(&mess,ptr_sh,sizeof(mess));
printf("Messaggio letto: \n",mess);
Signal_Sem(mutex,SPAZIO_DISP);

}

Prod-Cons con un unico buffer

//Procedure Produttore

void Produttore(msg*ptr_sh,int mutex){

struct timeval t1;
struct timezone t2;
gettimeofday(&t1,&t2);
msg val =t1.tv_usec;
Wait_Sem(mutex,SPAZIO_DISP);
printf (" <%d> Produzione in corso (%d), valore semaforo SPAZIO_DISP=%d \n",

getpid(),val,leggi_valore(mutex,SPAZIO_DISP));

*ptr_sh=val;
Signal_Sem(mutex,MSG_DISP);

}

Prod-Cons con un pool di buffer

Scrivere un’applicazione concorrente che implementi il problema dei Produttori/Consumatori utilizzando un pool di buffer gestito mediante un vettore ausiliario di STATO.

Prod-Cons con un pool di buffer

I vincoli imposti sono i seguenti:

  • un produttore non può produrre un messaggio se il buffer è pieno;
  • un consumatore non può prelevare un messaggio se il buffer è vuoto.

La gestione del pool di buffer avviene mediante due vettori:

  • buffer[DIM] – array di elementi di tipo msg (tipo del messaggio depositato dagli scrittori) contenente i valori prodotti;
  • stato[DIM]- array di elementi di tipo intero. Il valore i-simo, stato[i], può assumere i seguenti tre valori:
    • VUOTO – la cella buffer[i] non contiene alcun valore prodotto;
    • PIENO – la cella buffer[i] contiene un valore prodotto e non ancora consumato;
    • IN_USO – il valore della cella buffer[i] contiene un valore in uso da un processo attivo, consumatore o produttore.

Prod-Cons con un pool di buffer

Per la sincronizzazione dei processi si possono utilizzare due mutex, MUTEXP e MUTEXC, al fine di gestire l’accesso al buffer in mutua esclusione e due semafori, PROD e CONS, per la sincronizzazione delle operazioni di produzione e consumo.

/*************DEFINZIONE DELLE COSTANTI************/
#define VUOTO 2
#define PIENO 3
#define IN_USO 4
#define MUTEXP 0
#define MUTEXC 1
#define PROD 0
#define CONS 1
typedef long msg;

Prod-Cons con un pool di buffer

La produzione di un messaggio può articolarsi nelle seguenti funzioni:
int RichiestaP (int*stato,int sem, int mutex) – Effettua una richiesta di produzione. L’algoritmo implementato consiste dei seguenti passi:

  1. wait(PROD) – per bloccare eventuali processi produttori concorrenti;
  2. all’interno della regione critica (MUTEXP) si cerca la prima cella del buffer vuota (il cui valore corrispondente del vettore stato[] ha il valore VUOTO). Individuata la prima cella libera, destinata a contenere il valore prodotto, la si pone nello stato IN_USO

void Produzione(int indice,msg messaggio,msg *buffer) - consiste nel memorizzare il valore del messaggio nella cella buffer[indice];
void RilascioP (int indice,int*stato,int sem) – rilascia le risorse occupate durante la produzione. Essa consiste nel porre al valore PIENO lo stato relativo alla cella buffer[indice], nella quale è stato memorizzato il valore prodotto, e di sbloccare eventuali processi Consumatori mediante una signal sul semaforo CONS.

Prod-Cons con un pool di buffer

Il consumo di un messaggio può articolarsi nelle seguenti funzioni:
int RichiestaC (int*stato,int sem, int mutex) – Effettua una richiesta di consumo. L’algoritmo implementato consiste dei seguenti passi:

  1. wait(CONS) – per bloccare eventuali processi Consumatori concorrenti;
  2. all’interno della regione critica (MUTEXC) si cerca la prima cella del buffer il cui è stato è PIENO ovverosia la prima cella che contiene un valore prodotto. Una volta individuata la cella la si pone nello stato IN_USO;

msg Consumo(int indice,msg * buffer) – espleta l’operazione di consumo che consiste nel leggere il valore del messaggio nella cella buffer[indice], restituendolo al chiamante;
void RilascioC(int indice,int*stato,int sem) – rilascia le risorse occupate durante l’operazione di consumo. Essa consiste nel liberare la cella buffer[indice] contenente il messaggio consumato, ponendo il valore VUOTO in stato[indice] e di sbloccare eventuali processi Produttori mediante una signal sul semaforo PROD.

Prod-Cons con una coda

Scrivere un’applicazione concorrente che implementi il problema dei Produttori/Consumatori utilizzando una coda ( pool di buffer gestito come vettore circolare).

Prod-Cons con una coda

La coda è implementata mediante i seguenti campi:

  • buffer[DIM] – array di elementi di tipo msg (tipo del messaggio depositato dagli scrittori) contenente i valori prodotti;
  • testa – tipo intero. Si riferisce alla posizione del primo elemento libero in testa; in altre parole rappresenta il primo elemento disponibile per la memorizzazione del messaggio prodotto. L’elemento di testa è buffer[testa-1];
  • coda – tipo intero. L’elemento puntato si riferisce alla posizione dell’elemento di coda della coda. In altre parole l’elemento di coda è buffer[coda].

Prod-Cons con una coda

Per la sincronizzazione dei processi sono stati utilizzati due semafori, SPAZIO_DISP e NUM_MESS, che indicano rispettivamente la presenza di spazio disponibile in coda per la produzione di un messaggio e il numero di messaggi presenti in coda.

Prod-Cons con una coda

void Produttore() {

Wait(SPAZIO_DISP);

//Memorizzazione del valore prodotto

buffer[(testa)]=valore_prodotto;
testa=++(testa) % DIM; //gestione circolare degli indici

Signal(NUM_MESS); //nelem=nelem+1

}

Prod-Cons con una coda

void Consumatore(){

Wait(NUM_MESS);
//Consumo
mess=buffer[coda];
(coda)=++(coda) %DIM; //gestione circolare degli indici
printf("Messaggio letto: <%d> \n",mess);
Signal(SPAZIO_DISP); // nelem=nelem-1

}

Esercizio

Scrivere un’applicazione concorrente per il calcolo della somma degli elementi contenuti in una matrice.


Esercizio

Vincoli

La matrice e il buffer sums devono essere immagazzinati in memoria condivisa.

I processi P_i accedono al buffer sums per la somma della colonna i-sima in mutua esclusione senza alcun vincolo sulla posizione (e.s. Il processo P2 termina per primo e scrive nella casella sums[0]; il processo P1 finisce per secondo e scrive nella casella sums[1], ecc.)

Esercizio

Il processo P (per la somma totale) resta in attesa della terminazione di tutti i processi P_i. Implementare tale attesa con una wait_for_zero.

Il processo padre (diverso da P) attende la terminazione di tutti i processi e restituisce a video il totale calcolato da P (anch’esso memorizzato in shm).

  • Contenuti protetti da Creative Commons
  • Feed RSS
  • Condividi su FriendFeed
  • Condividi su Facebook
  • Segnala su Twitter
  • Condividi su LinkedIn
Progetto "Campus Virtuale" dell'Università degli Studi di Napoli Federico II, realizzato con il cofinanziamento dell'Unione europea. Asse V - Società dell'informazione - Obiettivo Operativo 5.1 e-Government ed e-Inclusion