Che materia stai cercando?

Anteprima

ESTRATTO DOCUMENTO

}

} //End For ... N_PROC

//----- SCHEDULAZIONE DEI FIGLI -----

for (i=0; i<N_PROC; i++) {

priority=1;

found=0;

while (!found) {

if (msgrcv(ID_Msg, (const void*)&msg, DIM_SG, priority, IPC_NOWAIT)!=-1) found=1;

else priority=(priority+1) % 3;

} //Almeno un processo ha richiesto

//la schedulazione...

printf("Mando in esecuzione il processo con PID %d e priorità %d\n", 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);

} 21

Problemi di cooperazione nel modello a scambio di messaggi

Si realizzi un processo che riceve messaggi da altri processi mediante una coda di

IX)

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

cessata attività

ricevuto un segnale di 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 { //Definizione del Messaggio

long tipo;

unsigned int PID;

unsigned int count; //Usato dal server per comunicare il numero di messaggi

//contenuti nella coda, ma anche dai client per comunicare

//il PID dell'allocatore della coda

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

perror("Impossibile creare un nuovo processo\n"); //In caso di errore...

exit(-1);

} else if (!pid) { //Codice del figlio

myPID=getpid(); //Memorizza il proprio PID

sleep(int(rand()) % N_PROC); //Attende un tempo casuale (pur essendo stato creato

//per primo potrebbe essere l'ultimo ad attivarsi)

//----- ALLOCAZIONE CODA DI MESSAGGI -----

key_t Key_Msg=ftok(“/usr/des/”,0); //Chiave della coda di messaggi

int ID_Msg; //Identificatore della coda di messaggi

allocatore=myPID; //Il processo corrente “suppone”

//di essere l'allocatore...

ID_Msg=msgget(Key_Msg, IPC_CREAT|IPC_EXCL|0664); //Si tenta di allocare in modo esclusivo una coda

//di messaggi...

if (ID_Msg==-1) {

allocatore=0; //Non è lui l'allocatore

if (errno==EEXIST) ID_Msg=msgget(Key_Msg, IPC_ALLOC); //Se la coda identificata da quella

//chiave già esiste, si limita ad

//allocare quella esistente

else { perror("MSGGET\n"); //In caso di altri errori...

exit(-1);

} 22

}

//----- COMPORTAMENTO DA CLIENT -----

if (i!=server) { //Se il processo corrente non è il server

printf("Sono il figlio Client con PID %d\n", myPID);

msg.tipo=HELLO;

msg.PID=myPID;

msg.count=allocatore;

msgsnd(ID_Msg, (const void*)&msg, DIM_MSG, 0); //Invia il messaggio sulla coda

msgrcv(ID_Msg, (const void*)&msg, DIM_MSG, (myPID+ACK), 0);

//Attende che il server invii un messaggio che

//abbia come TIPO esattamente il suo PID sommato

//alla costante ACK

printf("I messaggi sulla coda sono – al momento dell'invio del mio messaggio – pari a %d\n", msg.count);

//Solo se il processo corrente è l'allocatore, attende che il server gli

//comunichi la sua terminazione...

if (allocatore==myPID) {

msgrcv(ID_Msg, (const void*)&msg, DIM_MSG, STOP, 0);

//----- RILASCIO CODA DI MESSAGGI -----

msgctl(ID_Msg, IPC_RMID, 0);

}

exit(0); //Il figlio Client termina correttamente

//----- COMPORTAMENTO DA SERVER -----

} else {

printf("Sono il figlio Server con PID %d\n", myPID);

int count=0; //Contatore di messaggi finora ricevuti

int To_Send=NUM_PROC-1; //Messaggi ancora da mandare

while(To_Send) {

int found=0;

while(!found)

if (msgrcv(ID_Msg, (const void*)&msg, DIM_SG, HELLO, IPC_NOWAIT)!=-1) found=1;

//Appena trova un messaggio di HELLO

count++; //Incrementa il contatore

//Se l'allocatore è tuttora ignoto...

if (!allocatore) allocatore = msg.count;

msg.tipo = msg.PID + ACK;

msg.count = count;

msgsnd(ID_Msg, (const void*)&msg, DIM_MSG, 0);

//E comunica al processo che l'ha inviato il numero totale di messaggi

//di HELLO ricevuti fino a questo momento

To_Send--;

}

if (allocatore==myPID) {

msgctl(ID_Msg, IPC_RMID, 0); //Rilascia personalmente la CODA DI MESSAGGI

} else { //Segnala la sua terminazione

msg.tipo = STOP;

msgsnd(ID_Msg, (const void*)&msg, DIM_MSG, 0);

}

exit(0); //Il figlio Server termina correttamente

}

}

} //End For ... N_PROC

