Salve, vi scrivo perché dovrei svolgere il seguente esercizio : https://www.sendspace.com/file/mntjeb (pdf con la traccia) sfruttando code di messaggi, phtread.h,fork ed exec ... insomma tutto quello che forniscono le librerie UNIX in merito a problemi di sincronizzazione tra processi e tra threads.
Ho realizzato il progetto che consta di 4 file (5 contando il Makefile) : client-server.h , client.cpp , server.cpp , master.cpp
Come comprensibile dalla traccia master lancia 3 client ed 1 server e quest'ultimo lancia 3 threads ( 1 Manager e 2 Workers).
I file da me creati sono:
client-server.h:
codice:
//definisco i parametri della ftok per generare le due code di messaggio univocamente
#define REQUEST_QUEUE_PATH "."
#define REQUEST_QUEUE_CHAR 'a'
#define ANSWER_QUEUE_PATH "."
#define ANSWER_QUEUE_CHAR 'b'
//definisco gli id delle code di msg e i threads come variabili globali per le starting routines controlla ed elabora
static int request_id;
static int answer_id;
static pthread_t threads[3];
//definisco la struttura "form" che devono compilare i processi client che si collegano al processo server per ottenere l'elaborazione
typedef struct {
long pid; //è il tipo
int n1;
int n2;
int risposta;
} Form;
//definisco il buffer condiviso tra threads del proc server come array di record (tabella)
typedef struct{
Form f[2]; //in realtà basterebbe 3 perché i 3 client fanno recieve bloccanti prima di fare nuove send
int spazio_disp;
int mess_disp;
int dim; //dimensione totale del buffer, mi serve per l'incremento circolare di testa e coda
int testa;
int coda;
pthread_cond_t MESS_DISP;
pthread_cond_t SPAZIO_DISP;
pthread_mutex_t mutex;
}Buffer;
//definisco le funzioni client
void compila_form(Form,int,int); //funzione invocata dai processi Client per compilare la struct form
void consuma(Form*,int); //funzione invocata dai processi Client per consumare il risultato dell'elaborazione
//definisco le funzioni server
void* controlla(void*); //funzione invocata dal Thread Manager del Processo Server
void* elabora(void*); //funzione invocata dai 2 Threads Worker del Processo Server
void init_buffer(Buffer*); //inizalizza il buffer
void remove_buffer(Buffer*); //rimuove il buffer
client.cpp:
codice:
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/time.h>
#include "client-server.h"
using namespace std;
//definisco le funzioni client
void compila_form(Form f,int request_id, int answer_id){//funzione invocata dai processi Client per compilare la struct form
srand(time(NULL));
f.pid = getpid();
f.n1 = rand()%11;
f.n2 = rand()%11;
cout<<"[CLIENT]: valori compilati: "<<f.pid<<" "<<f.n1<<" "<<f.n2<<endl;
msgsnd(request_id,&f,sizeof(Form)-sizeof(long),0);
cout<<"xd1\n";
consuma(&f,answer_id);
}
void consuma(Form* f, int answer_id){ //funzione invocata dai processi Client per consumare il risultato dell'elaborazione
msgrcv(answer_id,f,sizeof(Form)-sizeof(long),getpid(),0); //recieve bloccante (così non può fare altre send) sul tipo getpid();
cout<<"[CLIENT]:Il valore Consumato dal client di pid "<<getpid()<<" è: "<<f->risposta<<endl;
}
int main(){
//PARTE DICHIARATIVA
key_t request_key , answer_key;
int request_id, answer_id;
Form f;
//PARTE ESECUTIVA
//genero le chiavi per le due code di messaggio
request_key = ftok(REQUEST_QUEUE_PATH,REQUEST_QUEUE_CHAR);
answer_key = ftok(ANSWER_QUEUE_PATH,ANSWER_QUEUE_CHAR);
//istanzio le due code di messaggio
request_id = msgget(request_key,IPC_CREAT|0664);
answer_id = msgget(answer_key,IPC_CREAT|0664);
//Esecuzione del cliente ripetuta 5 volte come indicato dalla traccia
for(int i = 0; i<5 ; i++) {
compila_form(f,request_id,answer_id);
}
return 0;
}
server.cpp :
codice:
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/time.h>
#include <errno.h>
#include <pthread.h>
#include "client-server.h"
using namespace std;
//definisco le funzioni server
void* controlla(void* buff){ //funzione invocata dal Thread Manager del Processo Server (Produttore dalla testa)
Buffer* b = (Buffer*)buff;
Form form;
msgrcv(request_id,&form,sizeof(Form)-sizeof(long),0,IPC_NOWAIT);//se va a buon fine ritorna il numero di byte della risposta >0
//è 0 perchè prende il primo mess che trova ed è IPC_NOWAIT
//perché non è una reicieve bloccante
if(form.n1 == -1 && form.n2 == -1) { //messaggio speciale di uccisione dei workers e disallocazione
cout<<"[MANAGER]:Ricevuto Messaggio Speciale\n";
cout<<"[MANAGER]:Terminazione dei Workes\n";
for(int i=0;i<2;i++){
pthread_cancel(threads[i]);
}
cout<<"[MANAGER]:Disalloco il buffer e le strutture allocate\n";
remove_buffer(b);
free(b);
msgctl(answer_id,IPC_RMID,0);
msgctl(request_id,IPC_RMID,0);
exit(0);
}
else{ //messaggio normale da mandare nel buffer per poi farlo elaborare dai workers
pthread_mutex_lock(&b->mutex);
while( b->spazio_disp == 0)
pthread_cond_wait(&b->SPAZIO_DISP,&b->mutex);
cout<<"[MANAGER]:Copia nel buffer di un msg ricevuto da un client\n";
b->f[b->testa]=form; //copio il form nella testa dell'array di form
b->testa= (b->testa+1)%(b->dim); //incremento circolare della testa
b->spazio_disp--;
b->mess_disp++;
pthread_cond_signal(&b->MESS_DISP);
pthread_mutex_unlock(&b->mutex);
}
sleep(1);
}
void init_buffer(Buffer* b){
b->spazio_disp = 2;
b->mess_disp = 0;
b->testa = 0;
b->coda = 0;
b->dim=2;
pthread_cond_init(&(b->MESS_DISP),NULL);
pthread_cond_init(&(b->SPAZIO_DISP),NULL);
pthread_mutex_init(&(b->mutex),NULL);
printf("[SERVER] - Buffer inizializzato...\n");
}
void remove_buffer(Buffer* b){
pthread_cond_destroy(&(b->MESS_DISP));
pthread_cond_destroy(&(b->SPAZIO_DISP));
pthread_mutex_destroy(&(b->mutex));
printf("[SERVER] - Buffer distrutto...\n");
}
void* elabora(void* buff){ //funzione invocata dai 2 Threads Worker del Processo Server (Consumatore dalla coda )
Buffer* b = (Buffer*)buff;
Form* form;
pthread_mutex_lock(&b->mutex);
while( b->mess_disp == 0)
pthread_cond_wait(&b->MESS_DISP,&b->mutex);
*form = b->f[b->coda]; //carico in form l'istanza di coda del buffer
form->risposta = form->n1 * form->n2;
cout<<"[WORKER]: Valore elaborato :"<<form->risposta<<endl;
msgsnd(answer_id,&form,sizeof(Form)-sizeof(long),IPC_NOWAIT); //il tipo di messaggio è già specificato grazie alla getpid() fatta dal client
b->coda= (b->coda+1)%(b->dim); //incremento circolare della coda
b->spazio_disp++;
b->mess_disp--;
pthread_cond_signal(&b->SPAZIO_DISP);
pthread_mutex_unlock(&b->mutex);
}
int main(){
//PARTE DICHIARATIVA
key_t request_key , answer_key;
int request_id, answer_id;
pthread_attr_t attr;
Form* f;
Buffer* b = (Buffer*)malloc(sizeof(Buffer));
//PARTE ESECUTIVA
//genero le chiavi per le due code di messaggio
request_key = ftok(REQUEST_QUEUE_PATH,REQUEST_QUEUE_CHAR);
answer_key = ftok(ANSWER_QUEUE_PATH,ANSWER_QUEUE_CHAR);
//istanzio le due code di messaggio
request_id = msgget(request_key,IPC_CREAT|0664);
answer_id = msgget(answer_key,IPC_CREAT|0664);
//setto l'attr come DETACHED,il contrario di joinable ,ossia non è possibile rilevare la teminazione dei thread (che avviene con la cancel)
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
//inizializzo il buffer
init_buffer(b);
//creo i due threads workers
for(int i=0;i<2;i++){
cout<<"[SERVER]:Istanziamento Workers\n";
pthread_create(&threads[i],&attr,elabora,(void*)b);
}
//creo il thread Manager
cout<<"[SERVER]:Istanziamento Manager\n";
pthread_create(&threads[2],&attr,controlla,(void*)b);
//non rilevo la fine dei thread in quanto gestita tramite un mess speciale dal thread Manager, termina il Manager e disalloco il resto
cout<<"[SERVER]: Terminazione thread Manager\n";
pthread_attr_destroy(&attr);
pthread_exit(NULL);
//fine del server
return 0;
}
master.cpp:
codice:
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/time.h>
#include "client-server.h"
using namespace std;
int main(){
//PARTE DICHIARATIVA
pid_t pid;
key_t request_key , answer_key;
int request_id, answer_id;
int stato;
int num_client,num_server,num_proc;
//PARTE ESECUTIVA
//inizializzo num_client,numserver e numero di processi
num_client = 3;
num_server = 1;
num_proc = num_client+num_server;
//genero le chiavi per le due code di messaggio
request_key = ftok(REQUEST_QUEUE_PATH,REQUEST_QUEUE_CHAR);
answer_key = ftok(ANSWER_QUEUE_PATH,ANSWER_QUEUE_CHAR);
//istanzio le due code di messaggio
request_id = msgget(request_key,IPC_CREAT|0664);
answer_id = msgget(answer_key,IPC_CREAT|0664);
//Genero client e server come processi figli con fork ed exec
for(int i=0;i<num_proc;i++){
pid=fork();
if(pid==0){
if(i == num_client+num_server-1){ //processo server
cout<<"[MASTER]:Istanzio il Server con pid "<<getpid()<<endl;
execl("./server","./server",NULL);
exit(0);
}
else{ //processi client
cout<<"[MASTER]:Istanzio il Client con pid "<<getpid()<<endl;
execl("./client","./client",NULL);
exit(0);
}
}
}
//rilevo la terminazione dei client
for(int i=0;i<num_client;i++){
pid=wait(&stato);
if(pid==-1)
cout<<"[MASTER]:Errore terminazione client\n";
else
cout<<"[MASTER]:Terminato con successo il Client con pid "<<pid<<endl;
}
//genero ed invio il form speciale per la terminazione del server da inviare al manager
Form terminazione;
terminazione.pid = getpid();
terminazione.n1 = -1;
terminazione.n2 = -1;
msgsnd(request_id,&terminazione,sizeof(Form)-sizeof(long),0);
//attendo la temrinazione del processo server
pid=wait(&stato);
cout<<"[MASTER]:Terminato con successo il Server con pid "<<pid<<endl;
//Fine
cout<<"[MASTER]:Fine\n";
return 0;
}
Makefile:
codice:
all: master client server
main: master.cpp
g++ -o master master.cpp
client: client.cpp
g++ -o client client.cpp
server: server.cpp
g++ -o server server.cpp -pthread
clean:
rm -f master client server
rm -f *.o
La compilazione avviene senza alcun errore o warning.
Ma l'esecuzione si blocca a questo punto qui : https://ibb.co/hZ5xSw
Ho individuato già le righe critiche :
codice:
b->f[b->testa]=form; //copio il form nella testa dell'array di form
b->testa= (b->testa+1)%(b->dim); //incremento circolare della testa
b->spazio_disp--;
b->mess_disp++;
in server.cpp , perché , come si vede dall'esecuzione, è da questo punto in poi che il programma non prosegue più.
Sapreste aiutarmi ? Grazie mille in anticipo a tutti coloro che proveranno ad aiutarmi