|
Appunti informatica |
|
Visite: 1044 | Gradito: | [ Grande appunti ] |
Leggi anche appunti:Supermercati e informaticaSUPERMERCATI E INFORMATICA Indice: Ø Introduzione Ø Cenni Il più semplice - computerIl più semplice La CPU si occupa di eseguire tutte le operazioni e i calcoli necessari Impianto per la gestione di un magazzino intermedioUNIVERSITA' DEGLI STUDI DI NAPOLI FEDERICO II Facoltà di Ingegneria Corso |
Problemi di mutua esclusione nel modello a memoria comune
I) Scrivere una applicazione concorrente che generi due processi figlio che competano per l'uso di un buffer di memoria (un processo figlio si comporti da lettore e l'altro da scrittore).
Descrizione: Il programma seguente implementa il problema dei Lettori/Scrittori nel caso particolare in cui ci sia un solo processo lettore ed un solo processo scrittore, ai quali si estendono i seguenti vincoli:
la lettura del buffer di memoria comune (allocato dal processo padre) da parte del processo lettore non può avvenire se non dopo la scrittura da parte del processo scrittore, e chiaramente il processo lettore non ha facoltà di modificare il contenuto del buffer.
il processo scrittore opera ogni volta una "sovrascrittura" del contenuto del buffer.
A garanzia della consistenza del contenuto del buffer condiviso, processo lettore e processo scrittore devono accedervi in mutua esclusione.
Semafori.H
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
void Init_Sem(int, int); //Inizializza Semaforo
void Wait_Sem(int, int); //Wait su Semaforo
void Signal_Sem(int, int); //Signal su Semaforo
int Awaiting_Sem(int, int); //Restituisce il numero di processi in attesa su Semaforo
Semafori.C
#include 'semafori.h'
struct sembuf Sem_Buf; //sembuf è un tipo predefinito contenente perlomeno i seguenti campi
//short sem_num; Numero del semaforo
//short sem_op; Valore da sommare algebricamente al semaforo
//short sem_flg; Flag dell'operazione
union semun Sem_Union; //semun è una unione predefinita contenente i seguenti elementi
//int val;
//struct semid_ds* buf;
//unshort_t* array;
//----- Nelle funzioni seguenti ID identifica il gruppo di semafori, N il singolo semaforo nel gruppo -----
void Init_Sem(int ID, int N, int Val)
void Wait_Sem(int ID, int N)
void Signal_Sem(int ID, int N)
int Awaiting_Sem(int ID, int N)
Programma I.C
#include <stdio.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include 'semafori.h'
#define DIM 1 //Dimensione dell'area di memoria condivisa
#define NUM_OPS 4 //Numero di letture/scritture
#define MUTEX 0 //Definizione di MACRO per l'accesso al semaforo
#define INITIALIZE(S,V) Init_Sem(ID_Sem,S,V)
#define WAIT(S) Wait_Sem(ID_Sem,S)
#define SIGNAL(S) Signal_Sem(ID_Sem,S)
void main()
Ptr_Buf=shmat(ID_Buf, 0, 0); //Il segmento allocato viene annesso al segmento dati
//del processo al primo indirizzo disponibile così come
//specificato dal sistema
if (Ptr_Buf==(char*)-1)
//----- CREAZIONE SEMAFORO -----
key_t Key_Sem=IPC_PRIVATE; //Chiave del semaforo/i
int ID_Sem; //Identificatore del semaforo/i
ID_Sem=semget(Key_Sem, 1, IPC_CREAT|0664); //Viene allocato un gruppo di semafori di cardinalità 1,
//viene associato al gruppo un ID e viene creata una
//struttura dati ausiliaria che consenta di gestirlo
//RW per User, RW per Gruppo, R only per Others
if (ID_Sem==-1)
INITIALIZE(MUTEX,1); //Setta MUTEX a 1
//----- GENERAZIONE FIGLIO SCRITTORE -----
pid=fork(); //Generazione del figlio Scrittore
if (pid==-1) else if (!pid)
exit(0); //Il figlio Scrittore termina correttamente
}
//----- GENERAZIONE FIGLIO LETTORE -----
pid=fork();
if (pid==-1) else if (!pid)
exit(0); //Il figlio Lettore termina correttamente
}
//----- SINCRONIZZAZIONE DEL PADRE CON I FIGLI -----
for(i=0; i<2; i++)
//----- RILASCIO MEMORIA CONDIVISA E SEMAFORO -----
shmctl(ID_Buf, IPC_RMID, 0);
semctl(ID_Sem, 0, IPC_RMID);
Problemi di cooperazione nel modello a memoria comune
II) Scrivere una applicazione concorrente che implementi il problema Produttore/Consumatore. In particolare, il programma crei due processi che agiscono, rispettivamente da produttore e consumatore, comunicando attraverso un unico buffer di memoria.
Descrizione: Il programma seguente implementa il problema dei Produttori/Consumatori nel caso particolare in cui ci sia un solo processo produttore ed un solo processo consumatore, ai quali si estendono i seguenti vincoli:
il consumo del buffer di memoria comune (allocato dal processo padre) da parte del processo consumatore non può avvenire se non dopo che il processo produttore vi abbia depositato un contenuto, e all'atto del consumo tale contenuto viene cancellato.
il processo produttore non può depositare nel buffer di memoria comune un nuovo contenuto se il precedente non è stato ancora consumato.
A garanzia della consistenza del contenuto del buffer condiviso, processo consumatore e processo produttore devono accedervi in mutua esclusione.
Semafori.H (L'implementazione è quella indicata per il programma I)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
void Init_Sem(int, int); //Inizializza Semaforo
void Wait_Sem(int, int); //Wait su Semaforo
void Signal_Sem(int, int); //Signal su Semaforo
int Awaiting_Sem(int, int); //Restituisce il numero di processi in attesa su Semaforo
Programma II.C
#include <stdio.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include 'semafori.h'
#define DIM 1 //Dimensione dell'area di memoria condivisa
#define NUM_OPS 4 //Numero di produzioni/consumi
#define SPAZIO_DISPONIBILE 0 //Definizione di MACRO per l'accesso ai semafori
#define MESSAGGIO_DISPONIBILE 1
#define INITIALIZE(S,V) Init_Sem(ID_Sem,S,V)
#define WAIT(S) Wait_Sem(ID_Sem,S)
#define SIGNAL(S) Signal_Sem(ID_Sem,S)
void main()
Ptr_Buf=shmat(ID_Buf, 0, 0); //Il segmento allocato viene annesso al segmento dati
//del processo al primo indirizzo disponibile così come
//specificato dal sistema
if (Ptr_Buf==(char*)-1)
//----- CREAZIONE SEMAFORI -----
key_t Key_Sem=IPC_PRIVATE; //Chiave del semaforo/i
int ID_Sem; //Identificatore del semaforo/i
ID_Sem=semget(Key_Sem, 2, IPC_CREAT|0664); //Viene allocato un gruppo di semafori di cardinalità 2,
//viene associato al gruppo un ID e viene creata una
//struttura dati ausiliaria che consenta di gestirlo
//RW per User, RW per Gruppo, R only per Others
if (ID_Sem==-1)
INITIALIZE(SPAZIO_DISPONIBILE,1); //Setta SPAZIO_DISPONIBILE a 1
INITIALIZE(MESSAGGIO_DISPONIBILE,0); //Setta MESSAGGIO_DISPONIBILE a 0
//----- GENERAZIONE FIGLIO PRODUTTORE -----
pid=fork(); //Generazione del figlio Produttore
if (pid==-1) else if (!pid)
exit(0); //Il figlio Produttore termina correttamente
}
//----- GENERAZIONE FIGLIO CONSUMATORE -----
pid=fork();
if (pid==-1) else if (!pid)
exit(0); //Il figlio Consumatore termina correttamente
}
//----- SINCRONIZZAZIONE DEL PADRE CON I FIGLI -----
for(i=0; i<2; i++)
//----- RILASCIO MEMORIA CONDIVISA E SEMAFORO -----
shmctl(ID_Buf, IPC_RMID, 0);
semctl(ID_Sem, 0, IPC_RMID);
Problemi di cooperazione nel modello a memoria comune
III) Scrivere un'applicazione concorrente che implementi il problema Produttore/Consumatore nella variante che prevede più produttori e consumatori che comunicano attraverso un pool di buffer di memoria gestito come vettore circolare.
Descrizione: Il programma seguente implementa il problema dei Produttori/Consumatori (più di uno) nel caso in cui essi si contendano un pool di buffer organizzato come coda circolare (inserimento in coda, prelievo in testa), posto che ad essi si applichino i seguenti vincoli:
il consumo di un singolo buffer di memoria comune (l'insieme del pool è allocato dal processo padre) da parte di un processo consumatore non può avvenire se non dopo che almeno un processo produttore vi abbia depositato un contenuto, e all'atto del consumo tale contenuto viene cancellato.
un processo produttore non può depositare in un buffer di memoria comune un nuovo contenuto se non è disponibile alcun buffer libero.
A garanzia della consistenza del contenuto del buffer condiviso, processi consumatori e processi produttori, globalmente, devono accedere al singolo buffer in mutua esclusione, tuttavia l'accesso al pool di buffer nella sua totalità avviene comunque in concorrenza
Semafori.H (L'implementazione è quella indicata per il programma I)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
void Init_Sem(int, int); //Inizializza Semaforo
void Wait_Sem(int, int); //Wait su Semaforo
void Signal_Sem(int, int); //Signal su Semaforo
int Awaiting_Sem(int, int); //Restituisce il numero di processi in attesa su Semaforo
Prod_Cons_Queue.H
#include <stdio.h>
#include "semafori.h"
#define DIM_QUEUE 4 //Cardinalità della Coda circolare
#define VUOTO 0 //Stati del singolo buffer della Coda circolare
#define IN_USO 1
#define PIENO 2
typedef struct Queue;
void Init_Queue(Queue*); //Inizializza la Coda circolare
void Init_Semafori_Queue(int); //Inizializza i semafori
int Richiesta_Produzione_Queue(Queue*, int); //Richiesta di un buffer libero per la produzione
void Produzione_Queue(int, char, Queue*); //Produzione di un carattere nella Coda circolare
void Rilascio_Produzione_Queue(int, Queue*, int); //Rilascio di un buffer pieno
int Richiesta_Consumo_Queue(Queue*, int); //Richiesta di un buffer pieno per il consumo
char Consumo_Queue(int, Queue*); //Consumo di un carattere dalla Coda circolare
void Rilascio_Consumo_Queue(int, Queue*, int); //Rilascio di un buffer vuoto
Prod_Cons_Queue.C
#include "prod_cons_queue.h"
#define SPAZIO_DISPONIBILE 0 //Definizione di MACRO per l'accesso ai semafori
#define MESSAGGIO_DISPONIBILE 1
#define MUTEX_PROD 2
#define MUTEX_CONS 3
#define INITIALIZE(S,V) Init_Sem(ID_Sem,S,V)
#define WAIT(S) Wait_Sem(ID_Sem,S)
#define SIGNAL(S) Signal_Sem(ID_Sem,S)
void Init_Queue(Queue* Q)
void Init_Semafori_Queue(int ID_Sem)
int Richiesta_Produzione_Queue(Queue* Q, int ID_Sem) while (Q->stato[Q->coda]!=VUOTO);
Q->stato[i]=IN_USO;
SIGNAL(MUTEX_PROD);
printf('Buffer %d in uson', i);
return i;
void Produzione_Queue(int i, char value, Queue* Q)
void Rilascio_Produzione_Queue(int i, Queue* Q, int ID_Sem)
int Richiesta_Consumo_Queue(Queue* Q, int ID_Sem)
if (i==Q->testa) Q->testa=(Q->testa+1) % DIM_QUEUE;
Q->stato[i]=IN_USO;
SIGNAL(MUTEX_CONS);
printf('Buffer %d in uson', i);
return i;
char Consumo_Queue(int i, Queue* Q)
void Rilascio_Consumo_Queue(int i, Queue* Q, int ID_Sem)
Programma III.C
#include <stdio.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include 'prod_cons_queue.h'
#define DIM sizeof(Queue) //Dimensione dell'area di memoria condivisa
#define NUM_PROD 3 //Numero di processi produttori
#define NUM_PRODUZ 5 //Numero di produzioni per ogni processo produttore
#define NUM_CONS 5 //Numero di processi consumatori
#define NUM_CONSUM 3 //Numero di consumi per ogni processo consumatore
void main()
Ptr_Buf=(Queue*)shmat(ID_Buf, 0, 0); //Il segmento allocato viene annesso al segmento dati
//del processo al primo indirizzo disponibile così come
//specificato dal sistema
if (Ptr_Buf==(Queue*)-1)
Init_Queue(Ptr_Buf); //Inizializzazione della Queue
//----- CREAZIONE SEMAFORI -----
key_t Key_Sem=IPC_PRIVATE; //Chiave del semaforo/i
int ID_Sem; //Identificatore del semaforo/i
ID_Sem=semget(Key_Sem, 4, IPC_CREAT|0664); //Viene allocato un gruppo di semafori di cardinalità 4,
//viene associato al gruppo un ID e viene creata una
//struttura dati ausiliaria che consenta di gestirlo
//RW per User, RW per Gruppo, R only per Others
if (ID_Sem==-1)
Init_Semafori_Queue(ID_Sem); //Inizializzazione dei semafori
//----- GENERAZIONE FIGLI PRODUTTORI -----
for(i=0; i<NUM_PROD; i++) {
pid=fork(); //Generazione del figlio Produttore i-esimo
if (pid==-1) else if (!pid)
exit(0); //Il figlio Produttore termina correttamente
}
} //End For NUM_PROD
//----- GENERAZIONE FIGLI CONSUMATORI -----
for(i=0; i<NUM_CONS; i++) {
pid=fork(); //Generazione del figlio Consumatore i-esimo
if (pid==-1) else if (!pid)
exit(0); //Il figlio Consumatore termina correttamente
}
} //End For NUM_CONS
//----- SINCRONIZZAZIONE DEL PADRE CON I FIGLI -----
for(i=0; i<NUM_PROD+NUM_CONS; i++)
//----- RILASCIO MEMORIA CONDIVISA E SEMAFORO -----
shmctl(ID_Buf, IPC_RMID, 0);
semctl(ID_Sem, 0, IPC_RMID);
Problemi di cooperazione nel modello a memoria comune
IV) Scrivere una applicazione concorrente che implementi il problema Produttore/Consumatore nella variante che prevede più produttori e consumatori e l'impiego di un pool di buffer di memoria.
Descrizione: Il programma seguente implementa il problema dei Produttori/Consumatori (più di uno) nel caso in cui essi si contendano un pool di buffer organizzato come semplice vettore, posto che ad essi si applichino i seguenti vincoli:
il consumo di un singolo buffer di memoria comune (l'insieme del pool è allocato dal processo padre) da parte di un processo consumatore non può avvenire se non dopo che almeno un processo produttore vi abbia depositato un contenuto, e all'atto del consumo tale contenuto viene cancellato.
un processo produttore non può depositare in un buffer di memoria comune un nuovo contenuto se non è disponibile alcun buffer libero.
A garanzia della consistenza del contenuto del buffer condiviso, processi consumatori e processi produttori, globalmente, devono accedere al singolo buffer in mutua esclusione, tuttavia l'accesso al pool di buffer nella sua totalità avviene comunque in concorrenza.
Semafori.H (L'implementazione è quella indicata per il programma I)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
void Init_Sem(int, int); //Inizializza Semaforo
void Wait_Sem(int, int); //Wait su Semaforo
void Signal_Sem(int, int); //Signal su Semaforo
int Awaiting_Sem(int, int); //Restituisce il numero di processi in attesa su Semaforo
Prod_Cons_Vector.H
#include <stdio.h>
#include "semafori.h"
#define DIM_VECTOR 4 //Cardinalità del vettore
#define VUOTO 0 //Stati del singolo buffer del vettore
#define IN_USO 1
#define PIENO 2
typedef struct Vector;
void Init_Vector(Vector*); //Inizializza il vettore
void Init_Semafori_Vector(int); //Inizializza i semafori
int Richiesta_Produzione_Vector(Vector*, int); //Richiesta di un buffer libero per la produzione
void Produzione_Vector(int, char, Vector*); //Produzione di un carattere nel vettore
void Rilascio_Produzione_Vector(int, Vector*, int); //Rilascio di un buffer pieno
int Richiesta_Consumo_Vector(Vector*, int); //Richiesta di un buffer pieno per il consumo
char Consumo_Vector(int, Vector*); //Consumo di un carattere dal vettore
void Rilascio_Consumo_Vector(int, Vector*, int); //Rilascio di un buffer vuoto
Prod_Cons_Vector.C
#include "prod_cons_vector.h"
#define SPAZIO_DISPONIBILE 0 //Definizione di MACRO per l'accesso ai semafori
#define MESSAGGIO_DISPONIBILE 1
#define MUTEX_PROD 2
#define MUTEX_CONS 3
#define INITIALIZE(S,V) Init_Sem(ID_Sem,S,V)
#define WAIT(S) Wait_Sem(ID_Sem,S)
#define SIGNAL(S) Signal_Sem(ID_Sem,S)
void Init_Vector(Vector* V)
void Init_Semafori_Vector(int ID_Sem)
int Richiesta_Produzione_Vector(Vector* V, int ID_Sem)
V->stato[i]=IN_USO;
SIGNAL(MUTEX_PROD);
printf('Buffer %d in uson', i);
return i;
void Produzione_Vector(int i, char value, Vector* V)
void Rilascio_Produzione_Vector(int i, Vector* V, int ID_Sem)
int Richiesta_Consumo_Vector(Vector* V, int ID_Sem)
V->stato[i]=IN_USO;
SIGNAL(MUTEX_CONS);
printf('Buffer %d in uson', i);
return i;
char Consumo_Vector(int i, Vector* V)
void Rilascio_Consumo_Vector(int i, Vector* V, int ID_Sem)
Programma IV.C
#include <stdio.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include 'prod_cons_vector.h'
#define DIM sizeof(Vector) //Dimensione dell'area di memoria condivisa
#define NUM_PROD 3 //Numero di processi produttori
#define NUM_PRODUZ 5 //Numero di produzioni per ogni processo produttore
#define NUM_CONS 5 //Numero di processi consumatori
#define NUM_CONSUM 3 //Numero di consumi per ogni processo consumatore
void main()
Ptr_Buf=(Vector*)shmat(ID_Buf, 0, 0); //Il segmento allocato viene annesso al segmento dati
//del processo al primo indirizzo disponibile così come
//specificato dal sistema
if (Ptr_Buf==(Vector*)-1)
Init_Vector(Ptr_Buf); //Inizializzazione del Vettore
//----- CREAZIONE SEMAFORI -----
key_t Key_Sem=IPC_PRIVATE; //Chiave del semaforo/i
int ID_Sem; //Identificatore del semaforo/i
ID_Sem=semget(Key_Sem, 4, IPC_CREAT|0664); //Viene allocato un gruppo di semafori di cardinalità 4,
//viene associato al gruppo un ID e viene creata una
//struttura dati ausiliaria che consenta di gestirlo
//RW per User, RW per Gruppo, R only per Others
if (ID_Sem==-1)
Init_Semafori_Vector(ID_Sem); //Inizializzazione dei semafori
//----- GENERAZIONE FIGLI PRODUTTORI -----
for(i=0; i<NUM_PROD; i++) {
pid=fork(); //Generazione del figlio Produttore i-esimo
if (pid==-1) else if (!pid)
exit(0); //Il figlio Produttore termina correttamente
}
} //End For NUM_PROD
//----- GENERAZIONE FIGLI CONSUMATORI -----
for(i=0; i<NUM_CONS; i++) {
pid=fork(); //Generazione del figlio Consumatore i-esimo
if (pid==-1) else if (!pid)
exit(0); //Il figlio Consumatore termina correttamente
}
} //End For NUM_CONS
//----- SINCRONIZZAZIONE DEL PADRE CON I FIGLI -----
for(i=0; i<NUM_PROD+NUM_CONS; i++)
//----- RILASCIO MEMORIA CONDIVISA E SEMAFORO -----
shmctl(ID_Buf, IPC_RMID, 0);
semctl(ID_Sem, 0, IPC_RMID);
Problemi di mutua esclusione nel modello a memoria comune
V) Scrivere una applicazione concorrente che implementi il problema dei Lettori/Scrittori nella variante che prevede che essi accedano ad un pool di buffer di memoria condivisa, con attesa indefinita dei processi scrittori.
Descrizione: Il programma seguente implementa il problema dei Lettori/Scrittori (più di uno) nel caso in cui si assuma che i processi lettori abbiano la massima priorità e che i processi scrittori possano invece subire starvation. I vincoli possono essere così riassunti:
Il processo scrittore attivo opera ogni volta una "sovrascrittura" del contenuto del buffer.
Finchè è attivo un processo scrittore i processi lettori e gli altri processi scrittori devono attendere (a garanzia della consistenza del contenuto del buffer).
Finchè sono attivi uno o più processi lettori i processi scrittori devono attendere.
Finchè è attivo almeno un processo lettore tutti i processi lettori che sopraggiungono hanno diritto di prelazione rispetto ad eventuali processi scrittori già in attesa.
Semafori.H (L'implementazione è quella indicata per il programma I)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
void Init_Sem(int, int); //Inizializza Semaforo
void Wait_Sem(int, int); //Wait su Semaforo
void Signal_Sem(int, int); //Signal su Semaforo
int Awaiting_Sem(int, int); //Restituisce il numero di processi in attesa su Semaforo
Lett_Scrit_StarvScr.H
#include <stdio.h>
#include"semafori.h"
typedef struct Buffer;
void Init_Buffer(Buffer*); //Inizializza il vettore
void Init_Semafori(int); //Inizializza i semafori
void Inizio_Lettura(Buffer*, int); //Acquisizione del buffer in lettura
char Lettura(Buffer*); //Lettura del buffer
void Fine_Lettura(Buffer*, int); //Rilascio del buffer dopo una lettura
void Inizio_Scrittura(int); //Acquisizione del buffer per la scrittura
void Scrittura(char, Buffer*); //Scrittura del buffer
void Fine_Scrittura(int); //Rilascio del buffer dopo una scrittura
Lett_Scrit_StarvScr.C
#include "Lett_Scrit_StarvScr.h"
#define MUTEX 0 //Definizione di MACRO per l'accesso ai semafori
#define SYNCH 1
#define INITIALIZE(S,V) Init_Sem(ID_Sem,S,V)
#define WAIT(S) Wait_Sem(ID_Sem,S)
#define SIGNAL(S) Signal_Sem(ID_Sem,S)
void Init_Buffer(Buffer* B)
void Init_Semafori(int ID_Sem)
void Inizio_Lettura(Buffer* B, int ID_Sem)
SIGNAL(MUTEX);
char Lettura(Buffer* B)
void Fine_Lettura(Buffer* B, int ID_Sem)
SIGNAL(MUTEX);
void Inizio_Scrittura(int ID_Sem)
void Scrittura(char value, Buffer* B)
void Fine_Scrittura(int ID_Sem)
Programma V.C
#include <stdio.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include 'lett_scrit_starvscr.h'
#define DIM sizeof(Buffer) //Dimensione dell'area di memoria condivisa
#define NUM_LETT 5 //Numero di processi lettori
#define NUM_SCRIT 2 //Numero di processi scrittori
void main()
Ptr_Buf=(Buffer*)shmat(ID_Buf, 0, 0); //Il segmento allocato viene annesso al segmento dati
//del processo al primo indirizzo disponibile così come
//specificato dal sistema
if (Ptr_Buf==(Buffer*)-1)
Init_Buffer(Ptr_Buf); //Inizializzazione del Buffer
//----- CREAZIONE SEMAFORI -----
key_t Key_Sem=IPC_PRIVATE; //Chiave del semaforo/i
int ID_Sem; //Identificatore del semaforo/i
ID_Sem=semget(Key_Sem, 2, IPC_CREAT|0664); //Viene allocato un gruppo di semafori di cardinalità 2,
//viene associato al gruppo un ID e viene creata una
//struttura dati ausiliaria che consenta di gestirlo
//RW per User, RW per Gruppo, R only per Others
if (ID_Sem==-1)
Init_Semafori(ID_Sem); //Inizializzazione dei semafori
//----- GENERAZIONE FIGLI SCRITTORI -----
for(i=0; i<NUM_SCRIT; i++) {
pid=fork(); //Generazione del figlio Scrittore i-esimo
if (pid==-1) else if (!pid)
Inizio_Scrittura(ID_Sem);
Scrittura(value, Ptr_Buf);
Fine_Scrittura(ID_Sem);
exit(0); //Il figlio Scrittore termina correttamente
}
} //End For NUM_SCRIT
//----- GENERAZIONE FIGLI LETTORI -----
for(i=0; i<NUM_LETT; i++) {
pid=fork(); //Generazione del figlio Lettore i-esimo
if (pid==-1) else if (!pid)
} //End For NUM_LETT
//----- SINCRONIZZAZIONE DEL PADRE CON I FIGLI -----
for(i=0; i<NUM_SCRIT+NUM_LETT; i++)
//----- RILASCIO MEMORIA CONDIVISA E SEMAFORO -----
shmctl(ID_Buf, IPC_RMID, 0);
semctl(ID_Sem, 0, IPC_RMID);
Problemi di mutua esclusione nel modello a memoria comune
VI) Scrivere una applicazione concorrente che implementi il problema dei Lettori/Scrittori nella variante che prevede che essi accedano ad un pool di buffer di memoria condivisa, con attesa indefinita sia dei processi scrittori che dei processi lettori.
Descrizione: Il programma seguente implementa il problema dei Lettori/Scrittori (più di uno) nel caso in cui si assuma che sia i processi lettori che i processi scrittori abbiano (quando attivi) la massima priorità sui processi dell'altra categoria, sicché ambedue le classi di processi possono subire starvation. I vincoli possono essere così riassunti:
Il processo scrittore attivo opera ogni volta una "sovrascrittura" del contenuto del buffer.
Finchè è attivo un processo scrittore i processi lettori e gli altri processi scrittori devono attendere (a garanzia della consistenza del contenuto del buffer).
Finchè sono attivi uno o più processi lettori i processi scrittori devono attendere, e finquando è attivo almeno un processo lettore tutti i processi lettori che sopraggiungono hanno diritto di prelazione rispetto ad eventuali processi scrittori già in attesa.
Finchè è attivo un processo scrittore i processi lettori devono attendere, e finquando è attivo almeno un processo scrittore tutti i processi scrittori che sopraggiungono hanno diritto di prelazione rispetto ad eventuali processi lettori già in attesa.
Semafori.H (L'implementazione è quella indicata per il programma I)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
void Init_Sem(int, int); //Inizializza Semaforo
void Wait_Sem(int, int); //Wait su Semaforo
void Signal_Sem(int, int); //Signal su Semaforo
int Awaiting_Sem(int, int); //Restituisce il numero di processi in attesa su Semaforo
Lett_Scrit_Starv.H
#include <stdio.h>
#include"semafori.h"
typedef struct Buffer;
void Init_Buffer(Buffer*); //Inizializza il vettore
void Init_Semafori(int); //Inizializza i semafori
void Inizio_Lettura(Buffer*, int); //Acquisizione del buffer in lettura
char Lettura(Buffer*); //Lettura del buffer
void Fine_Lettura(Buffer*, int); //Rilascio del buffer dopo una lettura
void Inizio_Scrittura(Buffer*, int); //Acquisizione del buffer per la scrittura
void Scrittura(char, Buffer*); //Scrittura del buffer
void Fine_Scrittura(Buffer*, int); //Rilascio del buffer dopo una scrittura
Lett_Scrit_Starv.C
#include "Lett_Scrit_Starv.h"
#define MUTEX_LETT 0 //Definizione di MACRO per l'accesso ai semafori
#define MUTEX_SCRIT 1
#define MUTEX 2
#define SYNCH 3
#define INITIALIZE(S,V) Init_Sem(ID_Sem,S,V)
#define WAIT(S) Wait_Sem(ID_Sem,S)
#define SIGNAL(S) Signal_Sem(ID_Sem,S)
void Init_Buffer(Buffer* B)
void Init_Semafori(int ID_Sem)
void Inizio_Lettura(Buffer* B, int ID_Sem)
SIGNAL(MUTEX_LETT);
char Lettura(Buffer* B)
void Fine_Lettura(Buffer* B, int ID_Sem)
SIGNAL(MUTEX_LETT);
void Inizio_Scrittura(Buffer* B, int ID_Sem)
SIGNAL(MUTEX_SCRIT);
WAIT(MUTEX);
void Scrittura(char value, Buffer* B)
void Fine_Scrittura(Buffer* B, int ID_Sem)
SIGNAL(MUTEX_SCRIT);
Programma VI.C
#include <stdio.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include 'lett_scrit_starv.h'
#define DIM sizeof(Buffer) //Dimensione dell'area di memoria condivisa
#define NUM_LETT 5 //Numero di processi lettori
#define NUM_SCRIT 2 //Numero di processi scrittori
void main()
Ptr_Buf=(Buffer*)shmat(ID_Buf, 0, 0); //Il segmento allocato viene annesso al segmento dati
//del processo al primo indirizzo disponibile così come
//specificato dal sistema
if (Ptr_Buf==(Buffer*)-1)
Init_Buffer(Ptr_Buf); //Inizializzazione del Buffer
//----- CREAZIONE SEMAFORI -----
key_t Key_Sem=IPC_PRIVATE; //Chiave del semaforo/i
int ID_Sem; //Identificatore del semaforo/i
ID_Sem=semget(Key_Sem, 4, IPC_CREAT|0664); //Viene allocato un gruppo di semafori di cardinalità 4,
//viene associato al gruppo un ID e viene creata una
//struttura dati ausiliaria che consenta di gestirlo
//RW per User, RW per Gruppo, R only per Others
if (ID_Sem==-1)
Init_Semafori(ID_Sem); //Inizializzazione dei semafori
//----- GENERAZIONE FIGLI SCRITTORI -----
for(i=0; i<NUM_SCRIT; i++) {
pid=fork(); //Generazione del figlio Scrittore i-esimo
if (pid==-1) else if (!pid)
Inizio_Scrittura(Ptr_Buf, ID_Sem);
Scrittura(value, Ptr_Buf);
Fine_Scrittura(Ptr_Buf, ID_Sem);
exit(0); //Il figlio Scrittore termina correttamente
}
} //End For NUM_SCRIT
//----- GENERAZIONE FIGLI LETTORI -----
for(i=0; i<NUM_LETT; i++) {
pid=fork(); //Generazione del figlio Lettore i-esimo
if (pid==-1) else if (!pid)
} //End For NUM_LETT
//----- SINCRONIZZAZIONE DEL PADRE CON I FIGLI -----
for(i=0; i<NUM_SCRIT+NUM_LETT; i++)
//----- RILASCIO MEMORIA CONDIVISA E SEMAFORO -----
shmctl(ID_Buf, IPC_RMID, 0);
semctl(ID_Sem, 0, IPC_RMID);
Problemi di cooperazione nel modello a scambio di messaggi
VII) Scrivere una applicazione concorrente che implementi il problema Produttore/Consumatore utilizzando le primitive send e receive per la comunicazione mediante code di messaggi (Costruzione di un protocollo sincrono mediante le primitive di scambio di messaggi asincrone).
Descrizione: Il programma seguente risolve il problema dei Produttori/Consumatori implementando un protocollo sincrono di comunicazione che fa uso delle primitive (asincrone) messe a disposizione dal sistema. Occorre fare in modo che produttore e consumatore si sincronizzino opportunamente in modo da non perdere il messaggio da scambiare. A tal fine produttore e consumatore, prima dell'invio del messaggio effettivo, si scambiano dei messaggi di sincronizzazione secondo lo schema seguente
Produttore Consumatore
Invia messaggio di pronto ad inviare In attesa di messaggio di pronto ad inviare
In attesa di messaggio di pronto a ricevere Invio messaggio di pronto a ricevere
Invio del messaggio effettivo In attesa del messaggio effettivo
Ovvero, in termini di Send Asincrona a Receive Bloccante
Produttore Consumatore
Send asincrona(coda, "pronto ad inviare") Receive bloccante(coda, pronto ad inviare)
Receive bloccante(coda, pronto a ricevere) Send asincrona(coda, "pronto a ricevere")
Send asincrona(coda, "messaggio") Receive bloccante(coda, messaggio)
Nell'implementazione proposta viene utilizzata una sola coda differenziando nel contempo il tipo di messaggi.
Programma VII.C
#include <sys/types.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/wait.h>
#define DIM_MSG 256 //Cardinalità del Messaggio
#define Ready_To_Send 0 //Tipi di Messaggio
#define Ready_To_Receive 1
#define Data_Message 2
typedef struct Messaggio;
void main()
//----- GENERAZIONE FIGLIO PRODUTTORE -----
pid=fork(); //Generazione del figlio Produttore
if (pid==-1) else if (!pid)
exit(0); //Il figlio Produttore termina correttamente
}
//----- GENERAZIONE FIGLIO CONSUMATORE -----
pid=fork(); //Generazione del figlio Consumatore
if (pid==-1) else if (!pid)
exit(0); //Il figlio Consumatore termina correttamente
}
//----- SINCRONIZZAZIONE DEL PADRE CON I FIGLI -----
for(i=0; i<2; i++)
//----- RILASCIO CODA DI MESSAGGI -----
msgctl(ID_Msg, IPC_RMID, 0);
Problemi di cooperazione nel modello a scambio di messaggi
VIII) Scrivere una applicazione concorrente che implementi uno schedulatore di processi che gestisce tre livelli di priorità usando le code di messaggi.
Descrizione: Il programma seguente mostra come è possibile implementare uno schedulatore di processi usando le code di messaggi. Un processo utente che voglia essere schedulato deve inviare allo schedulatore un messaggio di tipo Ready_To_Be_Scheduled, indicando in esso la propria priorità e il proprio PID, dopodiché si pone in attesa, da parte dello schedulatore, di un messaggio di tipo Ready_To_Schedule_You che gli comunichi che è stato scelto: a questo punto il processo eseguirà il suo carico di istruzioni e prima di terminare invierà allo scheduler un messaggio di tipo End_of_Execute, segnalando in tal modo di aver concluso il proprio lavoro. Più in generale, lo scheduler esegue ciclicamente il polling (receive non bloccante) di una coda dei messaggi (si è scelto per semplicità di usare una sola coda) alla ricerca di una richiesta di schedulazione, analizzando le richieste disponibili sulla base della priorità dichiarata dai singoli processi e scegliendo il prossimo processo da schedulare: letto il PID del messaggio prescelto, gli viene inviato un messaggio di attivazione. Fatto ciò, non gli resta che porsi in attesa della segnalazione di terminazione del processo schedulato, dopo la quale potrà riprendere a svolgere da capo la propria attività. Si noti che, nell'implementazione proposta, il processo padre è lo scheduler dei processi figli.
Programma VIII.C
#include <sys/types.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/wait.h>
#define DIM_MSG (sizeof(unsigned int)) //Cardinalità del Messaggio
#define Urgent 0 //Tipi del Messaggio
#define Normal 1
#define Idle 2
#define End_of_Execute 3
#define Ready_To_Schedule_You (long)100000
typedef struct Messaggio;
#define N_PROC 5 //Numero di processi da schedulare
void main()
//----- GENERAZIONE DEI FIGLI DA SCHEDULARE -----
for(i=0; i<N_PROC; i++)
pid=fork(); //Generazione del figlio i-esimo
if (pid==-1) else if (!pid)
} //End For N_PROC
//----- SCHEDULAZIONE DEI FIGLI -----
for (i=0; i<N_PROC; i++) //Almeno un processo ha richiesto
//la schedulazione
printf('Mando in esecuzione il processo con PID %d e priorità %dn', msg.PID, priority);
msg.tipo = msg.PID + Ready_To_Schedule_You;
msgsnd(ID_Msg, (const void*)&msg, DIM_MSG, 0); //Avvia l'esecuzione del processo
//appena schedulato
msgrcv(ID_Msg, (const void*)&msg, DIM_MSG, End_of_Execute, 0); //Rimane in attesa che tale
//processo schedulato gli comunichi
//la sua terminazione
}
//----- SINCRONIZZAZIONE DEL PADRE CON I FIGLI (ormai Zombie) -----
for(i=0; i<N_PROC; i++) pid=wait(&status); //I figli sono già terminati tutti e
//sono attualmente Zombie
//----- RILASCIO CODA DI MESSAGGI -----
msgctl(ID_Msg, IPC_RMID, 0);
Problemi di cooperazione nel modello a scambio di messaggi
IX) Si realizzi un processo che riceve messaggi da altri processi mediante una coda di messaggi e risponde con un intero che dice quanti messaggi sono stati ricevuti fino a quel momento. Per la comunicazione delle risposte si deve usare la stessa coda di messaggi delle richieste. La coda di messaggi deve essere creata dal primo processo che ne fa uso, non necessariamente il servente.
Descrizione: Il programma seguente mostra come è possibile implementare una comunicazione multidirezionale tra processi utilizzando una singola coda di messaggi. Il processo padre genera un certo numero di processi figli: tra questi viene casualmente designato un processo server mentre tutti gli altri divengono client. Il primo (fra tutti i processi figli) che entra in azione (dopo un tempo casuale) alloca la coda di messaggi e gli altri si limitano ad usarla. Inoltre, ciascun processo client invia un messaggio di HELLO sulla coda e attende una risposta dal server, affinchè gli comunichi quanti messaggi sono stati fino a quel momento inviati sulla coda, dopodichè termina. Il processo server attende al massimo che tutti i processi clienti abbiano inviato un messaggio a testa, dopodichè termina. Per la precisione, se ad allocare la coda è stato il server si occuperà personalmente di deallocarla prima di terminare, altrimenti ci penserà il client allocatore subito dopo aver ricevuto un segnale di cessata attività da parte del server. Si noti che il processo padre non ha alcun ruolo, se non quello di generare i processi clienti e il processo server.
Programma IX.C
#include <sys/types.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/wait.h>
#define DIM_MSG (2*sizeof(unsigned int)) //Cardinalità del Messaggio
#define HELLO 0 //Tipi del Messaggio
#define STOP 1
#define ACK (long)100000
typedef struct Messaggio;
#define N_PROC 10 //Numero totale di processi coinvolti, escluso il padre
//Di questi N_PROC, (N_PROC-1) saranno processo clienti
void main() {
pid_t pid; //Variabili locali
int i;
int status, myPID;
Messaggio msg;
//----- CHI SARA' IL PROCESSO SERVER? -----
int server=int(rand()) % N_PROC;
//----- CHI ALLOCHERA' LA CODA DI MESSAGGI? -----
int allocatore;
//----- GENERAZIONE DEI FIGLI -----
for(i=0; i<N_PROC; i++)
pid=fork(); //Generazione del figlio i-esimo
if (pid==-1) else if (!pid)
}
//----- COMPORTAMENTO DA CLIENT -----
if (i!=server)
exit(0); //Il figlio Client termina correttamente
//----- COMPORTAMENTO DA SERVER -----
} else
if (allocatore==myPID) else
exit(0); //Il figlio Server termina correttamente
}
}
} //End For N_PROC
//----- SINCRONIZZAZIONE DEL PADRE CON I FIGLI -----
for(i=0; i<N_PROC; i++)
Impiego del costrutto monitor
X) Si implementi il problema dei lettori/scrittori utilizzando il costrutto monitor.
Descrizione: Il costrutto monitor rappresenta un meccanismo di alto livello per implementare una risorsa gestore. In generale occorre definire, oltre alla risorsa da gestire, una struttura dati supplementare che contenga informazioni sullo stato della risorsa gestita. Una o più variabili di tipo condition vengono poi utilizzate per bloccare o sbloccare selettivamente i processi che competono per il controllo della risorsa, ciascuno in funzione della verifica (o meno) di una opportuna condizione di sincronizzazione. Il programma seguente implementa il problema dei lettori/scrittori evitando che l'una o l'altra categoria di processi possa andare in starvation:
Semafori.H (L'implementazione è quella indicata per il programma I)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
void Init_Sem(int, int); //Inizializza Semaforo
void Wait_Sem(int, int); //Wait su Semaforo
void Signal_Sem(int, int); //Signal su Semaforo
int Awaiting_Sem(int, int); //Restituisce il numero di processi in attesa su Semaforo
Monitor.H
#include <stdio.h>
#include <sys/wait.h>
#include "semafori.h"
typedef struct Monitor;
//Definizione di MACRO per l'accesso ai semafori del Monitor
#define MUTEX 0 //Il primo semaforo è sempre il Mutex
#define INITIALIZE(M,S,V) Init_Sem(M->ID_Sem,S,V)
#define WAIT(M,S) Wait_Sem(M->ID_Sem,S)
#define SIGNAL(M,S) Signal_Sem(M->ID_Sem,S)
#define AWAITING(M,S) Awaiting_Sem(M->ID_Sem,S)
#define ENTER_MONITOR(M) WAIT(M,MUTEX) //Implementazione della Entry
#define LEAVE_MONITOR(M) SIGNAL(M,MUTEX)
void Create_Monitor(Monitor*, int); //Semaforo Mutex e variabili Conditions vengono allocati
void Destroy_Monitor(Monitor*); //Semaforo Mutex e variabili Conditions vengono deallocati
void Wait_Cond(Monitor*, int); //Emula la Wait su una variabile Condition
void Signal_Cond(Monitor*, int); //Emula la Signal su una variabile Condition
Monitor.C
#include "monitor.c"
void Create_Monitor(Monitor* M, int N_Conditions)
void Destroy_Monitor(Monitor* M)
void Wait_Cond(Monitor* M, int ConditionID)
void Signal_Cond(Monitor* M, int ConditionID)
Lett_Scrit_Monitor.H
#include "monitor.h"
typedef struct Buffer;
void Init_Buffer(Buffer*); //Inizializza il Buffer
void Start_Monitor(Monitor*); //Inizializza il Monitor
void Stop_Monitor(Monitor*); //Elimina il Monitor
void Inizio_Lettura(Monitor*, Buffer*); //Acquisizione del buffer in lettura
char Lettura(Buffer*); //Lettura del buffer
void Fine_Lettura(Monitor*, Buffer*); //Rilascio del buffer dopo una lettura
void Inizio_Scrittura(Monitor*, Buffer*); //Acquisizione del buffer per la scrittura
void Scrittura(char, Buffer*); //Scrittura del buffer
void Fine_Scrittura(Monitor*, Buffer*); //Rilascio del buffer dopo una scrittura
Lett_Scrit_Monitor.C
#include "lett_scritt_monitor.h"
#define SOSPESI_LETTORI 1
#define SOSPESI_SCRITTORI 2
int Lettori_Sospesi;
void Init_Buffer(Buffer* B)
void Start_Monitor(Monitor* M)
void Stop_Monitor(Monitor* M)
void Inizio_Lettura(Monitor* M, Buffer* B)
char Lettura(Buffer* B)
void Fine_Lettura(Monitor* M, Buffer* B)
void Inizio_Scrittura(Monitor* M, Buffer* B)
void Scrittura(char value, Buffer* B)
void Fine_Scrittura(Monitor* M, Buffer* B)
Programma X.C
#include "lett_scrit_monitor.h"
#define DIM sizeof(Buffer) //Dimensione dell'area di memoria condivisa
#define NUM_LETT 5 //Numero di processi lettori
#define NUM_SCRIT 5 //Numero di processi scrittori
void main()
Ptr_Buf=(Buffer*)shmat(ID_Buf, 0, 0); //Il segmento allocato viene annesso al segmento dati
//del processo al primo indirizzo disponibile così come
//specificato dal sistema
if (Ptr_Buf==(Buffer*)-1)
Init_Buffer(Ptr_Buf); //Inizializzazione del Buffer
//----- CREAZIONE DEL MONITOR -----
Monitor M;
Start_Monitor(&M);
//----- GENERAZIONE FIGLI SCRITTORI -----
for(i=0; i<NUM_SCRIT; i++) {
pid=fork(); //Generazione del figlio Scrittore i-esimo
if (pid==-1) else if (!pid)
Inizio_Scrittura(&M, Ptr_Buf);
Scrittura(value, Ptr_Buf);
Fine_Scrittura(&M, Ptr_Buf);
exit(0); //Il figlio Scrittore termina correttamente
}
} //End For NUM_SCRIT
//----- GENERAZIONE FIGLI LETTORI -----
for(i=0; i<NUM_LETT; i++) {
pid=fork(); //Generazione del figlio Lettore i-esimo
if (pid==-1) else if (!pid)
} //End For NUM_LETT
//----- SINCRONIZZAZIONE DEL PADRE CON I FIGLI -----
for(i=0; i<NUM_SCRIT+NUM_LETT; i++)
//----- DISTRUZIONE DEL MONITOR -----
Stop_Monitor(&M);
//----- RILASCIO BUFFER DI MEMORIA CONDIVISA -----
shmctl(ID_Buf, IPC_RMID, 0);
Impiego del costrutto monitor
XI) Si implementi il problema dei produttori/consumatori utilizzando il costrutto monitor.
Descrizione: Il costrutto monitor rappresenta un meccanismo di alto livello per implementare una risorsa gestore. In generale occorre definire, oltre alla risorsa da gestire, una struttura dati supplementare che contenga informazioni sullo stato della risorsa gestita. Una o più variabili di tipo condition vengono poi utilizzate per bloccare o sbloccare selettivamente i processi che competono per il controllo della risorsa, ciascuno in funzione della verifica (o meno) di una opportuna condizione di sincronizzazione. Il programma seguente implementa il problema dei produttori/consumatori:
Semafori.H (L'implementazione è quella indicata per il programma I)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
void Init_Sem(int, int); //Inizializza Semaforo
void Wait_Sem(int, int); //Wait su Semaforo
void Signal_Sem(int, int); //Signal su Semaforo
int Awaiting_Sem(int, int); //Restituisce il numero di processi in attesa su Semaforo
Monitor.H (L'implementazione è quella indicata per il programma IX)
#include <stdio.h>
#include <sys/wait.h>
#include "semafori.h"
typedef struct Monitor;
//Definizione di MACRO per l'accesso ai semafori del Monitor
#define MUTEX 0 //Il primo semaforo è sempre il Mutex
#define INITIALIZE(M,S,V) Init_Sem(M->ID_Sem,S,V)
#define WAIT(M,S) Wait_Sem(M->ID_Sem,S)
#define SIGNAL(M,S) Signal_Sem(M->ID_Sem,S)
#define AWAITING(M,S) Awaiting_Sem(M->ID_Sem,S)
#define ENTER_MONITOR(M) WAIT(M,MUTEX) //Implementazione della Entry
#define LEAVE_MONITOR(M) SIGNAL(M,MUTEX)
void Create_Monitor(Monitor*, int); //Semaforo Mutex e variabili Conditions vengono allocati
void Destroy_Monitor(Monitor*); //Semaforo Mutex e variabili Conditions vengono deallocati
void Wait_Cond(Monitor*, int); //Emula la Wait su una variabile Condition
void Signal_Cond(Monitor*, int); //Emula la Signal su una variabile Condition
Prod_Cons_Monitor.H
#include "monitor.h"
#define DIM_QUEUE 4 //Cardinalità del Buffer (Coda circolare)
#define VUOTO 0 //Stati del singolo buffer della Coda circolare
#define IN_USO 1
#define PIENO 2
typedef struct Buffer;
void Init_Buffer(Buffer*); //Inizializza il Buffer
void Start_Monitor(Monitor*); //Inizializza il Monitor
void Stop_Monitor(Monitor*); //Elimina il Monitor
int Inizio_Consumo(Monitor*, Buffer*); //Acquisizione di un buffer per consumo
char Consumo(int, Buffer*); //Consumo di un buffer
void Fine_Consumo(int, Monitor*, Buffer*); //Rilascio del buffer dopo un consumo
int Inizio_Produzione(Monitor*, Buffer*); //Acquisizione del buffer per la produzione
void Produzione(int, char, Buffer*); //Produzione di un buffer
void Fine_Produzione(int, Monitor*, Buffer*); //Rilascio del buffer dopo la produzione
Prod_Cons_Monitor.C
#include "lett_scritt_monitor.h"
#define SOSPESI_CONSUMATORI 1
#define SOSPESI_PRODUTTORI 2
void Init_Buffer(Buffer* B)
void Start_Monitor(Monitor* M)
void Stop_Monitor(Monitor* M)
int Inizio_Consumo(Monitor* M, Buffer* B)
if (i==B->testa) B->testa=(B->testa+1) % DIM_QUEUE;
B->stato[i]=IN_USO;
LEAVE_MONITOR(M);
return i;
char Consumo(int i, Buffer* B)
void Fine_Consumo(int i, Monitor* M, Buffer* B)
int Inizio_Produzione(Monitor* M, Buffer* B) while (B->stato[B->coda]!=VUOTO);
B->stato[i]=IN_USO;
LEAVE_MONITOR(M);
return i;
void Produzione(int i, char value, Buffer* B)
void Fine_Produzione(int i, Monitor* M, Buffer* B)
Programma XI.C
#include 'prod_cons_monitor.h'
#define DIM sizeof(Buffer) //Dimensione dell'area di memoria condivisa
#define NUM_PROD 3 //Numero di processi produttori
#define NUM_PRODUZ 5 //Numero di produzioni per ogni processo produttore
#define NUM_CONS 5 //Numero di processi consumatori
#define NUM_CONSUM 3 //Numero di consumi per ogni processo consumatore
void main()
Ptr_Buf=(Buffer*)shmat(ID_Buf, 0, 0); //Il segmento allocato viene annesso al segmento dati
//del processo al primo indirizzo disponibile così come
//specificato dal sistema
if (Ptr_Buf==(Buffer*)-1)
Init_Buffer(Ptr_Buf); //Inizializzazione del Buffer
//----- CREAZIONE DEL MONITOR -----
Monitor M;
Start_Monitor(&M);
//----- GENERAZIONE FIGLI PRODUTTORI -----
for(i=0; i<NUM_PROD; i++) {
pid=fork(); //Generazione del figlio Produttore i-esimo
if (pid==-1) else if (!pid)
exit(0); //Il figlio Produttore termina correttamente
}
} //End For NUM_PROD
//----- GENERAZIONE FIGLI CONSUMATORI -----
for(i=0; i<NUM_CONS; i++) {
pid=fork(); //Generazione del figlio Consumatore i-esimo
if (pid==-1) else if (!pid)
exit(0); //Il figlio Consumatore termina correttamente
}
} //End For NUM_CONS
//----- SINCRONIZZAZIONE DEL PADRE CON I FIGLI -----
for(i=0; i<NUM_PROD+NUM_CONS; i++)
//----- DISTRUZIONE DEL MONITOR -----
Stop_Monitor(&M);
//----- RILASCIO BUFFER DI MEMORIA CONDIVISA -----
shmctl(ID_Buf, IPC_RMID, 0);
Impiego del costrutto monitor
XII) Scrivere una applicazione concorrente che implementi mediante un monitor il problema della gestione di un'unità a disco a teste mobili (secondo l'algoritmo dell'ascensore).
Descrizione: Il costrutto monitor rappresenta un meccanismo di alto livello per implementare una risorsa gestore. In generale occorre definire, oltre alla risorsa da gestire, una struttura dati supplementare che contenga informazioni sullo stato della risorsa gestita. Una o più variabili di tipo condition vengono poi utilizzate per bloccare o sbloccare selettivamente i processi che competono per il controllo della risorsa, ciascuno in funzione della verifica (o meno) di una opportuna condizione di sincronizzazione. Nel caso specifico, la libreria Monitor usata nei programmi precedenti è stata leggermente modificata in modo da supportare l'uso di una Wait con Priorità (è possibile comunque non specificare alcuna priorità, in tal caso la Wait su Condition si comporta esattamente come in un monitor senza priorità). Il programma seguente implementa il problema della gestione di un'unità a disco a testine mobili secondo l'algoritmo dello SCAN (altrimenti detto dell'Ascensore): il disco viene percorso da un estremo all'altro nelle direzioni SU (dalla traccia più esterna a quella più interna) e GIU (dalla traccia più interna a quella più esterna) e vengono servite le richieste di accesso al disco nell'ordine in cui si incontrano muovendosi lungo la direzione corrente; arrivati ad un estremo si inverte la direzione. Per ottimizzare le operazioni, si è inoltre fatto in modo che la direzione venga invertita qualora non si abbiano altre richieste di servizio nella direzione di percorrenza corrente (algoritmo LOOK). Si noti che per evitare il fenomeno della starvation non vengono servite richieste supplementari (rispetto alla prima) relative ad una traccia che si sta già servendo.
PMonitor.H
#include <stdio.h>
#include <sys/wait.h>
#include "semafori.h"
#define MAX_PROC (long)10 //Numero massimo di processi in coda su ogni Condition
typedef struct Monitor;
//Definizione di MACRO per l'accesso ai semafori del Monitor
#define MUTEX 0 //Il primo semaforo è sempre il Mutex
#define INITIALIZE(M,S,V) Init_Sem(M->ID_Sem,S,V)
#define WAIT(M,S) Wait_Sem(M->ID_Sem,S)
#define SIGNAL(M,S) Signal_Sem(M->ID_Sem,S)
#define AWAITING(M,S) Awaiting_Sem(M->ID_Sem,S)
#define ENTER_MONITOR(M) WAIT(M,MUTEX) //Implementazione della Entry
#define LEAVE_MONITOR(M) SIGNAL(M,MUTEX)
void Create_Monitor(Monitor*, int); //Semaforo Mutex e variabili Conditions vengono allocati
void Destroy_Monitor(Monitor*); //Semaforo Mutex e variabili Conditions vengono deallocati
void Wait_Cond(Monitor*, int, int = 1); //Emula la Wait (con priorità) su una variabile Condition
void Signal_Cond(Monitor*, int); //Emula la Signal su una variabile Condition
PMonitor.C
#include "pmonitor.c"
void Create_Monitor(Monitor* M, int N_Conditions)
M->Priority=(int*)shmat(ID_Buf, 0, 0); //Il segmento allocato viene annesso al segmento dati
//del processo al primo indirizzo disponibile così come
//specificato dal sistema
if (M->Priority==(int*)-1)
//Priority è gestito come una coda di priorità: ogni volta che un processo si pone in attesa sul Monitor, //aggancia a questo vettore la sua priorità. Il Monitor poi provvede a riordinarne in modo crescente gli
//elementi in modo che in coda a Priority ci sia sempre la priorità più alta (minimo 1)
for(int i=0; i<N_Conditions; i++) Priority[i*MAX_PROC]=i*MAX_PROC;
//In Priority[i*MAX_PROC] c'è il puntatore all'ultimo elemento del segmento i-esimo del vettore, ovvero
//il segmento che si riferisce alla (i+1)esima variabile condition
key_t Key_Sem=IPC_PRIVATE; //Chiave del gruppo di semafori
M->ID_Sem=semget(Key_Sem, N_Conditions+1, IPC_CREAT|0664);
if (M->ID_Sem==-1)
void Destroy_Monitor(Monitor* M)
void Wait_Cond(Monitor* M, int ConditionID, int P)
//Se il processo arriva QUI significa che è quello a priorità massima!
M->Priority[Zero]--; //e cancella la sua priorità da Priority
void Signal_Cond(Monitor* M, int ConditionID)
LOOK.H
#include "pmonitor.h"
#define TRACCE (long)1000
enum DIREZIONE ;
typedef struct Ascensore;
void Init_Ascensore(Ascensore*); //Inizializza l'Ascensore
void Start_Monitor(Monitor*); //Inizializza il Monitor
void Stop_Monitor(Monitor*); //Elimina il Monitor
void Richiesta(int, Monitor*, Ascensore*); //Richiesta di accesso al disco
void Accesso(int, Ascensore*); //Accesso ad una traccia
void Rilascio(Monitor*, Ascensore*); //Rilascio del disco
LOOK.C
#include "look.h"
#define DIREZIONE_SU 1
#define DIREZIONE_GIU 2
void Init_Ascensore(Ascensore* A)
void Start_Monitor(Monitor* M)
void Stop_Monitor(Monitor* M)
void Richiesta(int destinazione, Monitor* M, Ascensore* A)
void Accesso(int destinazione, Ascensore* A)
void Rilascio(Monitor* M, Ascensore* A)
} else
}
Programma XII.C
#include "look.h"
#define DIM sizeof(Ascensore) //Dimensione dell'area di memoria condivisa
#define NUM_PROC 10 //Numero di processi che eseguono accessi
void main()
Ptr_Buf=(Ascensore*)shmat(ID_Buf, 0, 0); //Il segmento allocato viene annesso al segmento dati
//del processo al primo indirizzo disponibile così come
//specificato dal sistema
if (Ptr_Buf==(Ascensore*)-1)
Init_Ascensore(Ptr_Buf); //Inizializzazione dell'Ascensore
//----- CREAZIONE DEL MONITOR -----
Monitor M;
Start_Monitor(&M);
//----- GENERAZIONE FIGLI CHE PRODUCONO GLI ACCESSI -----
for(i=0; i<NUM_PROC; i++) {
pid=fork(); //Generazione del figlio i-esimo
if (pid==-1) else if (!pid)
} //End For NUM_PROC
//----- SINCRONIZZAZIONE DEL PADRE CON I FIGLI -----
for(i=0; i<NUM_PROC; i++)
//----- DISTRUZIONE DEL MONITOR -----
Stop_Monitor(&M);
//----- RILASCIO BUFFER DI MEMORIA CONDIVISA -----
shmctl(ID_Buf, IPC_RMID, 0);
Impiego di file
XIII) Si realizzino due processi che interagiscono tramite memoria comune al fine di leggere dati da un disco. Il primo legge da disco blocchi di dati e li deposita in un insieme di buffer allocati in memoria comune. Il secondo processo stampa a video tutto ciò che trova in tali buffer.
Descrizione: Il programma presentato è un tipico esempio di cooperazione tra processi pertanto si è scelto di realizzarlo facendo riferimento al problema Produttori/Consumatori, con uso di un buffer multiplo di memoria comune, e facendo sincronizzare i due processi mediante scambio di messaggi. In pratica, il processo che legge i dati dal disco per poi depositarli nel buffer di memoria condivisa funge da produttore, mentre il processo che legge i dati dal buffer e li stampa a video funge da consumatore. I due processi interagiscono come segue:
Il produttore legge un blocco dati alla volta dal file finquando non ne viene raggiunta la fine.
Ad ogni lettura, il produttore deposita il blocco dati nel buffer di memoria comune, invia al consumatore una messaggio di SENT (Send Asincrona) con cui lo informa che un blocco dati è disponibile e si mette in attesa di un ACK da parte del consumatore (fondamentale perchè alla prossima lettura il contenuto del buffer sarà sovrascritto e finchè il consumatore non ha ricevuto il blocco dati corrente nessun'altra lettura è possibile).
Il consumatore è perennemente in attesa (Receive Bloccante) che un blocco dati sia disponibile, quando lo riceve ne stampa il contenuto a video e dopo invia l'ACK al produttore.
Quando il produttore è giunto (in lettura) alla fine del file, comunica al consumatore questa circostanza, il consumatore stampa a video un messaggio che segnala la fine delle operazioni ed entrambi terminano.
Programma XIII.C
#include <sys/types.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/wait.h>
#include <fcntl.h>
#define DIM 256 //Dimensione del Buffer condiviso
#define DIM_MSG (sizeof(unsigned int)) //Cardinalità del Messaggio
#define SENT 0 //Tipi del Messaggio
#define ACK 1
typedef struct Messaggio;
void main()
//----- ALLOCAZIONE BUFFER DI MEMORIA CONDIVISA -----
key_t Key_Buf=IPC_PRIVATE; //Chiave del buffer
int ID_Buf; //Identificatore del buffer
char* Ptr_Buf; //Puntatore al buffer
ID_Buf=shmget(Key_Buf, DIM, IPC_CREAT|0664); //Viene allocato un segmento di memoria di dimensione almeno
//pari DIM, gli viene associato un ID e viene creata una
//struttura dati ausiliaria che consenta di gestirlo
//RW per User, RW per Gruppo, R only per Others
if (ID_Buf==-1)
Ptr_Buf=shmat(ID_Buf, 0, 0); //Il segmento allocato viene annesso al segmento dati
//del processo al primo indirizzo disponibile così come
//specificato dal sistema
if (Ptr_Buf==(char*)-1)
//----- GENERAZIONE DEL FIGLIO PRODUTTORE -----
pid=fork(); //Generazione del figlio Produttore
if (pid==-1) else if (!pid)
do
if (N_Byte>=0)
//Rimane in attesa di un messaggio dal Consumatore
msgrcv(ID_Msg, (const void*)&msg, DIM_MSG, ACK, 0);
} while(N_Byte==DIM-1); //in caso contrario, con l'ultimo
//trasferimento si è raggiunto l'EOF
close(fd); //Chiusura del file
exit(0); //Il figlio Produttore termina correttamente
}
//----- GENERAZIONE DEL FIGLIO CONSUMATORE -----
pid=fork(); //Generazione del figlio Consumatore
if (pid==-1) else if (!pid) else
}
}
exit(0); //Il figlio Consumatore termina correttamente
}
//----- SINCRONIZZAZIONE DEL PADRE CON I FIGLI -----
for(i=0; i<2; i++)
//----- RILASCIO BUFFER E CODA DI MESSAGGI -----
shmctl(ID_Buf, IPC_RMID, 0);
msgctl(ID_Msg, IPC_RMID, 0);
Impiego di file
XIV) Si realizzino due processi che interagiscono tramite memoria comune al fine di duplicare dati da un disco. Il primo legge da disco blocchi di dati e li deposita in un insieme di buffer allocati in memoria comune. Il secondo processo crea un secondo file e vi scrive ciò che trova in tali buffer.
Descrizione: Il programma presentato è un tipico esempio di cooperazione tra processi pertanto si è scelto di realizzarlo facendo riferimento al problema Produttori/Consumatori, con uso di un buffer multiplo di memoria comune, e facendo sincronizzare i due processi mediante scambio di messaggi. In pratica, il processo che legge i dati dal disco per poi depositarli nel buffer di memoria condivisa funge da produttore, mentre il processo che legge i dati dal buffer e li deposita in un nuovo file funge da consumatore. I due processi interagiscono come segue:
Il produttore legge un blocco dati alla volta dal file finquando non ne viene raggiunta la fine.
Ad ogni lettura, il produttore deposita il blocco dati nel buffer di memoria comune, invia al consumatore una messaggio di SENT (Send Asincrona) con cui lo informa che un blocco dati è disponibile e si mette in attesa di un ACK da parte del consumatore (fondamentale perchè alla prossima lettura il contenuto del buffer sarà sovrascritto e finchè il consumatore non ha ricevuto il blocco dati corrente nessun'altra lettura è possibile).
Il consumatore è perennemente in attesa (Receive Bloccante) che un blocco dati sia disponibile, quando lo riceve lo invia in scrittura all'interno di un nuovo file (da esso inizialmente aperto) e dopo invia l'ACK al produttore.
Quando il produttore è giunto (in lettura) alla fine del file, comunica al consumatore questa circostanza, il consumatore stampa a video un messaggio che segnala la fine delle operazioni ed entrambi terminano.
Programma XIV.C
#include <sys/types.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/wait.h>
#include <fcntl.h>
#define DIM 256 //Dimensione del Buffer condiviso
#define DIM_MSG (sizeof(unsigned int)) //Cardinalità del Messaggio
#define SENT 0 //Tipi del Messaggio
#define ACK 1
typedef struct Messaggio;
void main()
//----- ALLOCAZIONE BUFFER DI MEMORIA CONDIVISA -----
key_t Key_Buf=IPC_PRIVATE; //Chiave del buffer
int ID_Buf; //Identificatore del buffer
char* Ptr_Buf; //Puntatore al buffer
ID_Buf=shmget(Key_Buf, DIM, IPC_CREAT|0664); //Viene allocato un segmento di memoria di dimensione almeno
//pari DIM, gli viene associato un ID e viene creata una
//struttura dati ausiliaria che consenta di gestirlo
//RW per User, RW per Gruppo, R only per Others
if (ID_Buf==-1)
Ptr_Buf=shmat(ID_Buf, 0, 0); //Il segmento allocato viene annesso al segmento dati
//del processo al primo indirizzo disponibile così come
//specificato dal sistema
if (Ptr_Buf==(char*)-1)
//----- GENERAZIONE DEL FIGLIO PRODUTTORE -----
pid=fork(); //Generazione del figlio Produttore
if (pid==-1) else if (!pid)
do
if (N_Byte>=0)
//Rimane in attesa di un messaggio di ACK dal Consumatore
msgrcv(ID_Msg, (const void*)&msg, DIM_MSG, ACK, 0);
if (msg.count<0)
} while(N_Byte==DIM-1); //in caso contrario, con l'ultimo
//trasferimento si è raggiunto l'EOF
close(fd); //Chiusura del file
exit(0); //Il figlio Produttore termina correttamente
}
//----- GENERAZIONE DEL FIGLIO CONSUMATORE -----
pid=fork(); //Generazione del figlio Consumatore
if (pid==-1) else if (!pid)
//Ciclo bloccante di lettura dalla coda di messaggi
while(true) else
if (msg.count<DIM-1)
}
} //END of WHILE
close(fd); //Chiusura del file
exit(0); //Il figlio Consumatore termina correttamente
}
//----- SINCRONIZZAZIONE DEL PADRE CON I FIGLI -----
for(i=0; i<2; i++)
//----- RILASCIO BUFFER E CODA DI MESSAGGI -----
shmctl(ID_Buf, IPC_RMID, 0);
msgctl(ID_Msg, IPC_RMID, 0);
Appunti su: |
|