//----- SINCRONIZZAZIONE DEL PADRE CON I FIGLI -----

for(i=0; i<N_PROC; i++) { //Il padre rimane in attesa che i figli terminino...

pid=wait(&status);

printf("Il figlio con PID %d è terminato!\n",pid);

}

} 23

Impiego del costrutto monitor

Si implementi il problema dei lettori/scrittori utilizzando il costrutto monitor.

X)

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

condition

contenga informazioni sullo stato della risorsa gestita. Una o più variabili di tipo 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

starvation

andare in :

 Ogni processo che voglia accedere al monitor deve farlo in mutua esclusione con gli altri processi.

 Un lettore che voglia leggere il contenuto del buffer di memoria condivisa (non deve farlo in mutua

esclusione con gli altri lettori!) rimane in attesa sulla variabile condition SOSPESI_LETTORI solo nel

caso in cui vi siano scrittori attivi o scrittori già in attesa.

 Uno scrittore che voglia scrivere sul buffer rimane in attesa sulla variabile condition

SOSPESI_SCRITTORI solo se vi sono lettori attivi o scrittori attivi.

 Quando un lettore riesce ad ottenere l'accesso al buffer, tutti gli altri in attesa passano a valanga.

 Uno scrittore che riesca ad ottenere l'accesso al buffer, una volta terminata la scrittura, sblocca

tutti gli eventuali lettori 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

 Monitor.H

#include <stdio.h>

#include <sys/wait.h>

#include “semafori.h”

typedef struct {

int ID_Sem;

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

key_t Key_Sem=IPC_PRIVATE; //Chiave del gruppo di semafori

M->ID_Sem=semget(Key_Sem, N_Conditions+1, IPC_CREAT|0664);

//Viene allocato un gruppo di semafori di cardinalità pari

//al numero delle “conditions” più un semaforo Mutex,

//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 (M->ID_Sem==-1) {

perror("MONITOR SEMGET\n"); //In caso di errore...

exit(-1);

}

}

void Destroy_Monitor(Monitor* M) { semctl(M->ID_Sem, 0, IPC_RMID); }

void Wait_Cond(Monitor* M, int ConditionID) {

LEAVE_MONITOR(M);

WAIT(M,ConditionID);

}

void Signal_Cond(Monitor* M, int ConditionID) { if (AWAITING(M,ConditionID)>0) SIGNAL(M,ConditionID); }

 Lett_Scrit_Monitor.H

#include “monitor.h”

typedef struct { //Definizione del Buffer

24

char value;

int lettori; //Contiene anche le variabili di stato necessarie...

int Lettori_Sospesi;

bool scrittore;

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

B->value=(char)0;

B->lettori=0;

B->Lettori_Sospesi=0;

B->scrittori=false;

}

void Start_Monitor(Monitor* M) {

Create_Monitor(M,2);

INITIALIZE(MUTEX,1);

INITIALIZE(SOSPESI_LETTORI,0);

INITIALIZE(SOSPESI_SCRITTORI,0);

}

void Stop_Monitor(Monitor* M) { Destroy_Monitor(M); }

void Inizio_Lettura(Monitor* M, Buffer* B) {

ENTER_MONITOR(M);

if (B->scrittore || AWAITING(M,SOSPESI_SCRITTORI)) Wait_Cond(M,SOSPESI_LETTORI);

if (Lettori_Sospesi) Lettori_Sospesi--; //Lettori_Sospesi memorizza il numero di Lettori da

B->lettori++; //sbloccare “simultaneamente”, e solo quelli!

if (B->Lettori_Sospesi) SIGNAL(M,SOSPESI_LETTORI); //il precedente sblocca il successivo...

else LEAVE_MONITOR(M); //e l'ultimo lettore sblocca il monitor...

}

