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
 
I corsi di Ingegneria
 
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