PROGETTO DI LABORATORIO DI SISTEMI OPERATIVI A.A. 2004/2005 $Id: README,v 1.28 2005/01/25 14:55:18 eim Exp $ 0. Indice --------- 0. Indice ................................................................... 1. Informazioni sulla documentazione ........................................ 2. Il gruppo di lavoro ...................................................... 3. Introduzione ............................................................. 3.1. Funzionalita' del programma .......................................... 3.2. Problemi riscontrati .................................................. 4. Compilazione ed esecuzione del programma ................................. 4.1. Makefile ............................................................. 4.1.1. Come si comporta il Makefile ..................................... 4.2. Compilazione manuale ................................................. 4.3. Esecuzione manuale ................................................... 4.4. Il file di configurazione ............................................ 4.5. Il file di input ..................................................... 4.6. Il file di output ..................................................... 5. Il codice di progetto.c .................................................. 5.1. Le funzioni .......................................................... 5.2. Le #define ........................................................... 5.3. main() ............................................................... 5.4. ParseRC() ............................................................ 5.4.1. Problema Apertura File ........................................... 5.4.2. #define keyword del file di configurazione ....................... 5.5. IsWord() ............................................................. 5.6. ParserInputFile() .................................................... 5.6.1. #define keyword del file di input ................................ 5.7. IsDataInputFile() .................................................... 5.8. CeckLine() ........................................................... 5.9. CalcProcessNumber() .................................................. 6. Il codice di client.c .................................................... 6.1. Le funzioni .......................................................... 6.2. Le #define ........................................................... 6.3. main() ............................................................... 6.4. product() ............................................................ 6.5. StringToInt() ........................................................ 7. I sorgenti del progetto .................................................. 7.1. Il sorgente di progetto.c ............................................ 7.2. Il sorgente di client.c .............................................. 7.3. Il sorgente del Makefile ............................................. 8. Il file di ChangeLog ..................................................... 1. Informazioni sulla documentazione ------------------------------------ Questo documentazione (file README), come anche l'intero progetto sviluppato, e' sotto controllo CVS. Ecco una serie di relative informazioni: Attuale revisione CVS: $Revision: 1.28 $ Ultima modifica da parte di: $Author: eim $ Ultima data di modifica: $Date: 2005/01/25 14:55:18 $ E' stato scelto di scrivere questa documentazione in puro file di testo, formattato DOS e Unix in modo da poter gestire il processo di editing al meglio utilizzando il sistema CVS. La documentazione cerca di preservare la formattazione di riga ad 80 caratteri in modo da essere consultabile su ogni terminale Unix. 2. Il gruppo di lavoro ---------------------- Il gruppo di lavoro di questo progetto e' composto da 4 membri. +-----------+----------+-----------+--------------------------+ | Nome | Cognome | Matricola | E-Mail | +-----------+----------+-----------+--------------------------+ | Ivo | Marino | 998423 | eim@mentors.debian.net | +-----------+----------+-----------+--------------------------+ | Marco | Marri | 694618 | marcomarri.uni@libero.it | +-----------+----------+-----------+--------------------------+ | Matteo | La Bella | 694189 | matteo.unive@flashnet.it | +-----------+----------+-----------+--------------------------+ | Francesco | Lomonaco | 688649 | chicco79@libero.it | +-----------+----------+-----------+--------------------------+ 3. Introduzione --------------- Il progetto e' sviluppato in linguaggio ANSI C in ambiente Linux. Il progetto e' composto da i seguenti file: . progetto.c Sorgente ANSI C del programma "server". . client.c Sorgente ANSI C del programma "client". . progettorc File di cofigurazione di default. . input File di input di default. Le funzioni di ogni singolo file saranno spiegate nella documentazione. 3.1. Funzionalita' del programma -------------------------------- Il programma "progetto" esaminera' periodicamente il contenuto della directory passatagli attraverso il file di configurazione al momento dell'esecuzione del programma. L'operazione che svolgera' il programma sara' quella di effettuare moltiplicazioni fra matrici e vettori tramite l'esecuzione di uno o piu' processi, rimuovendo il file di input e depositando il risultato in una seconda directory, anch'essa specificata nel file di configurazione. Successivamente il file di input sara' cancellato, e saranno svolte tutte le attivita' del programma. Inoltre il compito del server e' quello di gestire dinamicamente i processi, in base ai processi stabiliti nel file di configurazione in base alla grandezza della matrice o del vettore. Il server non potra' mai utilizzare un numero di processi maggiori a quelli specificati nel file di configurazione. I processi calcolatori e il programma utilizzeranno lo stesso buffer. Proprio per questo motivo e' indispensabile utilizzare memoria condivisa e semafori. 3.2. Problemi riscontrati ------------------------ Alcuni problemi riscontrati nella fase di progettazione sono stati: . Apertura del file di configurazione; . Apertura del file di input; . Esaminare il file di configurazione, input; . Funzioni fork(), exec(); . Semafori; . Condivisione memoria tra due programmi (server, client); Le problematiche sono state affrontate utilizzado la tecnica di sviluppo e progettazione top-down. 4. Compilazione ed esecuzione del programma ------------------------------------------- I due sorgenti che compongono il progetto possono essere compilati in diverse modalita': tramite Makefile (make) o manualmente. Il sistema tramite file make puo' essere considerato anche una metologia di installazione del progetto. 4.1. Makefile ------------- Per compilare ed eseguire il programma e' possibile utilizzare il file Makefile con il comando make . Il Makefile si occupera' di compilare i sorgenti e di copiarli, assime al file di configurazione e al file di input all'interno della cartella /tmp. % make compile run dove: compile: Compila i sorgenti. (Maggiori dettagli vedere la prossima sezione). run: esegue il programma server chiamato progetto con dei valori di default --- ATTENZIONE ------------------------------------------------------------- Nel caso si esegue un run con i valori di default, il file di input dopo la prima esecuzione sarà cancellato. Per ripristinare il valore di default riutilizzare il comando make compile, oppure utilizzare il programma in modo personale. ---------------------------------------------------------------------------- 4.1.1. Come si comporta il Makefile ----------------------------------- Il compito del Makefile e' quello di aiutarvi nella procedura di compilazione e di esecuzione. Come gia' detto in precedenza, il make compilera' e mandera' in esecuzione tramite make compile run. Analizziamo in specifico cosa fa: . Compila: Maggiori informazioni consultare il paragrafo 4.2. . Salva il file progetto.c compilato nella directory /tmp con il nome progetto. . Salva il file client.c compilato nella directory /tmp con il nome client. . Copia il file di input di default nella directory /tmp/input con il nome input.txt. Il Makefile puo essere usato come installatore del progetto, come mostrato un semplice "make" nella cartella sorgente del progetto si preoccupera' a configurare la cartella di input, output e di copiare il file di input nella giusta locazione. La destinazione dell'installazione tramite Makefile e' la cartella /tmp in quanto questa ci garantisce, come standard Unix, un'accesso di scrittura da parte di ogni utente, anche non-root, del sistema. 4.2. Compilazione manuale ------------------------- Se si preferisce una compilazione manuale, qui di seguito sono riportate le varie istruzioni da eseguire. Il programma utilizza come linguaggio di programmazione ANSI C. Per la compilazione di tale formato e' necessario un compilatore ANSI C. In ambiente UNIX e' possibile utilizzare il compilatore gcc Eseguire da riga di comando di shell: % gcc -pedantic -g -Wall -o /tmp/progetto progetto.c % gcc -pedantic -g -Wall -o /tmp/client client.c Le opzioni -pedantic, -g, -Wall, sono specifiche istruzioni per il compilatore gcc che garantiscono una compilazione piu' "severa" del codice. 4.3. Esecuzione del programma ---------------------------- Il programma dovrà essere eseguito dalla CLI (Command Line Interface). Per avviare il programma in modo corretto utilizzare la sintassi % /tmp/progetto dove: Specifica il file di configurazione. Maggiori informazioni sul file di configurazione consultare il paragrafo 4.4. Maggiori informazioni sul file di input consultare il paragrafo 4.5. E' disponibile anche un help del programma: % /tmp/progetto -h Oppure: % /tmp/progetto --help 4.4. Il file di configurazione --------------------------- Il file di configurazione dovra' essere argomento di comando al momento dell'esecuzione del programma e dovra' essere formattato in questo modo, altrimenti il programma generera' un errore: Esempio di un file di configurazione valido: # # Output # /tmp/input # # Input # /tmp/output # # Processi # 4 # # DimMinExecPar # # Dimensioni Minime per Esecuzione Parallela # 100 Il simbolo # antepone un commento, oppure una keyword. Le keyword accettate dal file di configurazione sono: . Input Percorso dove si trovera' la matrice, il vettore, e la dimensione dell'array. . Output Percorso dove il programma scrivera' il risultato della esecuzione. . Processi Numero dei processi massimi che che si dovranno utilizzare per compiere le operazioni di calcolo. . DimMinExecPar Dimensione minima per l'esecuzione in parallelo (devo essere presenti nella keyword almeno 2 processi). Subito dopo ogni keyword deve essere presente il valore che deve assumere la keyword. Salvare il file di configurazione con qualsiasi nome e in formato ASCII 4.5. Il File di input --------------------- Anche il file di input necessita' di alcune raccomandazioni, e dovra' essere formattato tenendo conto di una precisa sintassi. Ecco un esempio di un file di configurazione valido: # # DimArray # 4 5 # # Matrice # 10 11 12 13 14 20 21 22 23 24 30 31 32 33 34 40 41 42 43 44 # # Vettore # 50 51 52 53 54 Il carattere # antepone un commento, oppure un comando (o keyword). I comandi accettati dal file di configurazione sono: . DimmArray Dimensione dell'array nel formato RIGHE COLONNE. . Matrice Scrivere i valori della matrice. . Vettore Vettore che sara' moltiplicato alla matrice. Subito dopo ogni keyword devono essere presenti i dati relativi alla keyword. Il file di configurazione deve essere formattato DOS o Unix. 4.6. Il file di output ---------------------- Il server (progetto.c) raccogliera' i risultati forniti da parte dei vari client dopo la loro esecuzione e li scrive in un file di output per riprendere poi nuovamente il ciclo di scansione della cartella di input. Il file di output che il server scrivera' e' il seguente: /tmp/output/output.txt. Contiene una una sola le riga (come vettore) i relativi risultati dei processi client eseguiti. 5. Il codice di progetto.c -------------------------- Il programma chiamato server ha il compito di: . Aprire il file di configurazione e di input. . Controllare il file di configurazione e di input. . Prelevare le keyword con relativi valori. . Controllare periodicamente la cartella contenente il file di configurazione. . Passa la matrice e il vettore a client.c. 5.1. Le funzioni --------------- Nelle prossime sezioni sara' data una descrizione dei compiti svolti dalle varie funzioni. Inoltre nella sezione 7 e' possibile consultare il sorgente ANSI C. 5.2. Le #define --------------- Nel codice sono presenti alcune DEFINE estrane al sorgente C. Il progetto, con la relativa documentazione, essendo stato sviluppato in gruppo si e' cercato un modo per lavorare tutti insieme su file aggiornati, in diverso tempo e in diversi luoghi. Per questo si e' deciso di utilizzare la tecnica del CVS, che permette di avere sempre i file aggiornati all'ultima versione e controllare tutte le modifiche apportate da un componente di un gruppo. Maggiori informazioni su CVS: http://www.gnu.org/software/cvs/ 5.3. main() ----------- Il compito del main e' quello di: 1) Leggere i vari argomenti passati al momento dell'esecuzione del programma 1. Se non dovesse essere presente nessun argomento, il main() aprira' un file, in sola lettura, di default definito nelle #define RCFILE. Nel nostro progetto e' stata assegnato come valore: #define RCFILE progettorc 2. Se e' stato specificato nella riga di comando, un file di configurazione, il main() aprira' il file, in sola lettura. I tutti e due i casi il main() provvederà a chiamare la funzione parserRC per controllare se il file di configurazione e' corretto. 2) Leggere periodicamente la directory dove è salvato il file di input per compiere, se necessario nuove operazioni. Una volta letto il file di input, passato alla prima esecuzione dal file di configurazione, il main() del server ha il compito di eliminare il file di input, dopo aver estrapolato tutte le informazioni, e controllare periodicamente la presenza di un nuovo file di input. Per questo è stata utilizzata la funzione sleep() che sospende il processo corrente per n secondi o fino all'arrivo di un segnale SIGALRM. Nel nostro progetto non è stato considerato nessun segnale SIGALRM o di stop, in quanto il server deve controllare la directory ciclicamente. 3) Mettere in condivisione la memoria tra il programma server e client, cioe' viene allocata della SHARED MEMORY (memoria in condivisione). In che maniera opera la shared memory? Quando viene creato una shared memory il sistema identifica quella porzione di memory con una numero chiamato KEY. Un processo per accedere alla memoria deve "allacciare" la KEY al proprio spazio di reindirizzamento, e la "slacciera'" quando non avrà più bisogno dell'utilizzo della memoria. Per ottenere questo risultato dobbiamo prima allocare memoria tramite la funzione: shmget (key_t key, int size, int flag) dove: key: identifica la key della memoria condivisa in maniera univoca size: specifica in byte la dimensione della memoria condivisa flag: e' un numero intero che indica la modalità di accesso alla memoria condivisa esempio read only, write only, read/write, inoltre puo' essere aggiunto anche IPC_CREAT che si indica che si vuole chiamare l'area di memoria, con lo stesso nome del numero di KEY. Se l'area di memoria è stata creata il kernel del SO ci restituisce 1 e a questo punto bisogna allacciare la memoria, tramite la funzione shmat(int ds_shm, void *addr, int flag) dove: ds_shm: numero di KEY precedentemente assegnata *addr: e' l'indirizzo dove allacciare la memoria condivisa, se viene specificato un valore numero 0, allora il kernel automaticamente determinera' l'indirizzo. flag: in che modo il processo può accedere all'area condivisa: read only, write only, read/write. Perche' una memoria condivisa? Il server svolge molteplici funzioni, ma non svolge nessuna operazione di calcolo tra vettore e matrici. Il compito di svolgere tale operazione e' affidata al programma client. Nella Memoria Condivisa (SHM) passiamo una riga per volta della matrice ai vari processi client (chiamati via fork ed exec). Inoltre la SHM servira' anche per ritornare il risultato elaborato dal client al server. Il server scrivera' questa risultato sul file di output. 4) Gestire i semafori, che hanno il compito di far eseguire un solo processo alla volta nelle sezioni chiamate "sezioni critiche". E' importante stabilire delle priorita' ai processi in quanto nelle sezioni critiche alcuni processi eseguono ad esempio operazioni sulla stessa memoria condivisa. Un semaforo non e'altro che una variabile intera contenente un valore non negativo cui si puo' accedere solo tramite due operazioni denominate WAIT e SIGNAL. I semafori all'interno del progetto sono stati pensati in questo modo: SEMAFORI (0 = verde, 1 = rosso) A: 0 condizione di accesso al buffer per qualsiasi processo. B: 0 condizione di esecuzione per il server. C: 0 condizione di esecuzione per il client. SE (B == 0) ALLORA { SE (A == 0) ALLORA { A = 1 Inserisci riga A = 0 } B = 1 (il server deve attendere che un client setti B a 0) } Per creare il semaforo all'interno del sorgente abbiamo utilizzato la funzione: semget(key_t key, int number, int flag) dove: key: identifica l'array di semafori nel sistema. E' scelta dal programmatore e nel nostro caso e' definita nelle #define SEMKEY number: e' il numero di semafory che si vuol gestire flag: permette di specificare alcune modalita' di creazione dei semafori La funzione restituisce un numero non negativo che identifica l'array all'interno del sistema. Tale numero e' chiamato: IPC descriptor Una volta inizializzato l'array del semaforo ora bisogna gestire i vari segnali dei semafori, in questa maniera ogni processo può lavorare senza creare problemi con altri processi, in base ai "segnali" definiti dai semafori. Per svolgere le operazioni si utilizza la funzione: semctl(int sem_des, int sem_num, int cmd, union senum_arg) dove: sem_des: Identifica l'array dei semafori da utilizzare sem_num: Identifica il numero del semaforo da utilizzare cmd: il tipo del comando senum_arg: l'argomento che prendera' il comando. tutti i valori che devono assumere gli argomenti delle varie funzioni specificate sopra sono impostate nelle #define. 5) Crea dal padre il relativo figlio tramite fork(). Ora che la memoria e' correttamente allacciata, e sono impostati i semafori, dobbiamo iniziare a creare i vari processi che compiranno i calcoli tra vettore e matrice. Allora iniziamo a dire che unix assegna ad ogni processo un PID, un numero che identifica il processo. La funzione fork() ha il compito di generare un figlio identico al programma che ha generato il figlio, cioè il padre. Perche' tutto questo? Proprio perche' piu' processi devo eseguire i calcoli e quindi dobbiamo creare vari server che inviano le informazioni al client, cioe' al programma che deve eseguire i vari calcoli 6) Chiamata al programma di calcolo chiamato client.c. La chiamata viene effettuata tramite la funzione: execl (CLIENT_EXEC_BIN, "client", SharedMemoryID, SemaphoreID, colonne, LifeCycleArg); dove: CLIENT_EXEC_BIN: directory dove si trova il programma "client": valore impostato dal programmatore per eseguire il programma client SharedMemoryID: Identifica la memoria codivisa per effettuare i vari calcoli SemaphoreID: Identificano ID dei Semafori creati precedentemente colonne: Identificano le colonne delle varie matrici LifeCycleArg: Cicli di vita di client 7) Slacciare la sharedmemory. Una volta terminate tutte le esecuzioni di client il programma effettuerà lo slaccio della memoria condivisa e libererò tale memoria. 5.4. ParseRC() -------------- Questa funzione esegue il parsing del file di configurazione del progetto. Questo file e' solitamente nominato "progettorc" ma puo anche essere specificato come argomento del programma "progetto". La funzione ParseRC() fa uso di altre due funzioni chiamate isWord() e isData() per estrapolare correttamente le informaioni assocciate ad una specifica keyword. La funzione e' in grado di evitare. La funzione scorre tutto il file passatogli dal main() fino a che non trova un carattere '\n'. A questo punto il programma controlla se la parola che ha trovato e' effettivamente una parola. La funzione che si occupa di questo procedimento e' la ISWORD(). Se ISWORD() accerta che la stringa passata e' una parola, allora può essere una possibile keyword. Confronta la parola trovata con le varie keyword, assegnate nelle #define. Se la parola trovata e' effettivamente una keyword allora assegno alla variabile keyend la fine della parola. Una volta trovate le varie keyword, il compito di trovare i valori è assegnato a ISDATA(). 5.4.1 Problema apertura file ----------------------------- Visto che le specifiche del progetto di imponeva di NON utilizzare la fopen abbiamo dovuto utilizzare delle chiamate di sistema read() del File Descriptor. Inoltre FD non ci permetteva di utilizzare nessun puntatore al carattere del file aperto, cio' era impossibile utilizzare la funzione getc(), quindi abbiamo dovuto, aprire tutto il file e scorrerlo carattere per caratattere da parseRC() 5.4.2 #define keyword del file di configurazione ------------------------------------------------ #define N_KEYWORDS 5 #define KEYWORD_INPUT "Input" #define KEYWORD_OUTPUT "Output" #define KEYWORD_PROCESSI "Processi" #define KEYWORD_DIM_MIN_EXEC_PAR "DimMinExecPar" 5.5 IsWord() ------------ Questa funzione ha il compito di accertare che la stringa trovata in parseRC() sia effettivamente una parola. Le parole o possibili keyword sono composti solo da caratteri alfabetici (A-Z), quindi se solo 1 carattere non dovesse essere alfabetico allora questa stringa trovata non e' una possibile keyword. In questa funzione viene eseguito il controllo carattere per carattere, tramite la funzione che si trova in string.h, ISALPHA, che controlla effettivamente se e' un carattere alfabetico. Se e' un carattere alfabetico allora si incrementa un contatore. Se il contatore e' uguale alla lunghezza della stringa passatagli, allora la parola trovata puo' essere effettivamente una possibile keyword. 5.6 ParserInputFile() --------------------- La funzione esegue esattamante il compito della ParserRC() con l'unica accortezza che le keyword in questa funzione sono diverse. Anche in questo caso le keyword sono definite nelle #define. 5.6.1. Le #define ----------------- #define INPUTFILE_KEYWORD_DIM_ARRAY "DimArray" #define INPUTFILE_KEYWORD_MATRIX "Matrice" #define INPUTFILE_KEYWORD_ARRAY "Vettore" Maggiori informazioni su ParserInputFile si rimada al punto 2.3 5.7 IsDataInputFile() --------------------- IsDataInputFile ha lo stesso compito, gia precedentemente esaminato in IsData, con l'unico accorgimento che ogni riga della matrice per essere memorizzata per poi effettuare i dovuti calcoli e' stata immagazzinata in una struttura dati. Una volta presa la riga della matrice, viene chiamata la funzione Ceckline() 5.8 Ceckline() -------------- Il compito della ceckline e' quello di controllare se ogni riga della matrice contiene esattamente il numero delle colonne specificato all'interno del file di input. 5.9. CalcProcessNumber() ------------------------ La funzione CalcProcessNumber svolge la funzione di calcolo per ottimizzare i processi nel caso si debbano eseguire in parallelo le operazioni tra matrice e vettore. Come è stato specificato nelle direttive del progetto: ... qualora la dimensione della matrice (vettore) sia inferiore ad un valore prefissato (indicato nel file di configurazione), il calcolo verrà effettuato da un solo processo. Nel caso la dimensione sia compresa tra il valore indicato ed il suo doppio, verranno usati due processi, e così via, fino al massimo indicato nel file di configurazione... 6. Il codice di client.c ------------------------ Il codice client.c ha il compito di eseguire le varie operazioni tra la matrice e l'array. Naturalmente come visto per server.c daremo una spiegazione per ogni funzione. 6.1. Le #define --------------- Nel codice sono presenti alcune DEFINE estrane al sorgente C. Il progetto, con la relativa documentazione, essendo stato sviluppato in gruppo si e' cercato un modo per lavorare tutti insieme su file aggiornati, in diverso tempo e in diversi luoghi. Per questo si e' deciso di utilizzare la tecnica del CVS, che permette di avere sempre i file aggiornati all'ultima versione e controllare tutte le modifiche apportate da un componente di un gruppo. Maggiori informazioni su CVS: http://www.gnu.org/software/cvs/ 6.2. main() ----------- Il main() riceve come argomenti, gli argomenti passati con la funzione execl dal programma server. Oltre ad eseguire le funzioni qui sotto riportate, gestisce i semafori per il server in questo modo: SEMAFORI (0 = verde, 1 = rosso) A: 0 condizione di accesso al buffer x qualsiasi processo B: 0 condizione di esecuzione x il server C: 0 condizione di esecuzione per il client CODICE SERVER SE (B=0) ALLORA { SE (A=0) ALLORA { A=1 inserisci riga A=0 } B=1 (il server deve attendere che un client setti B=0) } CODICE CLIENT SE (C=0) ALLORA { C=1 (nessun altro client può eseguirsi) SE (B=1) ALLORA { SE (A=0) ALLORA { A=1 prendi riga e svuota buffer A=0 } B=0 (il server può eseguirsi) } C=0 (un altro client può partire ma si fermerà al controllo if(B=1), finchè il server non avrà messo qualcosa nel buffer) } 6.3. Product() -------------- Calcola il prodotto tra la riga della matrice interessata e il vettore, richiamando la funzione StringToInt per convertire le due stringhe in array di interi. 6.4. StringToInt() ------------------ Converte la stringa rappresentante la riga della matrice o il vettore in un array di interi. 7. I sorgenti del progetto -------------------------- In questa sezione sono riportati interamente il codice sorgente dei due file sorgente che compongono il progetto: . progetto.c (Server) . client.c (Client) 7.1. Il sorgente di progetto.c ------------------------------ /* {{{ COMMENT */ /* * CVS id: $Id: README,v 1.28 2005/01/25 14:55:18 eim Exp $ * * Corso: Laboratorio di sistemi operativi * * Titolo: "Progetto laboratorio di sistemi operativi" * * Informazioni * generali: Informazioni generali sul progetto. * TODO: Completa questa sezione... * * Ambiente Il seguente sorgente e' stato sviluppato e compilato * di sviluppo: sulle seguenti architetture hardware ed ambienti: * * Sistema operativo Architettura * -------------------------------------------- * Ubuntu GNU/Linux 4.10 (Warty) x86 * Ubuntu GNU/Linux 4.10 (Warty) PowerPC * Windows XP/Microsoft x86 * -------------------------------------------- * * Compilazione: gcc -pedantic -g -Wall -o progetto progetto.c * * Potete compilare semplicemente eseguendo "make". * * NON CI DEVONO MAI ESSERE WARNING E/O ERRORI NEL CODICE. * * Limit funzioni * da poter usare: Quote da WIKI (Domande ricorrenti): * * "Quali funzioni si possono usare e quali no per lo * sviluppo del progetto?" * * Risposta: In generale, si possono usare tutte le * funzioni del Kernel e delle librerie standard, eccetto * nella gestione dei file su disco, dove possono essere * utilizzate solo le funzioni del Kernel. Esempi: si a * atoi, sprintf, sscanf, sleep, strcat, strcmp, no a * fopen, fclose, fread, fwrite. * * Coding style: . I tab devono corrispondere ad 8 spazi. * . Usare solamente commenti compatibili con ISO C90. * . Ogni funzione deve essere commentata a dovere. * . Rispettare l'identazione del sorgente. * . Usate un editor di testo decente come vim o emacs. * * SEGUIRE SEMPRE QUESTO CODING STYLE PER GARANTIRE LA * MASSIMA LETTURA DEL CODICE -- (Vi[m] spacco le mani! ;) * * Files: Questo programma lavora su due file di tipo testo: * * File Accesso Informazioni file * ---------------------------------------------- * Input Read only File di input (ASCII) * Output Read & write File di output (ASCII) * ---------------------------------------------- * * Non ci sono altri file oltre a quelli descritti. * * Argomenti: Il programma accetta due argomento sulla CLI: * * Index Status Informazioni argomento * ---------------------------------------------- * ARG#1 Richiesto File di input (ASCII) * ARG#2 Opzionale File di output (ASCII) * ---------------------------------------------- * * Argomenti ARGV[>2] vengono prettamente ignorati. * * Limiti: TODO: Elencare eventuali limiti del programma. * * Gestione * degli errori: Segue un esempio di come elencare un'errore: * * ERROR CASE 2 (Basic CLI arguments not given) * Blah, blah, blah, ... * * Strutture dati: TODO: Elencare le strutture dati in uso. * * TODO: Completare tutte le parti del sorgente in cui compare * la stringa "TODO". * * Ulteriori modifiche, blah, blah, blah... */ /* }}} */ /* {{{ DEFINE */ /* * Define global static vars */ #define PROGRAM_NAME "progetto" #define PROGRAM_DESC "Progetto laboratorio Sistemi Operativi I" #define BUG_REPORTS "dsi-so-list@winetea.mine.nu" #define CVS_ID "$Id: README,v 1.28 2005/01/25 14:55:18 eim Exp $" #define CVS_REVISION "$Revision: 1.28 $" #define CVS_DATE "$Date: 2005/01/25 14:55:18 $" #define CVS_AUTHOR "$Author: eim $" #define DEBUG 1 #define ERROR_CASE_1 "Impossibile aprire file di configurazione" #define ERROR_CASE_2 "Errore durante la procedura di parsing del file" #define ERROR_CASE_3 "Impossibile aprire la directory di input" #define ERROR_CASE_4 "Impossibile aprire file di input" #define RCFILE "progettorc" #define INPUTFILE "input.txt" #define WORDSIZE 128 #define BUFSIZE 1024 #define N_KEYWORDS 4 #define KEYWORD_INPUT "Input" #define KEYWORD_OUTPUT "Output" #define KEYWORD_MAX_PROCESSES "MaxProcesses" #define KEYWORD_DIM_MIN_EXEC_PAR "DimMinExecPar" #define SLEEP 1 #define INPUTFILE_N_KEYWORDS 3 #define INPUTFILE_KEYWORD_DIM_ARRAY "DimArray" #define INPUTFILE_KEYWORD_MATRIX "Matrice" #define INPUTFILE_KEYWORD_ARRAY "Vettore" #define SHMKEY ((key_t)7890) /* Chiave shared memory */ #define SHMSIZE 50 /* Grandezza shared memory */ #define SEMKEY ((key_t)7891) /* Chiave semaforo */ #define NSEMS 3 /* Numero semafori in uso */ #define IPC_PERMS 0666 /* Permessi della shared memory */ #define CLIENT_EXEC_BIN "/tmp/client" /* }}} */ /* {{{ INCLUDE */ /* * Include external code and libs */ #include /* Standard I/O */ #include /* General utilities (Ci serve?) */ #include /* Character handling */ #include /* String handling */ #include /* Memory allocation */ #include /* Required for stat() */ #include /* Required for stat() */ #include /* Shared memory */ #include /* Sempahore */ #include /* Wait for children processes */ #include /* Required for read(), stat(), etc */ #include /* Required for open(), etc */ #include /* Directory operations */ /* }}} */ /* {{{ union semun */ /* * Struttura per i semafori (richiesta per alcune operazioni con semctl). * * Author Marco Marri */ union semun { int val; struct semid_ds *buf; ushort *array; } arg; /* }}} */ /* {{{ int isWord() */ /* * int isWord() * * True se char *s e' una parola valida, false se non lo e'. * * Author Marco Marri * Author Ivo Marino */ int IsWord (char *s) { int i = 0; /* Indice di stringa */ int cAlpha = 0; /* Contatore di caratteri alfabetici */ int ReturnValue = 0; for(i = 0; s[i]; i++) if (isalpha(s[i])) /* Se il carattere �alfabetico... */ cAlpha++; /* ...incremento il contatore */ /* Se tutti i caratteri sono alfabetici il contatore avr�lo stesso valore della lunghezza della stringa */ if (cAlpha == strlen(s)) ReturnValue = 1; return ReturnValue; } /* }}} */ /* {{{ int isData() */ /* * int isData() * * Questa funzione viene chiamata da parte di ParseRC() ogni qualvolta sia * necessario analizzare i settori di buffer sottostanti di una data keyword. * * IsData() si occupa ad estrapolare i suddetti settori buffer e ad a inserirli * in apposite strutture dati fornite da parte di ParseRC(). * * Author Marco Marri * Author Ivo Marino * * Param char *string Puntatore al primo charattere di un settore * sottostante di una keyword bel buffer * Param int dim Lunghezza buffer sottostante * Param char *key Puntatore alla keyword da analizzare * Param char *input Puntatore a file di input del main() * Param char *output Puntatore a file di output del main() * Param int *max_processes Puntatore ad int max_processes del main() * Param int *dim_min_exec_par Puntatore ad int dim_min_exec_par del main() * Return bool 1 in caso di successo, 0 altrimenti */ int IsData (char *string, int dim, char *key, char *input, char *output, int *max_processes, int *dim_min_exec_par) { int i = 0; int j = 0; int t = 0; char w[WORDSIZE] = {'\0'}; int ReturnValue = 0; /* {{{ Analizziamo Input */ if (!strcmp(key, "Input")) { /* Forse e' meglio un while? */ for (i=0; i= 1) { /* if (DEBUG) printf ("[DEBUG][IsData]: Output trovato: %s\n", w); */ strcpy (input, w); i = dim; ReturnValue = 1; } } } /* }}} */ /* {{{ Analizziamo Output */ } else if (!strcmp(key, "Output")) { for (i=0; i= 1) { /* if (DEBUG) printf ("[DEBUG][IsData]: Output trovato: %s\n", w); */ strcpy (output, w); i = dim; ReturnValue = 1; } } } /* }}} */ /* {{{ Analizziamo Processi */ } else if (!strcmp(key, "MaxProcesses")) { for (i=0; i * Param int fd File descriptor * Param char *intput Puntatore al file di input del main() * Param char *output Puntatore al file di output del main() * Param int *max_processes Puntatore ad int max_processes del main() * Param int *dim_min_exec_par Puntatore ad int dim_min_exec_par del main() * Return bool 1 in caso di successo, 0 altrimenti */ int ParseRC (int fd, char *input, char *output, int *max_processes, int *dim_min_exec_par) { char buffer[BUFSIZE]; char word[WORDSIZE]; char *keywords[N_KEYWORDS] = {KEYWORD_INPUT, KEYWORD_OUTPUT, KEYWORD_MAX_PROCESSES, KEYWORD_DIM_MIN_EXEC_PAR}; int keyflag[N_KEYWORDS] = { 0 }; int keyend[N_KEYWORDS] = { 0 }; int i = 0; /* Indice buffer */ int j = 0; /* Set inizio riga */ int k = 0; /* Indice per scorrere riga */ int l = 0; /* Set inizio parola */ int m = 0; /* Indice per scorrere parola */ int n = 0; /* Indice per scorrere le keyword */ int eob = 0; /* Indice di EOF nel buffer */ int ReturnValue = 0; if ((read (fd, buffer, BUFSIZE)) > 0) { /* * write() example * * if ((o = write (fd, buffer, i)) < i) { * * printf ("[DEBUG]: i is: %d, o is %d\n", i, o); * } */ if (DEBUG) printf ("\n[DEBUG][ParseRC]: Lunghezza buffer: %d (Informazione non affidabile)\n", strlen(buffer)); for (; i 0) { /* Abbiamo trovato una parola valida... */ word[m] = '\0'; if (IsWord(word)) { /* se nella stringa sono presenti caratteri alfabetici eseguo l'if successivo */ for (n = 0; n < N_KEYWORDS; n++) { /* Scorri le keyword */ /* if (DEBUG) printf ("[DEBUG]: Comparing \"%s\" with \"%s\".\n", word, keywords[n]); */ if (!(keyflag[n])) { /* Se keyword[n] ancora non trovata... */ if (!(strcmp(word, keywords[n]))) { /* Se parola == keyword */ if (DEBUG) printf("[DEBUG][ParseRC]: Trovata keyword (non doppione): \"%s\"\n", keywords[n]); keyflag[n] = 1; /* Segna keyword[n] come trovata */ /* printf ("Mi trovo su indice del buffer %d: %c\n", i, buffer[i+1]); */ keyend[n] = i-1; /* Segna la dove finisce keyword[n] nel buffer */ } } } } m = 0; } } } j = i; } eob = j; } /* Fine scorri buffer */ /* * 0 Processi Percorsi * --------+ --------------+ ---------- */ l = 0; /* Riciclo questo contatore per contare le esecuzioni con successo di isData() */ /* Questo implica l'ordine della dichiarazione delle keywords */ for (i=0; i < N_KEYWORDS; i++) { /* Calcola dalla prima keyword fino all'inizio della seconda... */ if (i == 0) { j = keyend[i]+1; k = keyend[i+1]-strlen(keywords[i+1]); /* Calcola dall'ultima keyword fino alla fine... */ } else if (i == N_KEYWORDS-1) { j = keyend[i]+1; k = eob; /* Calcola le keyword in mezzo ... */ } else { j = keyend[i]+1; k = keyend[i+1]-strlen(keywords[i+1]); } /* * printf ("\n\n[DEBUG]: Keyword: \"%s\" va da %d a %d\n", keywords[i], j, k); * printf ("---------------------------------------------\n"); * * for (l=j; l * Author Ivo Marino * * Param char *s Puntatore al primo carattere della riga della matrice * Param int c Numero di colonne della matrice * Return bool 1 in caso di successo, 0 altrimenti */ int CheckLine(char *s, int c) { int i = 0; int k = 0; int r = 1; int x = 1; /* Esci dal while se 0 */ /* Non rischiamo di cadere in un loop infinito qui? */ while (x) { if (s[i] == '\0') { if (k < c) { if (DEBUG) printf("[DEBUG][CheckLine]: Pochi elementi sulla stessa linea.\n"); r = 0; } x = 0; } else { while (!isdigit(s[i])) i++; if (++k <= c) { while (isdigit(s[i])) i++; } else { if (DEBUG) printf("[DEBUG][CheckLine]: Troppi elementi sulla stessa linea.\n"); r = 0; x = 0; } } } return r; } /* }}} */ /* {{{ int isDataInputFie() */ /* * int isDataInputFile() * * Questa funzione viene chiamata da parte di ParseInputFile() ogni qualvolta * sia necessario analizzare i settori di buffer sottostanti di una data * keyword per un file di input. * * IsDataInputFile() si occupa ad estrapolare i suddetti settori buffer e ad a * inserirli in apposite strutture dati fornite da parte di ParseInputFile(). * * Author Ivo Marino * Author Marco Marri * * Param char *string Puntatore al primo charattere di un settore * sottostante di una keyword bel buffer * Param int dim Lunghezza buffer sottostante * Param char *key Puntatore alla keyword da analizzare * Param int *matrice_righe Puntatore al numero delle righe della matrice del main() * Param int *matrice_colonne Puntatore al numero delle righe della matrice del main() * Param char **matrice Puntatore alla matrice del main() * Param char *vettore Puntatore al vettore del main() * Return bool 1 in caso di successo, 0 altrimenti */ int IsDataInputFile (char *string, int dim, char *key, int *matrice_righe, int *matrice_colonne, char ***matrice, char **vettore) { int i = 0; int j = 0; int r = 0; /* Indice di riga */ int ReturnValue = 1; /* Valore di ritorno, se tutto ok rimane 1, altrimenti viene cambiato in 0 */ char **matrice_temp = (*matrice); /* Solo per una questione di comodita' altrimenti dovrei riportarmi sempre uno * appresso */ char *vettore_temp = (*vettore); /* Vedi sopra */ char w[WORDSIZE] = {'\0'}; /* {{{ Analizziamo DimArray */ if (!strcmp(key, "DimArray")) { for (i=0; i * Param int fd File descriptor * Return bool 1 in caso di successo, 0 altrimenti */ int ParseInputFile (int fd, int *matrice_righe, int *matrice_colonne, char ***matrice, char **vettore) { char buffer[BUFSIZE]; char word[WORDSIZE]; char *keywords[INPUTFILE_N_KEYWORDS] = {INPUTFILE_KEYWORD_DIM_ARRAY, INPUTFILE_KEYWORD_MATRIX, INPUTFILE_KEYWORD_ARRAY}; int keyflag[INPUTFILE_N_KEYWORDS] = { 0 }; int keyend[INPUTFILE_N_KEYWORDS] = { 0 }; int i = 0; /* Indice buffer */ int j = 0; /* Set inizio riga */ int k = 0; /* Indice per scorrere riga */ int l = 0; /* Set inizio parola */ int m = 0; /* Indice per scorrere parola */ int n = 0; /* Indice per scorrere le keyword */ int eob = 0; /* Indice di EOF nel buffer */ int ReturnValue = 0; if ((read (fd, buffer, BUFSIZE)) > 0) { if (DEBUG) printf ("\n[DEBUG][ParseInputFile]: Lunghezza buffer: %d (Informazione non affidabile)\n", strlen(buffer)); for (; i 0) { /* Abbiamo trovato una parola valida... */ word[m] = '\0'; if (IsWord(word)) { /* se nella stringa sono presenti caratteri alfabetici eseguo l'if successivo */ for (n = 0; n < INPUTFILE_N_KEYWORDS; n++) { /* Scorri le keyword */ if (!(keyflag[n])) { /* Se keyword[n] ancora non trovata... */ if (!(strcmp(word, keywords[n]))) { /* Se parola == keyword */ if (DEBUG) printf("[DEBUG][ParseInputFile]: Trovata keyword (non doppione): \"%s\"\n", keywords[n]); keyflag[n] = 1; /* Segna keyword[n] come trovata */ keyend[n] = i-1; /* Segna la dove finisce keyword[n] nel buffer */ } } } } m = 0; } } } j = i; } eob = j; } /* Fine scorri buffer */ l = 0; /* Riciclo questo contatore per contare le esecuzioni con successo di isData() */ /* Questo implica l'ordine della dichiarazione delle keywords */ for (i=0; i < INPUTFILE_N_KEYWORDS; i++) { /* Calcola dalla prima keyword fino all'inizio della seconda... */ if (i == 0) { j = keyend[i]+1; k = keyend[i+1]-strlen(keywords[i+1]); /* Calcola dall'ultima keyword fino alla fine... */ } else if (i == INPUTFILE_N_KEYWORDS-1) { j = keyend[i]+1; k = eob; /* Calcola le keyword in mezzo ... */ } else { j = keyend[i]+1; k = keyend[i+1]-strlen(keywords[i+1]); } if (IsDataInputFile (&buffer[j], k-j, keywords[i], matrice_righe, matrice_colonne, matrice, vettore)) { if (DEBUG) printf ("[DEBUG][ParseInputFile]: Chiamata isDataInputFile() per keyword \"%s\" con successo\n", keywords[i]); l++; /* Incrementa contatore successo isData() */ } } /* Controllo se isDataInputFile() e' stata eseguita correttamente per tutte ke keywords */ /* if (l == INPUTFILE_N_KEYWORDS) */ ReturnValue = 1; } /* Fine read fd */ return (ReturnValue); } /* }}} */ /* {{{ int CalcProcessNumber() */ /* * int CalcProcessNumber() * * Calcola il numero ottimale di processi per eseguire in parallelo le * operazioni di moltiplicazione matrice/vettore. * * Author Ivo Marino * Param dim_min_exec_par Numero minimo di processi paralleli. * Param max_processes Numero massimo di processi paralleli eseguibili. * Param matrice_righe Numero righe nella matrice. * Param matrice_colonne Numero colonne nella matrice. * Return int ReturnValue Numero ottimale di processi. */ int CalcProcessNumber (int dim_min_exec_par, int max_processes, int matrice_righe, int matrice_colonne) { int ReturnValue = 0; /* * dim_min_exec_par Indica il numero massimo di righe che una fork * puo processare. Esempio dim_min_exec_par == 7 * allora ogni con 1 processo calcolo da 1-6 righe. * * max_processes Massimo numero di fork (esecuzioni parallele) * consentito. * * MATRICE A B C * D E F * G H I * * VETTORE X Y Z * * A*X + B*Y + C*Z + D*X + E*Y + F*Z + G*X + H*Y + I*Z * * * Procedura per il calcolo del numero dei processi da eseguire * * if(elementi matrice < 1*min_exec) * 1 processo client * * if(1*min_exec <= elementi matrice < 2*min_exec) * if(2 <= max_exec) * 2 processi client * else * max_exec processi client * * if(2*min_exec <= elementi matrice < 3*min_exec) * if(3 <= max_exec) * 3 processi client * else * max_exec processi client * * if(n*min_exec <= elementi matrice < (n+1)*min_exec) * if((n+1) <= max_exec) * (n+1) processi client * else * max_exec processi client */ /* * ElementiMatrice = matrice_righe * matrice_colonne; * if ((ReturnValue = (int)(ElementiMatrice/dim_min_exec_par)+1) > max_processes) */ /* (righe_matrice/numero_processi + righe_matrice%numero_processi) */ if ((ReturnValue = (int)(matrice_righe/dim_min_exec_par)+1) > max_processes) ReturnValue = max_processes; if (DEBUG) { printf ("[DEBUG][CalcProcessNumber]: Numero minimo righe per esecuzione parallela: %d.\n", dim_min_exec_par); printf ("[DEBUG][CalcProcessNumber]: Numero righe matrice: %d.\n", matrice_righe); printf ("[DEBUG][CalcProcessNumber]: Numero processi necessari: %d.\n", ReturnValue); } return ReturnValue; } /* }}} */ /* {{{ main() */ /* * Welcome to main(), ladies and gents. * * Author Ivo Marino * Author Marco Marri * Return bool 0 in caso di esecuzione corretta */ int main (int argc, char *argv[]) { int fd = -1; /* File descriptor, set to error by default */ char input[WORDSIZE]; /* (RC file) Percorso della cartella di input */ char output[WORDSIZE]; /* (RC file) Percorso della artella di output */ int max_processes = 0; /* (RC file) Numero dei processi */ int dim_min_exec_par = 0; /* (RC file) Dimensione minima esecuzione parallela */ char ComposedInputFile[WORDSIZE]; /* Percorso completo al file di input */ int matrice_righe = 0; /* (Input file) Numero di righe della matrice */ int matrice_colonne = 0; /* (Input file) Numero di colonne della matrice */ char **matrice = NULL; /* (Input File) Matrice */ char *vettore = NULL; /* (Input File) Vettore */ int sleeptime = SLEEP; /* Inizializza variabile per sleep() */ int found = 0; /* Flag che viene posto ad 1 se input.txt trovato */ int x = 0; /* Simple counter */ DIR *dirh = NULL; /* Directory handler */ struct dirent *dirp; /* Directory pointer */ int ProcessNumber = 0; /* Numero ottimale di processi per calcolo matrice */ int shmid = 0; /* Shared memory id */ int semid = 0; /* Semaforo id */ char *a = ""; /* Puntatore alla shared memory */ int i = 0; /* Semplice contatore */ int LifeCycle = 0; /* Ciclo di vita */ int pid = 0; /* Process identification */ char SharedMemoryID[20]; /* Indirizzo SHM sotto forma di stringa */ char SemaphoreID[20]; /* Indirizzo SHM sotto forma di stringa */ char colonne[20]; /* Numero colonne sotto forma di stringa */ char LifeCycleArg[20]; /* Numero enti matrice come stringa */ struct sembuf sb; /* Buffer semaforo */ int ReturnValue = 0; /* Application return value * * -v || -h -1 * Good execution 0 */ if (argc >= 2) { /* Se specificato uno o piu' argomenti */ /* Se -h or --help */ if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) { printf ("Usage: %s [CONFIGRC]\n", PROGRAM_NAME); printf ("%s\n\n", PROGRAM_DESC); printf (" -h --help Output this help screen.\n"); printf (" -v --version Show program version.\n\n"); printf ("Report bugs to: %s.\n", BUG_REPORTS); ReturnValue = -1; /* Se -v or --version */ } else if ((strcmp(argv[1], "-v") == 0) || (strcmp(argv[1], "--version") == 0)) { printf ("%s %s\n", PROGRAM_NAME, CVS_REVISION); printf ("Written by ...n\n\n"); printf ("Copyright (C) 2004-2005 Gruppo Progetto WineTea\n"); printf ("This is academic software; The source code will be freely\n"); printf ("available once the relative university exam has been passed.\n"); ReturnValue = -1; } else { /* Se argomento specificato diverso da "help" o "version" */ if (DEBUG) printf ("[DEBUG][main][%d]: Argomento (File di configurazione) specificato: \"%s\".\n", __LINE__, argv[1]); fd = open (argv[1], O_RDONLY); } } else { /* Se argomento non specificato */ if (DEBUG) printf ("[DEBUG][main][%d]: Argomento (File di configurazione non specificato).\n Implica default fallback a RCFILE: \"%s\".\n", __LINE__, RCFILE); fd = open (RCFILE, O_RDONLY); } if (ReturnValue != -1) { /* Se non specificati argomenti -v o -h */ if (fd > 0) { /* Se file descriptor valido */ if (DEBUG) printf ("[DEBUG][main][%d]: Status fd: %d\n", __LINE__, fd); if (ParseRC(fd, input, output, &max_processes, &dim_min_exec_par)) { close (fd); /* Chiudi il file e ricicla cosi il file descriptor */ if (DEBUG) { printf ("\n[DEBUG][main][%d]: Input: %s\n", __LINE__, input); printf ("[DEBUG][main][%d]: Output: %s\n", __LINE__, output); printf ("[DEBUG][main][%d]: MaxProcesses: %d\n", __LINE__, max_processes); printf ("[DEBUG][main][%d]: DimMinExecPar: %d\n", __LINE__, dim_min_exec_par); } strcpy (ComposedInputFile, input); strcat (ComposedInputFile, "/"); strcat (ComposedInputFile, INPUTFILE); if ((dirh = opendir(input)) != NULL) { if (DEBUG) { printf ("\n[DEBUG][main][%d]: Directory \"%s\" aperta con successo.\n", __LINE__, input); printf ("[DEBUG][main][%d]: Avvio processo server con valore sleep = %d.\n", __LINE__, sleeptime); } while (sleeptime) { if (DEBUG) printf ("[DEBUG][main][%d]: Server precedente sleep(%d), istanza: %d: ", __LINE__, sleeptime, x); if (x > 0) dirh = opendir(input); if (sleeptime > SLEEP) sleeptime = SLEEP; for (dirp = readdir(dirh); dirp != NULL; dirp = readdir(dirh)) { if (strcmp(dirp->d_name, INPUTFILE) == 0) { found = 1; dirp = NULL; } } if (found) { if (DEBUG) printf ("Found \"%s\".\n", ComposedInputFile); /* Questo codice non mi convince troppo... */ if ((fd = open(ComposedInputFile, O_RDONLY)) > 0) { if (DEBUG) printf ("[DEBUG][main][%d]: File descriptor per \"%s\" creato con successo, fd = %d.\n", __LINE__, ComposedInputFile, fd); if (ParseInputFile(fd, &matrice_righe, &matrice_colonne, &matrice, &vettore)) { if (DEBUG) { printf ("[DEBUG][main][%d]: File \"%s\" parsed successfully.\n", __LINE__, ComposedInputFile); printf ("[DEBUG][main][%d]: Matrice righe: %d.\n", __LINE__, matrice_righe); printf ("[DEBUG][main][%d]: Matrice colonne: %d.\n", __LINE__, matrice_colonne); if (matrice) printf ("[DEBUG][main][%d]: Abbiamo la matrice nel main.\n", __LINE__); if (vettore) printf ("[DEBUG][main][%d]: Abbiamo il vettore nel main.\n", __LINE__); } /* Chiudi file di input */ close(fd); /* Rimuovi file di input */ if ((unlink(ComposedInputFile)) == 0) { if (DEBUG) printf ("[DEBUG][main][%d]: File \"%s\" unlinked successfully.\n", __LINE__, ComposedInputFile); } else { printf ("[DEBUG][main][%d]: TODO: File di input non cancellato.\n", __LINE__); } if ((ProcessNumber = CalcProcessNumber(dim_min_exec_par, max_processes, matrice_righe, matrice_colonne))) { if (DEBUG) printf ("[DEBUG][main][%d]: Nunero ottimale processi calcolato (Ipotesi): %d.\n", __LINE__, ProcessNumber); /* Allocazione shared memory */ if ((shmid = shmget(SHMKEY, SHMSIZE, IPC_PERMS|IPC_CREAT)) >= 0) { if (DEBUG) printf ("[DEBUG][main][%d]: Segmento shared memory allocato: %d.\n", __LINE__, shmid); /* Allacciamento shared memory */ if ((a = (char *) shmat(shmid, NULL, 0)) == (char *) -1) { perror ("shmat"); exit (1); /* SHM allacciata con successo */ } else { if (DEBUG) printf ("[DEBUG][main][%d]: Shared memory (%d) correttamete allacciata.\n", __LINE__, shmid); /* Allocazione semaforo */ if ((semid = semget(SEMKEY, NSEMS, IPC_PERMS|IPC_CREAT)) >= 0) { if (DEBUG) printf ("[DEBUG][main][%d]: Semaforo correttamente allocato: %d.\n", __LINE__, semid); /* Valore con cui sara' inizializzato il semaforo */ arg.val = ProcessNumber; /* Initializza semaforo */ if (semctl (semid, 0, SETVAL, arg) >= 0 || semctl (semid, 1, SETVAL, arg) >= 0) { if (DEBUG) printf ("[DEBUG][main][%d]: Semaforo inizilizzato correttamente.\n", __LINE__); /* * Prima di chiamare i client con il fork scrivi il vettore * nella SHM cosi i client lo possono leggere una volta e salvare come variabile. */ strcpy (a, vettore); /* Copia vettore nella SHM */ LifeCycle = (matrice_righe / ProcessNumber); /* Definisce il numero di fork (execl) da eseguire */ for (i=1; i <= ProcessNumber; i++) { if (i == ProcessNumber) /* Se ultimo fork */ LifeCycle = (matrice_righe / ProcessNumber) + (matrice_righe % ProcessNumber); if (DEBUG) printf ("[DEBUG][main][%d]: Esecuzione processo numero: %d, ciclo di vita: %d.\n", __LINE__, i, LifeCycle); /* * TODO: Il vettore deve essere passato all'interno della SHM. */ /* strcpy (a, matrice[i]); */ /* Copia matrice nella SHM */ /* strcat (a, vettore); */ /* Concatena vettore alla SHM */ sprintf (SharedMemoryID, "%d", shmid); /* Converti id SHM in stringa */ sprintf (SemaphoreID, "%d", semid); /* Converti id semaforo in stringa */ sprintf (colonne, "%d", matrice_colonne); /* Converti numero colonne in stringa */ /* sprintf (ElementiMatrice, "%d", strlen(matrice[i])); Converti numero elementi matrice in stringa */ sprintf (LifeCycleArg, "%d", LifeCycle); /* Converti numero cicli di vita */ if (DEBUG) printf ("[DEBUG][main][%d]: Matrice copiata nella shared memory.\n", __LINE__); switch (pid = fork()) { case -1: perror ("fork"); exit (1); case 0: printf ("[DEBUG][main]: Fork pid is %d.\n", getppid()); /* execl (CLIENT_EXEC_BIN, "client", SharedMemoryID, SemaphoreID, ElementiMatrice, colonne, LifeCycleArg); */ execl (CLIENT_EXEC_BIN, "client", SharedMemoryID, SemaphoreID, colonne, LifeCycleArg); default: printf ("[DEBUG][main]: Fork pid is default.\n"); } } /* End for fork (execl) */ /* * . Prima di eseguire la execl() il server scrive il vettore nella SHM, * cosi i client se lo possono prendere quando nascono. * * . Il server deve scrivere tutte le righe, una per volta, in SHM. * Le scrive quando i client segnalano libero (semaforo). * * . I client se le andranno a' prendere tante volte quanto e' il loro ciclo di vita. */ if (DEBUG) printf ("[DEBUG][main][%d]: Attesa semaforo: Lettura vettore da parte del client... ", __LINE__); sb.sem_num = 0; /* Semaforo A (Indice 0) */ sb.sem_op = 0; sb.sem_flg = 0; semop (semid, &sb, 1); if (DEBUG) printf ("done.\n"); /* svuota il buffer while (*a != '\0') *a++ = '\0'; */ /* * . I client sono partiti ed hanno il vettore. * . Ora dobbiamo alimentare i loro cicli di vita. * . Accesso sincronizzato alla SHM. * * SEMAFORI (0 = verde, 1 = rosso) * * A: 0 condizione di accesso al buffer per qualsiasi processo. * B: 0 condizione di esecuzione per il server. * C: 0 condizione di esecuzione per il client. * * CODICE SERVER * * if (B == 0) { * * if (A == 0) { * * A = 1 * Inserisci riga * A = 0 * } * * B = 1 (il server deve attendere che un client setti B a 0) * } */ arg.val = 0; /* Setta semafori a 0 */ semctl (semid, 1, SETVAL, arg); /* Semaforo B */ semctl (semid, 2, SETVAL, arg); /* Semaforo C */ semctl (semid, 3, SETVAL, arg); /* Semaforo D */ i = 0; while (i < matrice_righe) { if (DEBUG) { printf ("[DEBUG][main][%d]: Processo righa matrice %d di %d.\n", __LINE__, i+1, matrice_righe); printf ("[DEBUG][main][%d]: Attesa semaforo B libero... ", __LINE__); } sb.sem_num = 1; /* Semaforo B (Indice 1) */ sb.sem_op = 0; /* Attendi che il semaforo sia 0 */ sb.sem_flg = 0; semop (semid, &sb, 1); if (DEBUG) { printf ("done.\n"); printf ("[DEBUG][main][%d]: Attesa semaforo A libero... ", __LINE__); } sb.sem_num = 0; /* Semaforo A (Indice 0) */ sb.sem_op = 0; /* Attendi che il semaforo sia 0 */ sb.sem_flg = 0; semop (semid, &sb, 1); if (DEBUG) printf ("done.\n"); arg.val = 1; /* Setta semaforo a 1 */ semctl (semid, 0, SETVAL, arg); /* Semaforo A */ strcpy (a, matrice[i]); /* Copia matrice in SHM */ if (DEBUG) { printf ("[DEBUG][main][%d]: HO SCRITTO \"%s\" nella SHM.\n", __LINE__, a); } arg.val = 0; /* Setta semaforo a 0 */ semctl (semid, 0, SETVAL, arg); /* Semaforo A */ /* Solo ora il client continua... */ arg.val = 1; /* Setta semaforo a 1 */ /*semctl (semid, 1, SETVAL, arg); */ /* Semaforo B */ if (semctl (semid, 1, SETVAL, arg) == -1) { perror("semctl"); exit (-1); } printf ("HO SETTATO B ad %d.\n", semctl(semid, 1, GETVAL, NULL)); /* D a 0 */ arg.val = 0; /* Setta semaforo a 0 */ if (semctl (semid, 3, SETVAL, arg) == -1) { perror("semctl"); exit (-1); } printf ("HO SETTATO D ad %d.\n", semctl(semid, 3, GETVAL, NULL)); i++; } printf ("STO PER SVUOTARE IL BUFFER...\n"); /* while (*a != '\0') *a++ = '\0'; */ /* Libera risorse semaforo */ semctl (semid, 0, IPC_RMID); } else { perror ("semctl"); exit(1); } } else { perror ("semget"); exit(1); } /* Detach Shared Memory */ if ((shmdt(a)) == 0) { if (DEBUG) printf ("[DEBUG][main][%d]: SHM (%d) correttamete slacciata.\n", __LINE__, shmid); } else { perror ("shmdt"); exit(1); } } /* * Libera risorse IPC * * Queste risorse devono essere solamente liberate dopo che tutti client hanno * completato il proprio lavoro. Per far attendere il server facciamo uso della wait(). * O anche dei semafori, se tutti N processi hanno segnalato di aver completato il * lavoro possiamo chiudere. * * shmctl (shmid, IPC_RMID, NULL); */ } else { printf ("[ERROR CASE X]: ...\n"); perror("shmget"); } } } else { if (DEBUG) printf ("[DEBUG][main][%d]: File \"%s\" parse error.\n\n", __LINE__, strcat(input, INPUTFILE)); } sleeptime = 5; /* Questo NON avviene mai... INUTILE! */ } else { /* sleeptime = 1; */ printf ("[ERROR CASE 4]: %s \"%s\".\n", ERROR_CASE_4, strcat(input, INPUTFILE)); perror ("open"); ReturnValue = 4; } found = 0; } else { if (DEBUG) printf ("No.\n"); } x++; closedir(dirh); sleep(sleeptime); } } else { printf ("[ERROR CASE 3]: %s \"%s\".\n", ERROR_CASE_3, input); perror ("opendir"); ReturnValue = 3; } } else { close(fd); printf ("[ERROR CASE 2]: %s.\n", ERROR_CASE_2); ReturnValue = 2; } } else { printf ("[ERROR CASE 1]: %s \"%s\".\n", ERROR_CASE_1, argv[1]); perror ("open"); ReturnValue = 1; } } return ReturnValue; } /* }}} */ /* vim: set tabstop=8 shiftwidth=8 softtabstop=8 expandtab foldmethod=marker: */ 7.2. Il sorgente di client.c ---------------------------- /* {{{ COMMENT */ /* * CVS id: $Id: README,v 1.28 2005/01/25 14:55:18 eim Exp $ * * Corso: Laboratorio di sistemi operativi * * Titolo: "Progetto laboratorio di sistemi operativi" * * Informazioni * generali: Informazioni generali sul progetto. * TODO: Completa questa sezione... * * Ambiente Il seguente sorgente e' stato sviluppato e compilato * di sviluppo: sulle seguenti architetture hardware ed ambienti: * * Sistema operativo Architettura * -------------------------------------------- * Ubuntu GNU/Linux 4.10 (Warty) x86 * Ubuntu GNU/Linux 4.10 (Warty) PowerPC * Windows XP/Microsoft x86 * -------------------------------------------- * * Compilazione: gcc -pedantic -g -Wall -o client client.c * * Potete compilare semplicemente eseguendo "make". * * NON CI DEVONO MAI ESSERE WARNING E/O ERRORI NEL CODICE. * * Limit funzioni * da poter usare: Quote da WIKI (Domande ricorrenti): * * "Quali funzioni si possono usare e quali no per lo * sviluppo del progetto?" * * Risposta: In generale, si possono usare tutte le * funzioni del Kernel e delle librerie standard, eccetto * nella gestione dei file su disco, dove possono essere * utilizzate solo le funzioni del Kernel. Esempi: si a * atoi, sprintf, sscanf, sleep, strcat, strcmp, no a * fopen, fclose, fread, fwrite. * * Coding style: . I tab devono corrispondere ad 8 spazi. * . Usare solamente commenti compatibili con ISO C90. * . Ogni funzione deve essere commentata a dovere. * . Rispettare l'identazione del sorgente. * . Usate un editor di testo decente come vim o emacs. * * SEGUIRE SEMPRE QUESTO CODING STYLE PER GARANTIRE LA * MASSIMA LETTURA DEL CODICE -- (Vi[m] spacco le mani! ;) * * Argomenti: Il programma accetta due argomento sulla CLI: * * Index Status Informazioni argomento * ---------------------------------------------- * ARG#1 Richiesto File di input (ASCII) * ARG#2 Opzionale File di output (ASCII) * ---------------------------------------------- * * Argomenti ARGV[>2] vengono prettamente ignorati. * * Limiti: TODO: Elencare eventuali limiti del programma. * * Gestione * degli errori: Segue un esempio di come elencare un'errore: * * ERROR CASE 2 (Basic CLI arguments not given) * Blah, blah, blah, ... * * Strutture dati: TODO: Elencare le strutture dati in uso. * * TODO: Completare tutte le parti del sorgente in cui compare * la stringa "TODO". * * Ulteriori modifiche, blah, blah, blah... */ /* }}} */ /* {{{ DEFINE */ /* * Define global static vars */ #define PROGRAM_NAME "client" #define PROGRAM_DESC "Progetto laboratorio Sistemi Operativi I" #define BUG_REPORTS "dsi-so-list@winetea.mine.nu" #define CVS_ID "$Id: README,v 1.28 2005/01/25 14:55:18 eim Exp $" #define CVS_REVISION "$Revision: 1.28 $" #define CVS_DATE "$Date: 2005/01/25 14:55:18 $" #define CVS_AUTHOR "$Author: eim $" #define DEBUG 1 #define ERROR_CASE_1 "EDITME" #define BUFSIZE 1024 #define TMP_OUTPUT "/tmp/output" /* }}} */ /* {{{ INCLUDE */ /* * Include external code and libs */ #include /* Standard I/O */ #include /* General utilities (Ci serve?) */ #include /* Character handling */ #include /* String handling */ #include /* String handling */ #include /* Shared memory */ #include /* Sempahore */ #include #include #include /* Execution suspension */ /* }}} */ /* {{{ union semun */ /* * Struttura per i semafori (richiesta per alcune operazioni con semctl). * * Author Marco Marri */ union semun { int val; struct semid_ds *buf; ushort *array; } arg; /* }}} */ /* {{{ int StringToInt() */ /* * int StringToInt() * * Converte la stringa rappresentante la riga della matrice o il vettore in un * array di interi. * * Author Marco Marri * Param s Puntatore alla stringa da covertire * Param a Puntatore all'array di interi in cui verr�convertita la stringa * Return void Modifiche attraverso puntatori */ void StringToInt(char *s, int *a) { int i=0, j=0, k=0; char *t=NULL; while(1) { /* alloco spazio per la stringa di appoggio */ t=(char *)malloc(strlen(s)*sizeof(char)); if(t) { if(s[i]=='\n' || s[i]=='\0') break; else { while(!isdigit(s[i])) i++; while(isdigit(s[i])) t[j++]=s[i++]; t[j]='\0'; j=0; a[k++]=atoi(t); } } else { if(DEBUG) printf("impossibile allocare spazio per la stringa temporanea.\n"); break; } } } /* }}} */ /* {{{ int Product() */ /* * int Product() * * Calcola il prodotto tra la riga della matrice interessata e il vettore, * richiamando la funzione StringToInt per convertire le due stringhe in * array di interi. * * Author Marco Marri * Param m Puntatore alla stringa rappresentante la riga della matrice * Param v Puntatore alla stringa rappresentante il vettore * Param k Numero delle colonne * Return int Risultato moltiplicazione (>=0) oppure -1 in caso di errore */ int Product(char *m, char *v, int k) { int *mat=NULL, *vet=NULL, i=0, r=-1; /* alloco dinamicamente lo spazio per i 2 vettori di k interi */ mat=(int *)malloc(k*sizeof(int)); vet=(int *)malloc(k*sizeof(int)); if(mat && vet) { /* parte che converte la stringa in un vettore di interi */ StringToInt(m, mat); StringToInt(v, vet); r=0; /* esecuzione del prodotto */ for(i=0;i * Param num Indice del semaforo all'interno dell'array di semafori * Param op Operazione sul semaforo * Param flg Flag per l'operazione * Return struct sembuf Struttura creata */ struct sembuf SetSemBuf(int num, int op, int flg) { struct sembuf sb; sb.sem_num = num; sb.sem_op = op; sb.sem_flg = flg; return sb; } /* }}} */ /* {{{ int main() */ /* * Welcome to main(), ladies and gents. * * Author Ivo Marino * Return bool 0 in caso di esecuzione corretta */ int main (int argc, char *argv[]) { int fd = -1; /* File descriptor */ int MyPid = 0; /* Il mio process id */ char *a = ""; /* Puntatore alla shared memory */ int i = 0; /* Semplice contatore */ char vettore[BUFSIZE]; /* Substring vettore nella SHM */ char matrice[BUFSIZE]; /* Substring matrice nella SHM */ char buffer[BUFSIZE]; char dest[BUFSIZE]; char strpid[BUFSIZE]; int semid; /* id semaforo (argomento) */ /* int semval; Valore semaforo */ int colonne = 0; /* Numero di colonne */ int cicli_vita = 0; /* Numero righe da processare */ int got = 0; /* Dacce sta grazia.......... */ int ReturnValue = 0; /* Application return value */ struct sembuf sb; /* Buffer semaforo */ if (argc >= 2) { /* Se specificato uno o piu' argomenti */ /* Se -h or --help */ if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) { printf ("Usage: %s\n", PROGRAM_NAME); printf ("%s\n\n", PROGRAM_DESC); printf (" -h --help Output this help screen.\n"); printf (" -v --version Show program version.\n\n"); printf ("Report bugs to: %s.\n", BUG_REPORTS); ReturnValue = -1; /* Se -v or --version */ } else if ((strcmp(argv[1], "-v") == 0) || (strcmp(argv[1], "--version") == 0)) { printf ("%s %s\n", PROGRAM_NAME, CVS_REVISION); printf ("Written by ...n\n\n"); printf ("Copyright (C) 2004-2005 Gruppo Progetto WineTea\n"); printf ("This is academic software; The source code will be freely\n"); printf ("available once the relative university exam has been passed.\n"); ReturnValue = -1; } else { /* Se argomento specificato diverso da "help" o "version" */ MyPid = getpid(); /* Riconosciamo un dato processo forkato all' iterno del debug tramite il proprio PID */ if (DEBUG) { printf ("[FORK][client][P:%d]: (ARG1) Indirizzo SHM specificato: \"%s\".\n", MyPid, argv[1]); printf ("[FORK][client][P:%d]: (ARG2) Indirizzo semaforo specificato: \"%s\".\n", MyPid, argv[2]); printf ("[FORK][client][P:%d]: (ARG4) Numero colonne (Ci serve?): \"%s\".\n", MyPid, argv[3]); printf ("[FORK][client][P:%d]: (ARG5) Cicli di vita: \"%s\".\n", MyPid, argv[4]); } /* Acquisisci id semaforo */ semid = atoi(argv[2]); /* Converti colonne */ colonne = atoi(argv[3]); /* Acquisisci cicli di vita */ cicli_vita = atoi(argv[4]); /* If error attach shared memory */ if ((a = (char *) shmat(atoi(argv[1]), NULL, 0)) == (char *) -1) { printf ("[FORK][client][P:%d]: Errore allacciamento SHM (%d).\n", MyPid, atoi(argv[1])); perror ("shmat"); exit (1); /* If attach shared memory successfull */ } else { if (DEBUG) printf ("[FORK][client][P:%d]: SHM (%d) correttamente allacciata.\n", MyPid, atoi(argv[1])); /* Leggi valore semaphoro */ if ((semctl(semid, 0, GETVAL, NULL)) >= 0) { if (DEBUG) { printf ("[FORK][client][P:%d]: Semaphore (%d).\n", MyPid, atoi(argv[2])); printf ("[FORK][client][P:%d]: Decremento semaforo...", MyPid); } /* Salva vettore */ strcpy (vettore, a); /* Decrementa semaforo */ /* sb.sem_num = 0; sb.sem_op = -1; sb.sem_flg = 0; */ sb = SetSemBuf(0,-1,0); semop (semid, &sb, 1); /* Attendi che tutti i client hanno prelevato il vettore dal buffer */ /* sb.sem_num = 0; sb.sem_op = 0; sb.sem_flg = 0; */ sb = SetSemBuf(0,0,0); semop (semid, &sb, 1); if (DEBUG) printf ("done.\n"); } /* * SEMAFORI (0 = verde, 1 = rosso) * A: 0 condizione di accesso al buffer x qualsiasi processo * B: 0 condizione di esecuzione x il server * C: 0 condizione di esecuzione per il client * * CODICE SERVER * * attendi(B==0) { * attendi(A==0) { * A=1 * inserisci riga * A=0 * } * B=1 (il server deve attendere che un client setti B=0) * } * * CODICE CLIENT * * attendi (C==0) { * C=1 (nessun altro client pu�eseguirsi) * attendi(B==1) { (la memoria ha scritto nel buffer ed è in attesa che un client lo svuoti) * attendi(A==0) { * A=1 * prendi riga && svuota buffer * A=0 * } * B=0 (il server può�continuare la sua esecuzione) * } * C=0 (un altro client può�partire ma si fermerà�al controllo if(B=1), * finchè�il server non avrà�messo qualcosa nel buffer) * } */ /* Si parte con i calcoli... */ for (i = 1; i <= cicli_vita; i++) { if (DEBUG) printf ("[FORK][client][P:%d]: Elaboro cilco di vita: %d di %d.\n", MyPid, i, cicli_vita); while (!got) { if (DEBUG) printf ("[FORK][client][P:%d]: Siamo nel got...\n", MyPid); /* semaforo C */ /* sb.sem_num = 2; sb.sem_op = 0; sb.sem_flg = 0; */ sb = SetSemBuf(2,0,0); semop(semid, &sb, 1); /* appena C==0 vai avanti */ /* setta C = 1 */ arg.val = 1; if(semctl (semid, 2, SETVAL, arg) == -1) { /* Semaforo C */ perror("semctl"); exit(-1); } /* semaforo D (Attendi che sia 0) */ sb = SetSemBuf(3,0,0); semop(semid, &sb, 1); /* appena D == 0 vai avanti */ /* semaforo A (attendi che sia 0) */ /* sb.sem_num = 0; sb.sem_op = 0; sb.sem_flg = 0; */ sb = SetSemBuf(0,0,0); semop(semid, &sb, 1); /* setta A = 1 */ arg.val = 1; if(semctl (semid, 0, SETVAL, arg) == -1) { /* Semaforo A */ perror("semctl"); exit(-1); } while (strlen(a) == 0) { printf ("[FORK][client][P:%d]: Sto aspettando...\n", MyPid); sleep(1); } /* copia la riga della matrice */ if ((strlen(a) > 0) && (strcmp(a, vettore) != 0)) { if (DEBUG) printf ("[FORK][client][P:%d]: Stringa contenuta in shared memory (%d): \"%s\".\n", MyPid, strlen(a), a); strcpy (matrice, a); /* svuota il buffer */ while (*a != '\0') *a++ = '\0'; got = 1; } /* setta A = 0 Rende buffer disponibile a tutti processi... */ arg.val = 0; semctl (semid, 0, SETVAL, arg); /* Semaforo A */ /* setta D = 1 Server: Scrivi prossima riga */ arg.val = 1; semctl (semid, 3, SETVAL, arg); /* Semaforo D */ /* setta B = 0 Server: Scrivi prossima riga */ arg.val = 0; semctl (semid, 1, SETVAL, arg); /* Semaforo B */ /* setta C = 0 Permette altri client di partire... */ arg.val = 0; semctl (semid, 2, SETVAL, arg); /* Semaforo C */ } /* End while(!got) */ if (DEBUG) { printf ("[FORK][client][P:%d]: Matrice: %s\n", MyPid, matrice); printf ("[FORK][client][P:%d]: Vettore: %s\n", MyPid, vettore); } ReturnValue = Product(matrice, vettore, colonne); sprintf (buffer, "%d", ReturnValue); sprintf (strpid, "%d", MyPid); strcpy (dest, "/tmp/output"); strcat (dest, "/"); strcat (dest, strpid); printf ("%s\n", dest); if ((fd = open (dest, O_CREAT)) < 0) { perror("open"); exit (-1); } /* if (write(fd, buffer, BUFSIZE) < 0) { perror("write"); exit(-1); } */ got = 0; /* riazzero la variabile per prendere le righe successive */ if (DEBUG) { printf ("[FORK][client][P:%d]: Prodotto riga matrice per vettore: \"%d\".\n", MyPid, ReturnValue); } } /* Fine cilco di vita */ /* * . Attendo accesso SHM in scrittura per ritornare risultato. */ } } } else { /* Se argomento non specificato */ if (DEBUG) printf ("[FORK][client][P:%d]: Argomento non specificato.\n", MyPid); } if (ReturnValue != -1) { /* Se non specificati argomenti -v o -h */ /* TODO: Da implementare... */ } return ReturnValue; } /* }}} */ /* vim: set tabstop=8 shiftwidth=8 softtabstop=8 expandtab foldmethod=marker: */ 7.3. Il sorgente del Makefile ----------------------------- Questo Makefile e' compatibile con GNU make. --- Inizio Makefile --- # # Makefile per il "progetto" # # $Id: README,v 1.28 2005/01/25 14:55:18 eim Exp $ # # Si puo lanciare il comando "make compile run" per compilare ed eseguire # direttamente il progetto. # all: directory compile directory: if [ ! -d "/tmp/input" ]; then mkdir /tmp/input; fi if [ ! -d "/tmp/output" ]; then mkdir /tmp/output; fi if [ ! -e "/tmp/input/input.txt" ]; then cp ${PWD}/input.txt /tmp/input; fi compile: gcc -pedantic -g -Wall -o /tmp/progetto progetto.c gcc -pedantic -g -Wall -o /tmp/client client.c # # TODO # # run e' un po problematico in quanto e' necessario eseguire: # progetto per un corrette funzionamento, di conseguenza a mio # avviso sarebbe necessario eseguire: make run . Al momento non so' # come passare un argomento ad un file make (Forse con $(1)), un'alternativa e' # quella di specificare l'argomento nel Makefile fisso, del tipo: # # run: # /tmp/progetto ArgomentoFisso # run: /tmp/progetto update: cvs update commit: cvs commit changelog: echo -e "#\n# Automatic generated ChangeLog\n#\n" |\ cvs2cl -T -t -w -r --fsf -U usermap --header -\ && rm ChangeLog.bak --- Fine Makefile --- Per dettagli sull'funzionamento del Makefile si prega di consultare il paragrafo 4.1. 8. Il file di ChangeLog ----------------------- Il sorgente del progetto e' stato sviluppato utilizzando il sistema di sviluppo CVS, questo tool permette di tenere traccia in automatico delle singole modifiche sui vari file sorgente da parte di ogni componente del gruppo di lavoro. Riportiamo qui di seguito, per esteso, il file di ChangeLog: --- Inizio file di ChangeLog --- 2005-01-25 Tuesday 15:53 Ivo Marino * README (1.27): Trasformato in file Unix ed aggiunto paragrafo sul file di output. 2005-01-25 Tuesday 15:51 Ivo Marino * README (1.26): Aggiunto informazioni sul file di output. 2005-01-25 Tuesday 15:46 Ivo Marino * README (1.25): Introdotto ultime modfiche del ChangeLog. 2005-01-25 Tuesday 15:39 Ivo Marino * ChangeLog (1.13): Aggiornato. 2005-01-25 Tuesday 15:33 Ivo Marino 2005-01-25 Tuesday 15:33 Ivo Marino * Makefile (1.11), Makefile (1.12): Pulisci /tmp/output. 2005-01-25 Tuesday 15:21 Ivo Marino * progetto.c (1.72): Il server scrive i risultati nel file di output. 2005-01-25 Tuesday 14:15 Ivo Marino * progetto.c (1.71): Il server attende correttamente i file di output dei client e li cancella. 2005-01-25 Tuesday 13:07 Marco Marri * progetto.c (1.70): introdotto codice server funzionale per l'algoritmo della sincronizzazione dei semafori 2005-01-25 Tuesday 12:21 Ivo Marino * client.c (1.33): Corretta write() per scrittura su file di output da parte dei client. 2005-01-25 Tuesday 12:10 Ivo Marino * README (1.24): clean up 2005-01-25 Tuesday 01:17 Matteo La Bella * README (1.23): File aggiornato 2005-01-25 Tuesday 01:04 Ivo Marino * client.c (1.32), progetto.c (1.69), progettorc (1.13): Correto progetto: Ora funzione con conbinazione MaxProcess 4 e DimMinExecPar 1, cioe': Ogni riga un process. Inoltre scriviamo correttamente i file di output dal client in /tmp/output. Il codice e' anche stato ripulito del tutto: Disabilitato semaforo D. Provare per credere. 2005-01-24 Monday 23:55 Ivo Marino * progetto.c (1.68): Introdotta funzione SetSemBuf() scritta da Marco. E' gia presente nel client e ora anche nel server. 2005-01-24 Monday 23:51 Ivo Marino * client.c (1.31), progetto.c (1.67): Prima fase di pulizia del codice (Operazione salvezza): Il percorso di output letta dal file di config viene ora passato al client come argomento. 2005-01-24 Monday 23:21 Ivo Marino * Makefile (1.10): Aggiunto opzione spell per controllare la correttezza grammaticale del file README. Richied il pacchetto aspell-it per funzionare. 2005-01-24 Monday 22:39 Ivo Marino * Makefile (1.9): Aggiunta creazione cartella di output. 2005-01-24 Monday 22:14 Marco Marri * client.c (1.30), progetto.c (1.66): file consegnati 2005-01-24 Monday 22:06 Ivo Marino * README (1.22), client.c (1.29), progetto.c (1.65), progettorc (1.12): Prima serie di aggiornamenti del giorno di consegna (Chianti). 2005-01-24 Monday 12:10 Matteo La Bella * README (1.21): Verifica generale 2005-01-24 Monday 00:12 Ivo Marino * ChangeLog (1.12): Aggiornato. 2005-01-24 Monday 00:11 Ivo Marino * usermap (1.2): Aggiornato indirizzo email. 2005-01-24 Monday 00:10 Ivo Marino * progetto.c (1.64): Piccoli accorgimenti prima del meritato riposo. 2005-01-23 Sunday 23:57 Ivo Marino * client.c (1.28): Rimosse informazioni Debug di minore importanza. 2005-01-23 Sunday 23:51 Ivo Marino * ChangeLog (1.11): Aggiornato 2005-01-23 Sunday 23:50 Ivo Marino * client.c (1.27): Rimpiazzata voce di Debug __LINE__ con il pid del processo, e' un'informazione piu' importante nel nostro caso. 2005-01-23 Sunday 23:27 Ivo Marino * ChangeLog (1.10): Aggiornato 2005-01-23 Sunday 23:13 Ivo Marino * progetto.c (1.63): Aggiunta printf che stampa riga matrice. 2005-01-23 Sunday 23:02 Marco Marri * client.c (1.26): o la va o la spacca.. speramo che va.. 2005-01-23 Sunday 22:00 Marco Marri * client.c (1.25): manca codice di attesa per B==1 2005-01-23 Sunday 21:34 Ivo Marino * README (1.20): Ulteriore clean up (Qui c'e' da mettere a posto ancora un bel po': Disordine totale, altro che RFC ;) 2005-01-23 Sunday 21:28 Ivo Marino * README (1.19): Clean uo. 2005-01-23 Sunday 20:34 Matteo La Bella * README (1.18): Aggiornato il server.c e client 2005-01-23 Sunday 20:20 Ivo Marino * progetto.c (1.62): Implementato pseudocodice lato server (Prima implementazione). 2005-01-23 Sunday 19:09 Ivo Marino * progetto.c (1.61): Clean up. 2005-01-23 Sunday 19:09 Ivo Marino * client.c (1.24): Introdotto pseudocodice di Marco. 2005-01-23 Sunday 18:59 Ivo Marino * progetto.c (1.60): Introdotto pseudocodice di Marco. 2005-01-23 Sunday 17:49 Ivo Marino * ChangeLog (1.9), Makefile (1.8), progetto.c (1.59): Aggiornato changelog 2005-01-23 Sunday 17:41 Ivo Marino * ChangeLog (1.8): Aggiornato 2005-01-23 Sunday 16:25 Ivo Marino * client.c (1.23), progetto.c (1.58): Aggiunti commenti relativi alla scrittura riga matrice => SHM. 2005-01-23 Sunday 16:10 Ivo Marino * client.c (1.22), progetto.c (1.57): Clean up. 2005-01-23 Sunday 16:01 Ivo Marino * client.c (1.21), progetto.c (1.56): Passaggio vettore server->client sincronizzato tramite SHM e semafori. 2005-01-23 Sunday 15:03 Marco Marri * progetto.c (1.55): correzione codice passaggio valore inizializzazione semaforo; 2005-01-23 Sunday 14:54 Marco Marri * progetto.c (1.54): aggiunta union semun per operazioni con int semctl(int semid, int semnum, int cmd, union semun arg); 2005-01-23 Sunday 12:06 Ivo Marino * client.c (1.20): Aggiunto info argomenti. 2005-01-23 Sunday 12:00 Ivo Marino * client.c (1.19), progetto.c (1.53): Aggiunto supporto per passaggio id del semaforo dal server al client. 2005-01-22 Saturday 22:21 Matteo La Bella * README (1.17): Indice, Shared Memory e Semafori 2005-01-22 Saturday 19:10 Ivo Marino * progetto.c (1.52): Bugfix: Risolto problema ParseRC(). 2005-01-22 Saturday 19:02 Ivo Marino * progettorc (1.11): Dos to UNIX. 2005-01-22 Saturday 18:56 Marco Marri * progetto.c (1.51): dim_max_exec_par è solo un brutto ricordo.. forse.. 2005-01-22 Saturday 17:52 Ivo Marino * progetto.c (1.50): Aggiunto commenti 2005-01-22 Saturday 17:39 Marco Marri * progettorc (1.10): eliminata keyword inutilizzata; 2005-01-22 Saturday 16:15 Ivo Marino * client.c (1.18): Bugfix, LifeCycle ora sembra funzionare a dovere. 2005-01-22 Saturday 16:12 Ivo Marino * progetto.c (1.49): Risolto problema di chiamata fork(), ora gira. 2005-01-22 Saturday 15:26 Ivo Marino * progetto.c (1.48): Riabilitata execl. 2005-01-22 Saturday 15:18 Marco Marri * client.c (1.17): aggiungo codice riguardante il ciclo di vita del client; variato metodo acquisizione vettore da buffer; 2005-01-22 Saturday 15:00 Ivo Marino * ChangeLog (1.7): Aggiornato. 2005-01-22 Saturday 14:57 Ivo Marino * progetto.c (1.47): Introdotto for(ProcessNumber) e variabile LifeCycle. Chiamate execl temporaneamente disabilitate. 2005-01-22 Saturday 13:50 Matteo La Bella * README (1.16): Installazione 2005-01-22 Saturday 13:39 Ivo Marino * progetto.c (1.46): Aggiornata funzione CalcProcessNumber(). 2005-01-22 Saturday 10:55 Ivo Marino * ChangeLog (1.6): Aggiornato. 2005-01-22 Saturday 10:50 Ivo Marino * client.c (1.16): Aumentato buffer vettore matrice. 2005-01-22 Saturday 10:46 Ivo Marino * client.c (1.15): Cosmetic updates. 2005-01-22 Saturday 10:43 Ivo Marino * client.c (1.14): Vettore e Matrice ora vengono passati tutti nella SHM. Il client estrapola correttamente i valori ed esegue la moltiplicazione. 2005-01-22 Saturday 02:51 Ivo Marino * README (1.15): Rimossa tabella ruolo e formattato UNIX. 2005-01-22 Saturday 02:43 Matteo La Bella * README (1.14): Aggiunto funzione SLEEP del main(), Controllo numeri processi 2005-01-21 Friday 19:48 Ivo Marino * client.c (1.13), progetto.c (1.45): Usual train hacks. 2005-01-21 Friday 16:27 Ivo Marino * progetto.c (1.44): Ora anche il vettore si trova nella SHM, inoltre passiamo al client il numero elementi che fanno parte della matrice in modo da poter dividere la parte matrice da quella vettore nei dati della SHM. 2005-01-21 Friday 15:55 Ivo Marino * client.c (1.12), progetto.c (1.43): Varie modifiche. 2005-01-21 Friday 14:13 Ivo Marino * progetto.c (1.42): Aggiunto __LINE__ nelle info di debug. 2005-01-21 Friday 13:57 Ivo Marino * Makefile (1.7): Il file di input ora viene copiato in /tmp/input invece che linkato. 2005-01-21 Friday 13:56 Ivo Marino * progetto.c (1.41): Cosmetic updates. 2005-01-21 Friday 13:55 Ivo Marino * progetto.c (1.40): Il file di input viene ora correttamente cancellato. 2005-01-21 Friday 12:58 Ivo Marino * progetto.c (1.39): Bugfix: Ora il ciclo while del main non termina piu' per errore. 2005-01-21 Friday 09:31 Marco Marri * README (1.13): corretto al volo errore di scrittura; possibile ce ne siano altri 2005-01-21 Friday 07:58 Ivo Marino * README (1.12): Cosmetic updates. 2005-01-20 Thursday 18:32 Matteo La Bella * README (1.11): RCF Style 2005-01-20 Thursday 17:04 Ivo Marino * progetto.c (1.38): Introdotto codice fork(). Cosa fa? E' ancora da capire. 2005-01-20 Thursday 16:06 Ivo Marino * progetto.c (1.37): Aggiunto codice per inizzializzare il semaforo. 2005-01-20 Thursday 15:43 Ivo Marino * ChangeLog (1.5): Aggiornato 2005-01-20 Thursday 15:38 Ivo Marino * progetto.c (1.36): Implementato codice per gestione semafori, il semaforo viene correttamente inizzializato. 2005-01-20 Thursday 15:13 Ivo Marino * client.c (1.11): Rimosso define legate alla SHM, non servono in quanto il client esegue solo un'allacciamento alla shared memory precedentemente creata dal server. 2005-01-20 Thursday 13:46 Marco Marri * client.c (1.10), progetto.c (1.35): ho fame; aggiunto passaggio di matrice_colonne al client tramite execl; buon pranzo 2005-01-20 Thursday 13:02 Ivo Marino * README (1.10): Cosmetic updates. 2005-01-20 Thursday 12:59 Ivo Marino * README (1.9): Introdotte variabili CVS. 2005-01-20 Thursday 12:55 Ivo Marino * README (1.8): Inizziamo a parlare di documentazione seria... 2005-01-20 Thursday 12:35 Matteo La Bella * README (1.7): TODO e migliorate istruzioni 2005-01-20 Thursday 12:21 Ivo Marino * client.c (1.9), progetto.c (1.34): Passaggio vettore al client come argv[2]. 2005-01-20 Thursday 11:53 Matteo La Bella * README (1.6): Aggiornato Matricole - Salvataggio 2005-01-20 Thursday 11:17 Matteo La Bella * SO.sxw (1.4): [no log message] 2005-01-20 Thursday 11:08 Ivo Marino * client.c (1.8), progetto.c (1.33): Risolto accesso shared memory da parte del client. 2005-01-19 Wednesday 21:19 Marco Marri * progettorc (1.9): ripristino versione precedente (eseguito ommit per errore 2005-01-19 Wednesday 21:15 Marco Marri * progetto.c (1.32), progettorc (1.8): ottimizzata funzione CalcProcessNumber 2005-01-19 Wednesday 19:48 Ivo Marino * client.c (1.7), progetto.c (1.31): Train hacks. 2005-01-19 Wednesday 16:54 Ivo Marino * ChangeLog (1.4): Aggiornato. 2005-01-19 Wednesday 16:53 Ivo Marino * client.c (1.6), progetto.c (1.30): Introduzione exec(). 2005-01-19 Wednesday 16:51 Marco Marri * progetto.c (1.29): sistemata la funzione CalcProcessNumber; aggiunta spiegazione del calcolo dei processi utili; 2005-01-19 Wednesday 16:41 Matteo La Bella * README (1.5): Modificata struttura del file README. Abbandonata il file SO.swx 2005-01-19 Wednesday 16:09 Marco Marri * ChangeLog (1.3): prova 2005-01-19 Wednesday 15:36 Matteo La Bella * README (1.4): Versione TXT di SO.sxx 2005-01-19 Wednesday 15:17 Marco Marri * client.c (1.5): aggiunto qualche commento nelle funzioni StringToInt e Product; sistemato un bug che poteva causare il crash del programma per segmentation fault; 2005-01-19 Wednesday 15:03 Ivo Marino * progetto.c (1.28): La shared memory ora viene correttamente allacciata e slacciata, introdotto anche un primo test di scrittura nella shared memory. 2005-01-19 Wednesday 15:02 Ivo Marino * ChangeLog (1.2): Aggiornato 2005-01-19 Wednesday 15:01 Ivo Marino * Makefile (1.6): Piccolo problema di sintassi, corretto. 2005-01-19 Wednesday 14:58 Ivo Marino * Makefile (1.5): Aggiunto codice per rimuovere il vecchio file ChangeLog.bak. 2005-01-19 Wednesday 14:54 Ivo Marino * Makefile (1.4): Aggiunto direttiva changelog per la generazione automatica del file ChangeLog tramite il tool cvs2cl. Date un'occhiata al nuovo file ChangeLog per capire come funziona. 2005-01-19 Wednesday 14:53 Ivo Marino * ChangeLog (1.1), usermap (1.1): Initial import 2005-01-19 Wednesday 14:24 Ivo Marino * progetto.c (1.27): Aggiunto ulteriore codice per gestire la shared memory. 2005-01-19 Wednesday 12:53 Ivo Marino * progetto.c (1.26): Introdotto codice shared memory iniziale (Ancora non del tutto funzionale). 2005-01-18 Tuesday 23:40 Ivo Marino * progetto.c (1.25): Aggiornata funzione CalcProcessNumber() 2005-01-18 Tuesday 23:34 Ivo Marino * progettorc (1.7): Aggiornato 2005-01-18 Tuesday 23:23 Ivo Marino * client.c (1.4): Retab (TAB to spaces), code clean up. 2005-01-18 Tuesday 22:57 Marco Marri * client.c (1.3): warnings (del cazzo) corretti 2005-01-18 Tuesday 22:00 Marco Marri * progetto.c (1.24): [no log message] 2005-01-18 Tuesday 21:59 Marco Marri * client.c (1.2): aggiunte 2 funzioni, vedere codice 2005-01-18 Tuesday 18:36 Marco Marri * progetto.c (1.23): checkline corretta; matrice e vettore ora vengono salvate correttamente; piccoli sfoghi contro Ivo che mi edita la formattazione del codice mandandomi in bestia 2005-01-18 Tuesday 18:11 Ivo Marino * progetto.c (1.22): Ampliata funzione CalcProcessNumber(). 2005-01-18 Tuesday 18:10 Matteo La Bella * SO.sxw (1.3): Miglioramenti, aggiunte 2005-01-18 Tuesday 17:28 Ivo Marino * progetto.c (1.21), progettorc (1.6): Aggiunto supporto per DimMaxExecPar. 2005-01-18 Tuesday 16:44 Ivo Marino * progetto.c (1.20): Bugfix segnalato da parte di Marco. 2005-01-18 Tuesday 16:37 Ivo Marino * progetto.c (1.19): Introdotta la funzione CalcProcessNumber(). 2005-01-18 Tuesday 16:35 Ivo Marino * input.txt (1.4): Aggiornato 2005-01-18 Tuesday 16:29 Ivo Marino * Makefile (1.3): Aggiunto supporto per autogenerare /tmp/input e linkare il file di input. 2005-01-18 Tuesday 16:26 Matteo La Bella * progettorc (1.5): Accorgimenti per rendere il file di default 2005-01-18 Tuesday 15:39 Ivo Marino * progettorc (1.4): Test commit su auster 2005-01-17 Monday 22:36 Ivo Marino * progetto.c (1.18): Retab (Niente modifiche strutturali). 2005-01-17 Monday 22:14 Marco Marri * progetto.c (1.17): piccole migliorie 2005-01-17 Monday 21:56 Marco Marri * progetto.c (1.16): correzioni al codice onde evitare la presenza di warnings 2005-01-17 Monday 20:30 Marco Marri * progetto.c (1.15): aggiunta unzione CheckLine; cambiate strutture dati per matrice/vettore; aggiunto codice in IsDataInputFile() per estrapolate matrice/vettore dal file di input; varie ed eventuali; 2005-01-17 Monday 19:15 Matteo La Bella * SO.sxw (1.2): Correzione grammaticale di alcune parole 2005-01-17 Monday 18:07 Matteo La Bella * README (1.3): [no log message] 2005-01-17 Monday 17:59 Matteo La Bella * README (1.2): Prove dei tag CVS 2005-01-17 Monday 17:56 Matteo La Bella * SO.sxw (1.1): Documentazione del progetto. Formato OpenOffice.org 2005-01-17 Monday 11:41 Ivo Marino * client.c (1.1): Initial import 2005-01-17 Monday 10:59 Ivo Marino * Makefile (1.2): Aggiunto compilazione client.c. 2005-01-15 Saturday 11:01 Ivo Marino * progetto.c (1.14): Cosmetic updates 2005-01-15 Saturday 10:53 Ivo Marino * progetto.c (1.13): Aggiunto isDataInputFile(); Ora siamo in grado di leggere il numero di righe e colonne della matrice dai file di input. 2005-01-05 Wednesday 16:27 Ivo Marino * progetto.c (1.12): Cleanup 2005-01-05 Wednesday 16:20 Ivo Marino * progetto.c (1.11): Introdotta funzione ParseInputFile(), e' una versione modificata di ParseRC(). Sembra funzionare a dovere. 2005-01-05 Wednesday 16:07 Ivo Marino * input.txt (1.3): Definite keyword 2005-01-05 Wednesday 15:04 Ivo Marino * progetto.c (1.10): Aggiunto supporto per aprire il file di input nella cartella di input durante il ciclo while temporizzato. 2005-01-05 Wednesday 00:09 Ivo Marino * progetto.c (1.9): Il file input.txt viene ora rilevato durante il ciclo di controllo temporizzato nella cartella di input non appena viene inserito. 2005-01-04 Tuesday 23:11 Ivo Marino * progetto.c (1.8): Aggiunto supporto per apertura directory di input, importante bugfix nella funzione isData() e introdotti codici di ritorni relativi agli errori. 2005-01-04 Tuesday 22:45 Ivo Marino * progetto.c (1.7): Introdotto ciclo while e sleep. 2004-12-30 Thursday 18:35 Ivo Marino * progetto.c (1.6): Implementata gestione valori di ritorno di int main(). 2004-12-30 Thursday 18:23 Ivo Marino * progetto.c (1.5): Introdotta la funzione IsData(), il file di configurazione viene letto correttamente ora. 2004-12-30 Thursday 17:56 Ivo Marino * progetto.c (1.4), progettorc (1.3): Introdotta la funzione ParseRC() 2004-12-30 Thursday 16:35 Ivo Marino * progetto.c (1.3): Implementata gestione argomenti 2004-12-29 Wednesday 13:50 Ivo Marino * progetto.c (1.2): Aggiunto folding per vim. 2004-12-29 Wednesday 13:22 Ivo Marino * progettorc (1.2): Aggiunto commento 2004-12-29 Wednesday 13:04 Ivo Marino * progettorc (1.1): Initial import 2004-12-29 Wednesday 12:29 Ivo Marino * input.txt (1.2): Aggiunto commento e trasformato in file UNIX 2004-12-29 Wednesday 11:56 Ivo Marino * input.txt (1.1): Initial import 2004-12-27 Monday 14:30 Ivo Marino * progetto.c (1.1): Initial import 2004-12-27 Monday 14:18 Ivo Marino * README (1.1), Makefile (1.1): Initial import --- Fine file di ChangeLog --- EOF Documentazione progetto.