char Lettura(Buffer* B) {

char value=B->value;

printf("Il processo con PID %d ha letto %c\n", getpid(), value);

return value;

}

void Fine_Lettura(Monitor* M, Buffer* B) {

ENTER_MONITOR(M);

B->lettori--;

if (B->lettori==0 && AWAITING(M,SOSPESI_SCRITTORI)) Signal_Cond(M,SOSPESI_SCRITTORI);

else LEAVE_MONITOR(M);

}

void Inizio_Scrittura(Monitor* M, Buffer* B) {

ENTER_MONITOR(M);

if (B->scrittore || B->lettori) Wait_Cond(M,SOSPESI_SCRITTORI);

B->scrittore=true;

LEAVE_MONITOR(M);

}

void Scrittura(char value, Buffer* B) {

B->value=value;

printf("Il processo con PID %d ha scritto %c\n", getpid(), value);

}

void Fine_Scrittura(Monitor* M, Buffer* B) {

ENTER_MONITOR(M);

B->scrittore=false;

if (Lettori_Sospesi=AWAITING(M,SOSPESI_LETTORI)) SIGNAL(M,SOSPESI_LETTORI);

else if (AWAITING(M,SOSPESI_SCRITTORI)) SIGNAL(M,SOSPESI_SCRITTORI);

else LEAVE_MONITOR(M);

}

 Programma X.C 25

#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() {

pid_t pid; //Variabili locali

int i;

char value='A', lettura;

int status;

//----- ALLOCAZIONE BUFFER DI MEMORIA CONDIVISA -----

key_t Key_Buf=IPC_PRIVATE; //Chiave del buffer

int ID_Buf; //Identificatore del buffer

Buffer* Ptr_Buf; //Puntatore al buffer

ID_Buf=shmget(Key_Buf, DIM, IPC_CREAT|0664); //Viene allocato un segmento di memoria atto a contenere

//un Buffer, 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) {

perror("SHMGET\n"); //In caso di errore...

exit(-1);

}

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) {

perror("SHMAT\n"); //In caso di errore...

shmctl(ID_Buf, IPC_RMID, 0); //Rimuove l'ID della Shared Memory indicata, dealloca

//il segmento di memoria corrispondente ed elimina le

//strutture dati ad esso associate

exit(-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) {

perror("Impossibile creare un nuovo processo\n"); //In caso di errore...

exit(-1);

} else if (!pid) { //Codice del figlio Scrittore

printf("Sono un aspirante Scrittore, PID = %d\n", getpid());

if (i>0) { //Se non è il primo scrittore...

value++;

sleep(5); //Simula la produzione del dato da scrivere

}

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) {

perror("Impossibile creare un nuovo processo\n"); //In caso di errore...

exit(-1);

} else if (!pid) { //Codice del figlio Lettore

printf("Sono un aspirante Lettore, PID = %d\n", getpid());

Inizio_Lettura(&M, Ptr_Buf);

lettura=Lettura(Ptr_Buf);

Fine_Lettura(&M, Ptr_Buf);

exit(0); //Il figlio Lettore termina correttamente

}

} //End For ... NUM_LETT

//----- SINCRONIZZAZIONE DEL PADRE CON I FIGLI -----

for(i=0; i<NUM_SCRIT+NUM_LETT; i++) { //Il padre rimane in attesa che i figli terminino...

pid=wait(&status); 26

printf("Il figlio con PID %d è terminato!\n",pid);

}

//----- DISTRUZIONE DEL MONITOR -----

Stop_Monitor(&M);

//----- RILASCIO BUFFER DI MEMORIA CONDIVISA -----

shmctl(ID_Buf, IPC_RMID, 0);

} 27

Impiego del costrutto monitor

Si implementi il problema dei produttori/consumatori utilizzando il costrutto

XI)

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

condition

contenga informazioni sullo stato della risorsa gestita. Una o più variabili di tipo 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:

 contenuto

Un produttore che voglia scrivere un nel buffer di memoria condivisa deve farlo in mutua

esclusione con gli altri produttori.

 contenuto

Un consumatore che voglia prelevare un dal buffer di memoria condivisa deve farlo in mutua

esclusione con gli altri consumatori.

 contenuto

Quando un produttore riesce ad ottenere l'accesso al buffer, dopo avervi depositato un ,

sblocca uno dei consumatori eventualmente in attesa.

 contenuto

Quando un consumatore riesce ad ottenere l'accesso al buffer, una volta prelevatone il ,

sblocca uno dei produttori eventualmente 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

 Monitor.H (L'implementazione è quella indicata per il programma IX)

#include <stdio.h>

#include <sys/wait.h>

#include “semafori.h”

typedef struct {

int ID_Sem;

} 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 { //Definizione della Coda circolare

char buffer[DIM_QUEUE];

char stato[DIM_QUEUE]; //VUOTO, IN_USO o PIENO

int testa;

int coda;

int messaggi; //Numero di messaggi attualmente contenuti nella coda...

} 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

28

 Prod_Cons_Monitor.C

#include “lett_scritt_monitor.h”

#define SOSPESI_CONSUMATORI 1

#define SOSPESI_PRODUTTORI 2

void Init_Buffer(Buffer* B) {

for(int i=0; i<DIM_QUEUE; i++) B->stato[i]=VUOTO;

B->testa=0;

B->coda=0;

B->messaggi=0;

}

void Start_Monitor(Monitor* M) {

Create_Monitor(M,2);

INITIALIZE(MUTEX,1);

INITIALIZE(SOSPESI_CONSUMATORI,0);

INITIALIZE(SOSPESI_PRODUTTORI,0);

}

void Stop_Monitor(Monitor* M) { Destroy_Monitor(M); }

int Inizio_Consumo(Monitor* M, Buffer* B) {

ENTER_MONITOR(M);

if (B->messaggi==0) Wait_Cond(M,SOSPESI_CONSUMATORI);

int i=B->testa;

while (B->stato[i]!=PIENO) { i=(i+1) % DIM_QUEUE; }

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) { return B->buffer[i]; }

void Fine_Consumo(int i, Monitor* M, Buffer* B) {

ENTER_MONITOR(M);

B->stato[i]=VUOTO;

if (AWAITING(M,SOSPESI_PRODUTTORI)) Signal_Cond(M,SOSPESI_PRODUTTORI);

else LEAVE_MONITOR(M);

}

int Inizio_Produzione(Monitor* M, Buffer* B) {

ENTER_MONITOR(M);

if (B->messaggi==DIM_QUEUE) Wait_Cond(M,SOSPESI_PRODUTTORI);

int i=B->coda;

do { B->coda=(B->coda+1) % DIM_QUEUE; } while (B->stato[B->coda]!=VUOTO);

B->stato[i]=IN_USO;

LEAVE_MONITOR(M);

return i;

}

void Produzione(int i, char value, Buffer* B) { B->buffer[i]=value; }

void Fine_Produzione(int i, Monitor* M, Buffer* B) {

ENTER_MONITOR(M);

B->stato[i]=PIENO;

if (AWAITING(M,SOSPESI_CONSUMATORI)) Signal_Cond(M,SOSPESI_CONSUMATORI);

else LEAVE_MONITOR(M);

}

 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() {

pid_t pid; //Variabili locali

int i,j,k;

char value='A';

int status;

//----- ALLOCAZIONE BUFFER DI MEMORIA CONDIVISA -----

key_t Key_Buf=IPC_PRIVATE; //Chiave del buffer

int ID_Buf; //Identificatore del buffer

Buffer* Ptr_Buf; //Puntatore al buffer

ID_Buf=shmget(Key_Buf, DIM, IPC_CREAT|0664); //Viene allocato un segmento di memoria atto a contenere

//un Buffer, 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) {

perror("SHMGET\n"); //In caso di errore...

exit(-1); 29

}

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) {

perror("SHMAT\n"); //In caso di errore...

shmctl(ID_Buf, IPC_RMID, 0); //Rimuove l'ID della Shared Memory indicata, dealloca

//il segmento di memoria corrispondente ed elimina le

//strutture dati ad esso associate

exit(-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) {

perror("Impossibile creare un nuovo processo\n"); //In caso di errore...

exit(-1);

} else if (!pid) { //Codice del figlio Produttore

printf("Sono un Produttore, PID = %d\n", getpid());

for (j=0; j<NUM_PRODUZ; j++) {

k=Inizio_Produzione(&M, Ptr_Buf);

Produzione(k, value, Ptr_Buf);

Fine_Produzione(k, &M, Ptr_Buf);

value++; //Avanza nell'alfabeto

sleep(5); //Simula un tempo di produzione

}

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) {

perror("Impossibile creare un nuovo processo\n"); //In caso di errore...

exit(-1);

} else if (!pid) { //Codice del figlio Consumatore

printf("Sono un Consumatore, PID = %d\n", getpid());

sleep(3); //Attende prima di tentare di consumare

for (j=0; j<NUM_CONSUM; j++) {

k=Inizio_Consumo(&M, Ptr_Buf);

char consumo=Consumo(k, Ptr_Buf);

Fine_Consumo(i, &M, Ptr_Buf);

sleep(5); //Simula un tempo di consumo

}

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++) { //Il padre rimane in attesa che i figli terminino...

pid=wait(&status);

printf("Il figlio con PID %d è terminato!\n",pid);

}

//----- DISTRUZIONE DEL MONITOR -----

Stop_Monitor(&M);

//----- RILASCIO BUFFER DI MEMORIA CONDIVISA -----

shmctl(ID_Buf, IPC_RMID, 0);

} 30

Impiego del costrutto monitor

Scrivere una applicazione concorrente che implementi mediante un monitor il problema

XII)

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

condition

contenga informazioni sullo stato della risorsa gestita. Una o più variabili di tipo 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

starvation

corrente (algoritmo LOOK). Si noti che per evitare il fenomeno della 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” Condition

#define MAX_PROC (long)10 //Numero massimo di processi in coda su ogni

typedef struct {

int ID_Sem;

int ID_Buf; //Campi aggiuntivi per implementare la WAIT con PRIORITA'

int* Priority;

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

key_t Key_Buf=IPC_PRIVATE; //Chiave del buffer

M->ID_Buf=shmget(Key_Buf, N_Conditions*(sizeof(int)*MAX_PROC), IPC_CREAT|0664);

if (M->ID_Buf==-1) {

perror("MONITOR SHMGET\n"); //In caso di errore...

exit(-1);

}

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) {

perror("MONITOR SHMAT\n"); //In caso di errore...

shmctl(ID_Buf, IPC_RMID, 0); //Rimuove l'ID della Shared Memory indicata, dealloca

//il segmento di memoria corrispondente ed elimina le

//strutture dati ad esso associate

exit(-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; segmento

//In Priority[i*MAX_PROC] c'è il puntatore all'ultimo elemento del i-esimo del vettore, ovvero

segmento

//il 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) {

perror("MONITOR SEMGET\n"); //In caso di errore...

31

exit(-1);

}

}

void Destroy_Monitor(Monitor* M) {

shmctl(M->ID_Buf, IPC_RMID, 0);

semctl(M->ID_Sem, 0, IPC_RMID);

}

void Wait_Cond(Monitor* M, int ConditionID, int P) {

//Inserimento ordinato della priorità P nell'opportuno segmento del vettore Priority...

int Zero=(ConditionID-1)*MAX_PROC;

for(int i=Zero; (M->Priority[i]<=P) && (i<=M->Priority[Zero]); i++);

M->Priority[Zero]++;

for(int j=M->Priority[Zero]; j>i; j--) M->Priority[j]=M->Priority[j-1];

M->Priority[i]=P;

LEAVE_MONITOR(M);

WAIT(M,ConditionID);

while(M->Priority[M->Priority[Zero]]>P) { //Se la priorità indicata NON è quella massima...

SIGNAL(M,ConditionID); //Sblocca un altro processo...

WAIT(M,ConditionID); //...e si risospende

}

//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) { if (AWAITING(M,ConditionID)>0) SIGNAL(M,ConditionID); }

 LOOK.H

#include “pmonitor.h”

#define TRACCE (long)1000

enum DIREZIONE {SU, GIU};

typedef struct { //Definizione della struttura dati Ascensore

int posizione;

DIREZIONE direzione;

bool occupato;

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

A->posizione=0;

A->DIREZIONE=SU;

A->occupato=false;

}

void Start_Monitor(Monitor* M) {

Create_Monitor(M,2);

INITIALIZE(MUTEX,1);

INITIALIZE(DIREZIONE_SU,0);

INITIALIZE(DIREZIONE_GIU,0);

}

void Stop_Monitor(Monitor* M) { Destroy_Monitor(M); }

void Richiesta(int destinazione, Monitor* M, Ascensore* A) {

ENTER_MONITOR(M);

if (A->occupato)

if ((A->posizione==destinazione && A->direzione==SU) ||

(destinazione<A->posizione)) Wait_Cond(M, DIREZIONE_GIU, destinazione);

else Wait_Cond(M, DIREZIONE_SU, TRACCE-destinazione);

A->occupato=true;

LEAVE_MONITOR(M);

}

void Accesso(int destinazione, Ascensore* A) { A->posizione=destinazione; }

void Rilascio(Monitor* M, Ascensore* A) {

ENTER_MONITOR(M);

A->occupato=false; 32

if (A->direzione==SU) {

if (AWAITING(M,DIREZIONE_SU)) Signal_Cond(M,DIREZIONE_SU);

else { A->direzione=GIU;

if (AWAITING(M,DIREZIONE_GIU)) Signal_Cond(M,DIREZIONE_GIU);

else LEAVE_MONITOR(M);

}

} else {

if (AWAITING(M,DIREZIONE_GIU)) Signal_Cond(M,DIREZIONE_GIU);

else { A->direzione=SU;

if (AWAITING(M,DIREZIONE_SU)) Signal_Cond(M,DIREZIONE_SU);

else LEAVE_MONITOR(M);

}

}

}

 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() {

pid_t pid; //Variabili locali

int i;

int destinazione;

int status;

//----- ALLOCAZIONE BUFFER DI MEMORIA CONDIVISA -----

key_t Key_Buf=IPC_PRIVATE; //Chiave del buffer

int ID_Buf; //Identificatore del buffer

Ascensore* Ptr_Buf; //Puntatore al buffer

ID_Buf=shmget(Key_Buf, DIM, IPC_CREAT|0664); //Viene allocato un segmento di memoria atto a contenere

//un Buffer, 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) {

perror("SHMGET\n"); //In caso di errore...

exit(-1);

}

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) {

perror("SHMAT\n"); //In caso di errore...

shmctl(ID_Buf, IPC_RMID, 0); //Rimuove l'ID della Shared Memory indicata, dealloca

//il segmento di memoria corrispondente ed elimina le

//strutture dati ad esso associate

exit(-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) {

perror("Impossibile creare un nuovo processo\n"); //In caso di errore...

exit(-1);

} else if (!pid) { //Codice del figlio

destinazione=int(rand()) % TRACCE;

printf("Sono il figlio con PID %d e voglio accedere a %d\n", getpid(), destinazione);

Richiesta(destinazione, &M, Ptr_Buf);

Accesso(destinazione, Ptr_Buf); //QUI si può produrre una Lettura o una Scrittura...

Rilascio(&M, Ptr_Buf);

exit(0); //Il figlio termina correttamente

}

} //End For ... NUM_PROC

//----- SINCRONIZZAZIONE DEL PADRE CON I FIGLI -----

for(i=0; i<NUM_PROC; i++) { //Il padre rimane in attesa che i figli terminino...

pid=wait(&status);

printf("Il figlio con PID %d è terminato!\n",pid);

} 33

//----- DISTRUZIONE DEL MONITOR -----

Stop_Monitor(&M);

//----- RILASCIO BUFFER DI MEMORIA CONDIVISA -----

shmctl(ID_Buf, IPC_RMID, 0);

} 34

Impiego di file

Si realizzino due processi che interagiscono tramite memoria comune al fine di

XIII)

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

produttore

i dati dal disco per poi depositarli nel buffer di memoria condivisa funge da , mentre il processo

consumatore

che legge i dati dal buffer e li stampa a video funge da . I due processi interagiscono come segue:

 produttore

Il legge un blocco dati alla volta dal file finquando non ne viene raggiunta la fine.

 produttore

Ad ogni lettura, il 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

consumatore

e si mette in attesa di un ACK da parte del (fondamentale perchè alla prossima lettura il

consumatore

contenuto del buffer sarà sovrascritto e finchè il non ha ricevuto il blocco dati corrente

nessun'altra lettura è possibile).

 consumatore

Il è perennemente in attesa (Receive Bloccante) che un blocco dati sia disponibile, quando

produttore

lo riceve ne stampa il contenuto a video e dopo invia l'ACK al .

 produttore consumatore

Quando il è giunto (in lettura) alla fine del file, comunica al questa

consumatore

circostanza, il 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 { //Definizione del Messaggio

long tipo;

unsigned int count; //Contiene il numero di Byte depositati dal

//produttore nel buffer...

} Messaggio;

void main() {

pid_t pid; //Variabili locali

int i;

int status;

int N_Byte; //Contatore di byte letti

Messaggio msg;

//----- ALLOCAZIONE CODA DI MESSAGGI -----

key_t Key_Msg=IPC_PRIVATE; //Chiave della coda di messaggi

int ID_Msg; //Identificatore della coda di messaggi

ID_Msg=msgget(Key_Msg, IPC_CREAT|0664); //Viene allocata una coda di messaggi, le viene associato

//un ID e viene creata una struttura dati ausiliaria che

//consenta di gestirla, con i seguenti permessi

//RW per User, RW per Gruppo, R only per Others

if (ID_Msg==-1) {

perror("MSGGET\n"); //In caso di errore...

exit(-1);

}

//----- 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) {

perror("SHMGET\n"); //In caso di errore...

exit(-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) {

perror("SHMAT\n"); //In caso di errore...

shmctl(ID_Buf, IPC_RMID, 0); //Rimuove l'ID della Shared Memory indicata, dealloca

//il segmento di memoria corrispondente ed elimina le

//strutture dati ad esso associate

exit(-1);

}

//----- GENERAZIONE DEL FIGLIO PRODUTTORE ----- 35

pid=fork(); //Generazione del figlio Produttore

if (pid==-1) {

perror("Impossibile creare un nuovo processo\n"); //In caso di errore...

exit(-1);

} else if (!pid) { //Codice del figlio Produttore

file handle

int fd=open("/usr/testo",O_RDONLY); //Apertura del file (fd è un )

if (fd==-1) { //In caso di errore nell'apertura...

msg.tipo=SENT; //Invia un messaggio di Abort al Consumatore...

msg.count=-1;

msgsnd(ID_Msg, (const void*)&msg, DIM_MSG, 0);

perror("ERROR in OPEN\n"); //...e termina

exit(-1);

}

do { N_Byte=read(fd, Ptr_Buf, (DIM-1)); //Legge ripetutamente blocchi di dati dal file

//aperto (l'ultimo carattere è riservato per

//l'inserimento del simbolo '\0' di terminazione

//di stringa)

if (N_Byte==-1) { //In caso di errore durante la lettura...

msg.tipo=SENT; //Invia un messaggio di Abort al Consumatore...

msg.count=-2;

msgsnd(ID_Msg, (const void*)&msg, DIM_MSG, 0);

perror("ERROR in READ\n"); //...e termina

exit(-1);

}

if (N_Byte>=0) { //Se è stato letto almeno un byte o è stato

//raggiunto l'EOF...

Ptr_Buf[N_Byte]='\0'; //La stringa viene terminata

msg.tipo=SENT;

msg.count=N_Byte;

msgsnd(ID_Msg, (const void*)&msg, DIM_MSG, 0); //Invia il messaggio di SENT

}

//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) {

perror("Impossibile creare un nuovo processo\n"); //In caso di errore...

exit(-1);

} else if (!pid) { //Codice del figlio Consumatore

//Ciclo bloccante di lettura dalla coda di messaggi...

while(true) {

//Rimane in attesa di un messaggio dal Produttore

msgrcv(ID_Msg, (const void*)&msg, DIM_MSG, SENT, 0);

if (msg.count<0) { //Gestione della terminazione anomala del Produttore

if (msg.count==-1) printf("Lettura lato server mai cominciata!\n");

else printf("Lettura lato server interrotta!\n");

break;

} else { //Se è stato letto almeno un byte o è stato

//raggiunto l'EOF...

if (msg.count) printf(“%s”,Ptr_Buf); //Stampa a video il contenuto del buffer

msg.tipo=ACK;

msgsnd(ID_Msg, (const void*)&msg, DIM_MSG, 0); //Invia il messaggio di ACK

if (msg.count<DIM-1) {

printf(“ -> EOF!”); //Raggiunto l'EOF

break;

}

}

}

exit(0); //Il figlio Consumatore termina correttamente

}

//----- SINCRONIZZAZIONE DEL PADRE CON I FIGLI -----

for(i=0; i<2; i++) { //Il padre rimane in attesa che i figli terminino...

36

pid=wait(&status);

printf("Il figlio con PID %d è terminato!\n",pid);

}

//----- RILASCIO BUFFER E CODA DI MESSAGGI -----

shmctl(ID_Buf, IPC_RMID, 0);

msgctl(ID_Msg, IPC_RMID, 0);

} 37

Impiego di file

Si realizzino due processi che interagiscono tramite memoria comune al fine di

XIV)

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

produttore

i dati dal disco per poi depositarli nel buffer di memoria condivisa funge da , mentre il processo

consumatore

che legge i dati dal buffer e li deposita in un nuovo file funge da . I due processi interagiscono

come segue:

 produttore

Il legge un blocco dati alla volta dal file finquando non ne viene raggiunta la fine.

 produttore

Ad ogni lettura, il 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

consumatore

e si mette in attesa di un ACK da parte del (fondamentale perchè alla prossima lettura il

consumatore

contenuto del buffer sarà sovrascritto e finchè il non ha ricevuto il blocco dati corrente

nessun'altra lettura è possibile).

 consumatore

Il è 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

produttore

l'ACK al .

 produttore consumatore

Quando il è giunto (in lettura) alla fine del file, comunica al questa

consumatore

circostanza, il 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 { //Definizione del Messaggio

long tipo;

unsigned int count; //Contiene il numero di Byte depositati dal

//produttore nel buffer...

} Messaggio;

void main() {

pid_t pid; //Variabili locali

int i;

int status;

int N_Byte; //Contatore di byte letti

Messaggio msg;

//----- ALLOCAZIONE CODA DI MESSAGGI -----

key_t Key_Msg=IPC_PRIVATE; //Chiave della coda di messaggi

int ID_Msg; //Identificatore della coda di messaggi

ID_Msg=msgget(Key_Msg, IPC_CREAT|0664); //Viene allocata una coda di messaggi, le viene associato

//un ID e viene creata una struttura dati ausiliaria che

//consenta di gestirla, con i seguenti permessi

//RW per User, RW per Gruppo, R only per Others

if (ID_Msg==-1) {

perror("MSGGET\n"); //In caso di errore...

exit(-1);

}

//----- 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) {

perror("SHMGET\n"); //In caso di errore...

exit(-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) {

perror("SHMAT\n"); //In caso di errore...

shmctl(ID_Buf, IPC_RMID, 0); //Rimuove l'ID della Shared Memory indicata, dealloca

//il segmento di memoria corrispondente ed elimina le

//strutture dati ad esso associate

exit(-1);

} 38


PAGINE

41

PESO

167.10 KB

PUBBLICATO

+1 anno fa


DETTAGLI
Corso di laurea: Corso di laurea in ingegneria informatica
SSD:
A.A.: 2013-2014

I contenuti di questa pagina costituiscono rielaborazioni personali del Publisher cecilialll di informazioni apprese con la frequenza delle lezioni di Sistemi operativi e studio autonomo di eventuali libri di riferimento in preparazione dell'esame finale o della tesi. Non devono intendersi come materiale ufficiale dell'università Napoli Federico II - Unina o del prof De Carlini Riccardo.

Acquista con carta o conto PayPal

Scarica il file tutte le volte che vuoi

Paga con un conto PayPal per usufruire della garanzia Soddisfatto o rimborsato

Recensioni
Ti è piaciuto questo appunto? Valutalo!

Altri appunti di Sistemi operativi

Sistemi Operativi
Dispensa
Sistemi Operativi
Dispensa
Sistemi operativi - schema suntivo per la prova pratica
Appunto
Sistemi operativi - Syscall atoi
Appunto