From 59b107d5207f19e0049bbd1032e10cba660da92e Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Sun, 12 Jan 2003 00:24:28 +0000 Subject: [PATCH] Messi gli esempi nel testo e usata daemon dove serve nei server --- ipc.tex | 419 +++++++++++++++++++++++++++------------- sources/DirMonitor.c | 14 +- sources/FortuneServer.c | 5 +- sources/Makefile | 11 +- sources/ReadMonitor.c | 4 +- 5 files changed, 306 insertions(+), 147 deletions(-) diff --git a/ipc.tex b/ipc.tex index 2e4d381..6f69e46 100644 --- a/ipc.tex +++ b/ipc.tex @@ -349,7 +349,7 @@ quindi associato allo standard input) in caso di \code{"w"}. Lo stream restituito da \func{popen} è identico a tutti gli effetti ai file stream visti in \capref{cha:files_std_interface}, anche se è collegato ad una -pipe e non ad un inode\index{inode}, e viene sempre aperto in modalità +pipe e non ad un file, e viene sempre aperto in modalità \textit{fully-buffered} (vedi \secref{sec:file_buffering}); l'unica differenza con gli usuali stream è che dovrà essere chiuso dalla seconda delle due nuove funzioni, \funcd{pclose}, il cui prototipo è: @@ -600,7 +600,7 @@ int main(int argc, char *argv[]) { /* Variables definition */ int i, n = 0; - char *fortunefilename = "/usr/share/games/fortunes/italia"; + char *fortunefilename = "/usr/share/games/fortunes/linux"; char **fortune; char line[80]; int fifo_server, fifo_client; @@ -617,6 +617,7 @@ int main(int argc, char *argv[]) exit(1); } } + daemon(0, 0); /* open fifo two times to avoid EOF */ fifo_server = open(fifoname, O_RDONLY); if (fifo_server < 0) { @@ -676,41 +677,45 @@ qualora si riscontri un errore il server uscir in cui la funzione \func{mkfifo} fallisce per la precedente esistenza della fifo). -Una volta che si è certi che la fifo di ascolto esiste si procede -(\texttt{\small 23--32}) alla sua apertura. Questo viene fatto due volte -per evitare di dover gestire all'interno del ciclo principale il caso in cui -il server è in ascolto ma non ci sono client che effettuano richieste. -Si ricordi infatti che quando una fifo è aperta solo dal capo in lettura, -l'esecuzione di \func{read} ritorna con zero byte (si ha cioè una condizione -di end-of-file). +Una volta che si è certi che la fifo di ascolto esiste la procedura di +inizializzazione è completata. A questo punto si può chiamare (\texttt{\small + 23}) la funzione \func{daemon} per far proseguire l'esecuzione del programma +in background come demone. Si può quindi procedere (\texttt{\small 24--33}) +alla apertura della fifo: si noti che questo viene fatto due volte, prima in +lettura e poi in scrittura, per evitare di dover gestire all'interno del ciclo +principale il caso in cui il server è in ascolto ma non ci sono client che +effettuano richieste. Si ricordi infatti che quando una fifo è aperta solo +dal capo in lettura, l'esecuzione di \func{read} ritorna con zero byte (si ha +cioè una condizione di end-of-file). Nel nostro caso la prima apertura si bloccherà fintanto che un qualunque client non apre a sua volta la fifo nota in scrittura per effettuare la sua richiesta. Pertanto all'inizio non ci sono problemi, il client però, una volta ricevuta la risposta, uscirà, chiudendo tutti i file aperti, compresa la fifo. A questo punto il server resta (se non ci sono altri client che stanno -effettuando richieste) con la fifo chiusa sul lato in lettura e a questo punto -\func{read} non si bloccherà in attesa di input, ma ritornerà in continuazione -restituendo un end-of-file.\footnote{Si è usata questa tecnica per - compatibilità, Linux infatti supporta l'apertura delle fifo in - lettura/scrittura, per cui si sarebbe potuto effettuare una singola apertura - con \const{O\_RDWR}, la doppia apertura comunque ha il vantaggio che non si - può scrivere per errore sul capo aperto in sola lettura.} +effettuando richieste) con la fifo chiusa sul lato in lettura, ed in questo +stato la funzione \func{read} non si bloccherà in attesa di input, ma +ritornerà in continuazione, restituendo un end-of-file.\footnote{Si è usata + questa tecnica per compatibilità, Linux infatti supporta l'apertura delle + fifo in lettura/scrittura, per cui si sarebbe potuto effettuare una singola + apertura con \const{O\_RDWR}, la doppia apertura comunque ha il vantaggio + che non si può scrivere per errore sul capo aperto in sola lettura.} Per questo motivo, dopo aver eseguito l'apertura in lettura (\texttt{\small - 24--28}),\footnote{di solito si effettua l'apertura del capo in lettura in - modalità non bloccante, per evitare il rischio di uno stallo (se nessuno - apre la fifo in scrittura il processo non ritornerà mai dalla \func{open}) - che nel nostro caso non esiste, mentre è necessario potersi bloccare in - lettura in attesa di una richiesta.} si esegue una seconda apertura in -scrittura (\texttt{\small 29--32}), scartando il relativo file descriptor che -non sarà mai usato, ma lasciando la fifo comunque aperta anche in scrittura, -cosicché le successive possano bloccarsi. + 24--28}),\footnote{di solito si effettua l'apertura del capo in lettura di + una fifo in modalità non bloccante, per evitare il rischio di uno stallo: se + infatti nessuno apre la fifo in scrittura il processo non ritornerà mai + dalla \func{open}. Nel nostro caso questo rischio non esiste, mentre è + necessario potersi bloccare in lettura in attesa di una richiesta.} si +esegue una seconda apertura in scrittura (\texttt{\small 29--32}), scartando +il relativo file descriptor, che non sarà mai usato, in questo modo però la +fifo resta comunque aperta anche in scrittura, cosicché le successive chiamate +a \func{read} possono bloccarsi. A questo punto si può entrare nel ciclo principale del programma che fornisce -le risposte ai client (\texttt{\small 34--50}), che viene eseguito +le risposte ai client (\texttt{\small 34--50}); questo viene eseguito indefinitamente (l'uscita del server viene effettuata inviando un segnale, in -modo da passare attraverso la routine di chiusura che cancella la fifo). +modo da passare attraverso la routine di chiusura che cancella la fifo). Il server è progettato per accettare come richieste dai client delle stringhe che contengono il nome della fifo sulla quale deve essere inviata la risposta. @@ -719,9 +724,9 @@ richiesta dalla fifo nota (che a questo punto si bloccher non ci sono richieste). Dopo di che, una volta terminata la stringa (\texttt{\small 40}) e selezionato (\texttt{\small 41}) un numero casuale per ricavare la frase da inviare, si procederà (\texttt{\small 42--46}) -all'apertura della fifo per la risposta, che \texttt{\small 47--48}) poi vi +all'apertura della fifo per la risposta, che poi \texttt{\small 47--48}) vi sarà scritta. Infine (\texttt{\small 49}) si chiude la fifo di risposta che -non serve più. +non serve più. Il codice del client è invece riportato in \figref{fig:ipc_fifo_client}, anche in questo caso si è omessa la gestione delle opzioni e la funzione che stampa @@ -802,6 +807,62 @@ la richiesta, se non si fosse fatto cos quanto senza la richiesta, il server non avrebbe potuto aprirne il capo in scrittura e l'apertura si sarebbe bloccata indefinitamente. +Verifichiamo allora il comportamento dei nostri programmi, in questo, come in +altri esempi precedenti, si fa uso delle varie funzioni di servizio, che sono +state raccolte nella libreria \file{libgapil.so}, per poter usare quest'ultima +occorrerà definire la speciale variabile di ambiente \code{LD\_LIBRARY\_PATH} +in modo che il linker dinamico possa accedervi. + +In generale questa variabile indica il pathname della directory contenente la +libreria. Nell'ipotesi (che daremo sempre per verificata) che si facciano le +prove direttamente nella directory dei sorgenti (dove di norma vengono creati +sia i programmi che la libreria), il comando da dare sarà \code{export + LD\_LIBRARY\_PATH=./}; a questo punto potremo lanciare il server, facendogli +leggere una decina di frasi, con: +\begin{verbatim} +[piccardi@gont sources]$ ./fortuned -n10 +\end{verbatim} + +Avendo usato \func{daemon} per eseguire il server in background il comando +ritornerà immediatamente, ma potremo verificare con \cmd{ps} che in effetti il +programma resta un esecuzione in background, e senza avere associato un +terminale di controllo (si ricordi quanto detto in \secref{sec:sess_daemon}): +\begin{verbatim} +[piccardi@gont sources]$ ps aux +... +piccardi 27489 0.0 0.0 1204 356 ? S 01:06 0:00 ./fortuned -n10 +piccardi 27492 3.0 0.1 2492 764 pts/2 R 01:08 0:00 ps aux +\end{verbatim}%$ +e si potrà verificare anche che in \file{/tmp} è stata creata la fifo di +ascolto \file{fortune.fifo}. A questo punto potremo interrogare il server con +il programma client; otterremo così: +\begin{verbatim} +[piccardi@gont sources]$ ./fortune +Linux ext2fs has been stable for a long time, now it's time to break it + -- Linuxkongreß '95 in Berlin +[piccardi@gont sources]$ ./fortune +Let's call it an accidental feature. + --Larry Wall +[piccardi@gont sources]$ ./fortune +......... Escape the 'Gates' of Hell + `:::' ....... ...... + ::: * `::. ::' + ::: .:: .:.::. .:: .:: `::. :' + ::: :: :: :: :: :: :::. + ::: .::. .:: ::. `::::. .:' ::. +...:::.....................::' .::::.. + -- William E. Roadcap +[piccardi@gont sources]$ ./fortune +Linux ext2fs has been stable for a long time, now it's time to break it + -- Linuxkongreß '95 in Berlin +\end{verbatim}%$ +e ripetendo varie volte il comando otterremo, in ordine casuale, le dieci +frasi tenute in memoria dal server. + +Infine per chiudere il server basterà inviare un segnale di terminazione con +\code{killall fortuned} e potremo verificare che il gestore del segnale ha +anche correttamente cancellato la fifo di ascolto da \file{/tmp}. + Benché il nostro sistema client-server funzioni, la sua struttura è piuttosto complessa e continua ad avere vari inconvenienti\footnote{lo stesso Stevens, che esamina questa architettura in \cite{APUE}, nota come sia impossibile @@ -2575,19 +2636,20 @@ Chiamare \func{MutexLock} decrementa il valore del semaforo: se questo libero (ha già valore 1) sarà bloccato (valore nullo), se è bloccato la chiamata a \func{semop} si bloccherà fintanto che la risorsa non venga rilasciata. Chiamando \func{MutexUnlock} il valore del semaforo sarà -incrementato di uno, sbloccandolo qualora fosse bloccato. Si noti che occorre -eseguire sempre prima \func{MutexLock} e poi \func{MutexUnlock}, perché se per -un qualche errore si esegue più volte quest'ultima il valore del semaforo -crescerebbe oltre 1, e \func{MutexLock} non avrebbe più l'effetto aspettato -(bloccare la risorsa quando questa è considerata libera). Si tenga presente -che usare \func{MutexRead} per controllare il valore dei mutex prima di -proseguire non servirebbe comunque, dato che l'operazione non sarebbe atomica. +incrementato di uno, sbloccandolo qualora fosse bloccato. + +Si noti che occorre eseguire sempre prima \func{MutexLock} e poi +\func{MutexUnlock}, perché se per un qualche errore si esegue più volte +quest'ultima il valore del semaforo crescerebbe oltre 1, e \func{MutexLock} +non avrebbe più l'effetto aspettato (bloccare la risorsa quando questa è +considerata libera). Infine si tenga presente che usare \func{MutexRead} per +controllare il valore dei mutex prima di proseguire in una operazione di +sblocco non servirebbe comunque, dato che l'operazione non sarebbe atomica. Vedremo in \secref{sec:ipc_lock_file} come sia possibile ottenere un'interfaccia analoga a quella appena illustrata, senza incorrere in questi problemi, usando il file locking\index{file!locking}. - \subsection{Memoria condivisa} \label{sec:ipc_sysv_shm} @@ -2967,9 +3029,6 @@ video; al solito il codice completo si trova con i sorgenti allegati nel file \footnotesize \centering \begin{minipage}[c]{15cm} \begin{lstlisting}{} -/* computation function for DirScan */ -int ComputeValues(struct dirent * direntry); -void HandSIGTERM(int signo); /* global variables for shared memory segment */ struct DirProp { int tot_size; @@ -2981,8 +3040,7 @@ struct DirProp { int tot_block; int tot_char; int tot_sock; -}; -struct DirProp *shmptr; +} *shmptr; int shmid; int mutex; /* main body */ @@ -2995,11 +3053,13 @@ int main(int argc, char *argv[]) printf("Wrong number of arguments %d\n", argc - optind); usage(); } + if (chdir(argv[1])) { /* chdir to be sure dir exist */ + perror("Cannot find directory to monitor"); + } Signal(SIGTERM, HandSIGTERM); /* set handlers for termination */ Signal(SIGINT, HandSIGTERM); Signal(SIGQUIT, HandSIGTERM); - /* create needed IPC objects */ - key = ftok("./DirMonitor.c", 1); /* define a key */ + key = ftok("~/gapil/sources/DirMonitor.c", 1); /* define a key */ shmid = shmget(key, 4096, IPC_CREAT|0666); /* get a shared memory */ if (shmid < 0) { perror("Cannot create shared memory"); @@ -3007,12 +3067,14 @@ int main(int argc, char *argv[]) } if ( (shmptr = shmat(shmid, NULL, 0)) == NULL ) { /* attach to process */ perror("Cannot attach segment"); + exit(1); } if ((mutex = MutexCreate(key)) == -1) { /* get a Mutex */ perror("Cannot create mutex"); exit(1); } /* main loop, monitor directory properties each 10 sec */ + daemon(1, 0); /* demonize process, staying in monitored dir */ while (1) { MutexLock(mutex); /* lock shared memory */ memset(shmptr, 0, sizeof(struct DirProp)); /* erase previous data */ @@ -3028,54 +3090,78 @@ int main(int argc, char *argv[]) \label{fig:ipc_dirmonitor_main} \end{figure} -Il programma usa delle variabili globali (\texttt{\small 4--18}) per mantenere +Il programma usa delle variabili globali (\texttt{\small 2--14}) per mantenere i valori relativi agli oggetti usati per la comunicazione inter-processo; si è definita inoltre una apposita struttura \struct{DirProp} che contiene i dati relativi alle proprietà che si vogliono mantenere nella memoria condivisa, per l'accesso da parte dei client. Il programma, dopo la sezione, omessa, relativa alla gestione delle opzioni da -riga di comando, che si limitano alla eventuale stampa di un messaggio di +riga di comando (che si limitano alla eventuale stampa di un messaggio di aiuto a video ed all'impostazione della durata dell'intervallo con cui viene -ripetuto il calcolo delle proprietà della directory, controlla (\texttt{\small - 25--28}) che sia stato specificato un parametro (il nome della directory da -tenere sotto controllo) senza il quale esce immediatamente con un messaggio di -errore. - -Il passo successivo (\texttt{\small 29--31}) è quello di installare i gestori -per i segnali di terminazione, infatti, visto che il programma è progettato -come server, non è prevista una conclusione esplicita nel corpo principale, -per cui, per gestire l'uscita, si è fatto uso di questi ultimi. - -Si può poi passare a creare (\texttt{\small 32--45}) gli oggetti di -intercomunicazione necessari. Si ricava (\texttt{\small 33}) una chiave usando -il nome del programma, con questa si ottiene (\texttt{\small 34--38}) un -segmento di memoria condivisa \var{shmid} (uscendo in caso di errore), che si -aggancia (\texttt{\small 39--41}) al processo all'indirizzo \var{shmptr}; sarà -attraverso questo puntatore che potremo accedere alla memoria condivisa, che -sarà vista nella forma data da \struct{DirProp}. Infine con la stessa chiave -si crea (\texttt{\small 42--45}) anche un mutex (utilizzando le funzioni di -interfaccia già descritte in \secref{sec:ipc_sysv_sem}) che utilizzeremo per -regolare l'accesso alla memoria condivisa. +ripetuto il calcolo delle proprietà della directory) controlla (\texttt{\small + 21--24}) che sia stato specificato il parametro necessario contenente il +nome della directory da tenere sotto controllo, senza il quale esce +immediatamente con un messaggio di errore. + +Poi, per verificare che il parametro specifichi effettivamente una directory, +si esegue (\texttt{\small 25--27}) su di esso una \func{chdir}, uscendo +immediatamente in caso di errore. Questa funzione serve anche per impostare +la directory di lavoro del programma nella directory da tenere sotto +controllo, in vista del successivo uso della funzione +\func{daemon}.\footnote{Si noti come si è potuta fare questa scelta, + nonostante le indicazioni illustrate in \secref{sec:sess_daemon}, per il + particolare scopo del programma, che necessita comunque di restare + all'interno di una directory.} Infine (\texttt{\small 28--30}) si installano +i gestori per i vari segnali di terminazione che, avendo a che fare con un +programma che deve essere eseguito come server, sono il solo strumento +disponibile per concluderne l'esecuzione. + +Il passo successivo (\texttt{\small 31--44}) è quello di creare gli oggetti di +intercomunicazione necessari. Si inizia costruendo (\texttt{\small 31}) la +chiave da usare come riferimento con il nome del programma,\footnote{si è + usato un riferimento relativo alla home dell'utente, supposto che i sorgenti + di GaPiL siano stati installati direttamente in essa. Qualora si effettui + una installazione diversa si dovrà correggere il programma.} dopo di che si +richiede (\texttt{\small 32}) la creazione di un segmento di memoria condivisa +con \func{shmget} (una pagina di memoria è sufficiente per i dati che +useremo), uscendo (\texttt{\small 33--36}) qualora la creazione non abbia +successo. + +Una volta ottenutone l'identificatore in \var{shmid}, si può agganciare +(\texttt{\small 37--40}) il segmento al processo con \func{shmat} anche in +questo caso si esce qualora la funzione non abbia successo. Con l'indirizzo +\var{shmptr} così ottenuto potremo poi accedere alla memoria condivisa, che, +per come abbiamo lo abbiamo definito, sarà vista nella forma data da +\struct{DirProp}. Infine (\texttt{\small 41--44}) utilizzando sempre la stessa +chiave, si crea, tramite le funzioni di interfaccia già descritte in +\secref{sec:ipc_sysv_sem}, anche un mutex, che utilizzeremo per regolare +l'accesso alla memoria condivisa. Una volta completata l'inizializzazione e la creazione degli oggetti di -intercomunicazione il programma eseguirà indefinitamente (\texttt{\small - 46--53}) il ciclo principale: si inizia bloccando il mutex (\texttt{\small - 48}) per poter accedere alla memoria condivisa (la funzione si bloccherà -automaticamente se qualche client sta leggendo), poi si cancellano -(\texttt{\small 49}) i valori precedentemente memorizzati, e si esegue -(\texttt{\small 50}) un nuovo calcolo degli stessi; infine si sblocca il mutex -(\texttt{\small 51}), e si attende (\texttt{\small 52}) per il periodo di -tempo specificato a riga di comando con l'opzione \code{-p}. +intercomunicazione il programma entra nel ciclo principale (\texttt{\small + 45--54}) dove vengono eseguitw indefinitamente le attività di monitoraggio. +Il primo passo (\texttt{\small 46}) è esguire \func{daemon} per proseguire con +l'esecuzione in background come si conviene ad un programma demone; si noti +che si è mantenuta, usando un valore non nullo del primo argomento, la +directory di lavoro corrente. + +Una volta che il programma è andato in background l'esecuzione prosegue +(\texttt{\small 47--53}) all'interno di un ciclo infinito: si inizia +(\texttt{\small 48}) bloccando il mutex con \func{MutexLock} per poter +accedere alla memoria condivisa (la funzione si bloccherà automaticamente se +qualche client sta leggendo), poi (\texttt{\small 49}) si cancellano i valori +precedentemente immagazzinati nella memoria condivisa con \func{memset}, e si +esegue (\texttt{\small 50}) un nuovo calcolo degli stessi utilizzando la +funzione \func{DirScan}; infine (\texttt{\small 51}) si sblocca il mutex con +\func{MutexUnlock}, e si attende (\texttt{\small 52}) per il periodo di tempo +specificato a riga di comando con l'opzione \code{-p} con una \func{sleep}. \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15cm} \begin{lstlisting}{} -... -/* - * Routine to compute directory properties inside DirScan - */ +/* Routine to compute directory properties inside DirScan */ int ComputeValues(struct dirent * direntry) { struct stat data; @@ -3091,9 +3177,7 @@ int ComputeValues(struct dirent * direntry) if (S_ISSOCK(data.st_mode)) shmptr->tot_sock++; return 0; } -/* - * Signal Handler to manage termination - */ +/* Signal Handler to manage termination */ void HandSIGTERM(int signo) { MutexLock(mutex); if (shmdt(shmptr)) { @@ -3121,56 +3205,43 @@ effettuare la scansione delle voci della directory, chiamando per ciascuna di esse la funzione \func{ComputeValues}, che esegue tutti i calcoli necessari. Il codice di quest'ultima è riportato in \figref{fig:ipc_dirmonitor_sub}. Come -si vede la funzione (\texttt{\small 5--19}) è molto semplice e si limita a -chiamare (\texttt{\small 8}) la funzione \func{stat} sul file indicato da +si vede la funzione (\texttt{\small 2--16}) è molto semplice e si limita a +chiamare (\texttt{\small 5}) la funzione \func{stat} sul file indicato da ciascuna voce, per ottenerne i dati, che poi utilizza per incrementare i vari -contatori. +contatori nella memoria condivisa, cui accede grazie alla variabile globale +\var{shmptr}. Dato che la funzione è chiamata da \func{DirScan}, si è all'interno del ciclo principale del programma, con un mutex acquisito, perciò non è necessario effettuare nessun controllo e si può accedere direttamente alla memoria -condivisa usando \var{shmptr} riempiendo i campi della struttura -\struct{DirProp}; così prima (\texttt{\small 9--10}) si sommano le dimensioni -dei file ed il loro numero, poi utilizzando le macro di -\tabref{tab:file_type_macro}, si contano (\texttt{\small 11--17}) quanti ce ne +condivisa usando \var{shmptr} per riempire i campi della struttura +\struct{DirProp}; così prima (\texttt{\small 6--7}) si sommano le dimensioni +dei file ed il loro numero, poi, utilizzando le macro di +\tabref{tab:file_type_macro}, si contano (\texttt{\small 8--14}) quanti ce ne sono per ciascun tipo. -In \figref{fig:ipc_dirmonitor_sub} è riportato anche (\texttt{\small 23--35}) -il codice del gestore di segnali di terminazione usato per chiudere il -programma, che oltre a provocare l'uscita del programma si incarica anche di -cancellare tutti gli oggetti di intercomunicazione non più necessari. Nel -caso anzitutto (\texttt{\small 24}) si acquisisce il mutex per evitare di -operare mentre un client sta ancora leggendo i dati, dopo di che prima si -distacca (\texttt{\small 25--28}) il segmento e poi lo si cancella -(\texttt{\small 29--32}). Infine (\texttt{\small 33}) si rimuove il mutex e si -esce. - -Il codice del client che permette di leggere le informazioni mantenute nella -memoria condivisa è riportato in \figref{fig:ipc_dirmonitor_client}, al solito -si è omessa la sezione di gestione delle opzioni e la funzione che stampa a -video le istruzioni; il codice completo è nei sorgenti allegati, nel file -\file{ReadMonitor.c}. +In \figref{fig:ipc_dirmonitor_sub} è riportato anche (\texttt{\small 17--30}) +il codice del gestore dei segnali di terminazione, usato per chiudere il +programma. Esso, oltre a provocare l'uscita del programma, si incarica anche +di cancellare tutti gli oggetti di intercomunicazione non più necessari. Per +questo anzitutto (\texttt{\small 19}) acquisisce il mutex con +\func{MutexLock}, per evitare di operare mentre un client sta ancora leggendo +i dati, dopo di che (\texttt{\small 20--23}) prima distacca il segmento di +memoria condivisa con \func{shmad} e poi (\texttt{\small 24--27}) lo cancella +con \func{shctl}. Infine (\texttt{\small 28}) rimuove il mutex con +\func{MutexRemove} ed esce. \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15cm} \begin{lstlisting}{} -#include -#include -#include /* directory */ -#include /* C standard library */ -#include - -#include "Gapil.h" -#include "macros.h" - int main(int argc, char *argv[]) { int i; key_t key; - .. - /* find needed IPC objects */ - key = ftok("./DirMonitor.c", 1); /* define a key */ + ... + /* create needed IPC objects */ + key = ftok("~/gapil/sources/DirMonitor.c", 1); /* define a key */ shmid = shmget(key, 4096, 0); /* get the shared memory ID */ if (shmid < 0) { perror("Cannot find shared memory"); @@ -3200,28 +3271,110 @@ int main(int argc, char *argv[]) \end{lstlisting} \end{minipage} \normalsize - \caption{Codice del programma client \file{ReadMonitor.c}.} + \caption{Codice del programma client del monitori di directory, + \file{ReadMonitor.c}.} \label{fig:ipc_dirmonitor_client} \end{figure} +Il codice del client, che permette di leggere le informazioni mantenute nella +memoria condivisa, è riportato in \figref{fig:ipc_dirmonitor_client}. Al +solito si è omessa la sezione di gestione delle opzioni e la funzione che +stampa a video le istruzioni; il codice completo è nei sorgenti allegati, nel +file \file{ReadMonitor.c}. + Una volta completata la gestione delle opzioni a riga di comando il programma -rigenera (\texttt{\small 16}) la chiave usata per identificare memoria -condivisa e mutex, richiede (\texttt{\small 17}) l'identificatore della -memoria condivisa (che in questo caso deve già esistere), uscendo in caso di -errore (\texttt{\small 18--21}). Una volta ottenuto l'identificatore si può -(\texttt{\small 22--25}) agganciare il segmento al processo (anche in questo -caso uscendo se qualcosa non funziona). Infine (\texttt{\small 26--29}) si -richiede pure l'identificatore del mutex. +rigenera (\texttt{\small 7}) con \func{ftok} la stessa chiave usata dal server +per identificare il segmento di memoria condivisa ed il mutex, poi +(\texttt{\small 8}) si richiede con \func{semget} l'identificatore della +memoria condivisa, ma in questo caso si vuole che esso esista di già; al +solito (\texttt{\small 9--12}) si esce in caso di errore. Una volta ottenuto +l'identificatore in \var{shmid} si può (\texttt{\small 13--16}) agganciare il +segmento al processo all'indirizzo \func{shmptr}; anche in questo caso si +chiude immediatamente il programma se qualcosa non funziona. Infine +(\texttt{\small 17--20}) con \func{MutexFind} si richiede l'identificatore del +mutex. Una volta completata l'inizializzazione ed ottenuti i riferimenti agli oggetti di intercomunicazione necessari viene eseguito il corpo principale del -programma (\texttt{\small 30--41}); si acquisisce (\texttt{\small 31}) il -mutex (qui avviene il blocco se la memoria condivisa non è disponibile), e poi -(\texttt{\small 32--40}) si stampano i vari valori in essa contenuti. Infine -(\texttt{\small 41}) si rilascia il mutex. +programma (\texttt{\small 21--33}); si comincia (\texttt{\small 22}) +acquisendo il mutex con \func{MutexLock}; qui avviene il blocco del processo +se la memoria condivisa non è disponibile. Poi (\texttt{\small 23--31}) si +stampano i vari valori mantenuti nella memoria condivisa attraverso l'uso di +\var{shmptr}. Infine (\texttt{\small 41}) con \func{MutexUnlock} si rilascia +il mutex, prima di uscire. + +Verifichiamo allora il funzionamento dei nostri programmi; al solito, usando +le funzioni di libreira occorre definire opportunamente +\code{LD\_LIBRARY\_PATH}; poi si potrà lanciare il server con: +\begin{verbatim} +[piccardi@gont sources]$ ./dirmonitor ./ +\end{verbatim}%$ +ed avendo usato \func{daemon} il comando ritornerà immediatamente. Una volta +che il server è in esecuzione, possiamo passare ad invocare il client per +verificarne i risultati, in tal caso otterremo: +\begin{verbatim} +[piccardi@gont sources]$ ./readmon +Ci sono 68 file dati +Ci sono 3 directory +Ci sono 0 link +Ci sono 0 fifo +Ci sono 0 socket +Ci sono 0 device a caratteri +Ci sono 0 device a blocchi +Totale 71 file, per 489831 byte +\end{verbatim}%$ +ed un rapido calcolo (ad esempio con \code{ls -a | wc} per contare i file) ci +permette di verificare che il totale dei file è giusto. Un controllo con +\cmd{ipcs} ci permette inoltre di verificare la presenza di un segmento di +memoria condivisa e di un semaforo: +\begin{verbatim} +[piccardi@gont sources]$ ipcs +------ Shared Memory Segments -------- +key shmid owner perms bytes nattch status +0xffffffff 54067205 piccardi 666 4096 1 -Verifichiamo allora il funzionamento del nostro programma +------ Semaphore Arrays -------- +key semid owner perms nsems +0xffffffff 229376 piccardi 666 1 +------ Message Queues -------- +key msqid owner perms used-bytes messages +\end{verbatim}%$ + +Se a questo punto aggiungiamo un file, ad esempio con \code{touch prova}, +potremo verificare (passati nel peggiore dei casi almeno 10 secondi, cioè +l'intervallo scelto per la rilettura dei dati), che: +\begin{verbatim} +[piccardi@gont sources]$ ./readmon +Ci sono 69 file dati +Ci sono 3 directory +Ci sono 0 link +Ci sono 0 fifo +Ci sono 0 socket +Ci sono 0 device a caratteri +Ci sono 0 device a blocchi +Totale 72 file, per 489887 byte +\end{verbatim}%$ + +Infine potremo terminare il server con il comando \code{killall dirmonitor}, +nel qual caso, ripetendo la lettura otterremo che: +\begin{verbatim} +[piccardi@gont sources]$ ./readmon +Cannot find shared memory: No such file or directory +\end{verbatim}%$ +e potremo verificare che anche gli oggetti di intercomunicazion e sono stati +cancellati: +\begin{verbatim} +[piccardi@gont sources]$ ipcs +------ Shared Memory Segments -------- +key shmid owner perms bytes nattch status + +------ Semaphore Arrays -------- +key semid owner perms nsems + +------ Message Queues -------- +key msqid owner perms used-bytes messages +\end{verbatim}%$ @@ -3515,15 +3668,13 @@ motivo la funzione restituir terzo valore possibile, \const{F\_RDLCK}, dato che la nostra interfaccia usa solo i write lock. Però è sempre possibile che siano richiesti altri lock sul file al di fuori dell'interfaccia, nel qual caso si potranno avere, - ovviamente, interferenze indesiderate.} ) in caso di successo, ed indicare -che il mutex è, rispettivamente libero o occupato. - - + ovviamente, interferenze indesiderate.} in caso di successo, ad indicare che +il mutex è, rispettivamente, libero o occupato. -Basandosi sulla semantica dei file lock POSIX valgono tutte le precisazioni +Basandosi sulla semantica dei file lock POSIX valgono tutte le considerazioni relative al comportamento di questi ultimi fatte in -\secref{sec:file_posix_lock}; questo significa che, al contrario di quanto -avveniva con l'altra interfaccia basata sui semafori, chiamate multiple a +\secref{sec:file_posix_lock}; questo significa ad esempio che, al contrario di +quanto avveniva con l'interfaccia basata sui semafori, chiamate multiple a \func{UnlockMutex} o \func{LockMutex} non hanno nessun inconveniente. diff --git a/sources/DirMonitor.c b/sources/DirMonitor.c index 2f11833..c9b92f4 100644 --- a/sources/DirMonitor.c +++ b/sources/DirMonitor.c @@ -25,7 +25,7 @@ * * Author: S. Piccardi Jan. 2003 * - * $Id: DirMonitor.c,v 1.4 2003/01/10 09:28:49 piccardi Exp $ + * $Id: DirMonitor.c,v 1.5 2003/01/12 00:24:28 piccardi Exp $ * *****************************************************************************/ #include @@ -54,8 +54,7 @@ struct DirProp { int tot_block; int tot_char; int tot_sock; -}; -struct DirProp *shmptr; +} *shmptr; int shmid; int mutex; @@ -99,11 +98,14 @@ int main(int argc, char *argv[]) printf("Wrong number of arguments %d\n", argc - optind); usage(); } + if (chdir(argv[1])) { /* chdir to be sure dir exist */ + perror("Cannot find directory to monitor"); + exit(1); + } Signal(SIGTERM, HandSIGTERM); /* set handlers for termination */ Signal(SIGINT, HandSIGTERM); Signal(SIGQUIT, HandSIGTERM); - /* create needed IPC objects */ - key = ftok("./DirMonitor.c", 1); /* define a key */ + key = ftok("~/gapil/sources/DirMonitor.c", 1); /* define a key, use dir */ shmid = shmget(key, 4096, IPC_CREAT|0666); /* get a shared memory */ if (shmid < 0) { perror("Cannot create shared memory"); @@ -111,12 +113,14 @@ int main(int argc, char *argv[]) } if ( (shmptr = shmat(shmid, NULL, 0)) == NULL ) { /* attach to process */ perror("Cannot attach segment"); + exit(1); } if ((mutex = MutexCreate(key)) == -1) { /* get a Mutex */ perror("Cannot create mutex"); exit(1); } /* main loop, monitor directory properties each 10 sec */ + daemon(1, 0); /* demonize process, staying in monitored dir */ while (1) { MutexLock(mutex); /* lock shared memory */ memset(shmptr, 0, sizeof(struct DirProp)); /* erase previous data */ diff --git a/sources/FortuneServer.c b/sources/FortuneServer.c index 7e3dfb6..8ba3a2f 100644 --- a/sources/FortuneServer.c +++ b/sources/FortuneServer.c @@ -26,7 +26,7 @@ * * Usage: fortuned -h give all info * - * $Id: FortuneServer.c,v 1.5 2002/12/03 11:06:05 piccardi Exp $ + * $Id: FortuneServer.c,v 1.6 2003/01/12 00:24:28 piccardi Exp $ * ****************************************************************/ /* @@ -56,7 +56,7 @@ int main(int argc, char *argv[]) { /* Variables definition */ int i, n = 0; - char *fortunefilename = "/usr/share/games/fortunes/italia"; + char *fortunefilename = "/usr/share/games/fortunes/linux"; char **fortune; char line[80]; int fifo_server, fifo_client; @@ -112,6 +112,7 @@ int main(int argc, char *argv[]) exit(1); } } + daemon(0, 0); /* open fifo two times to avoid EOF */ fifo_server = open(fifoname, O_RDONLY); if (fifo_server < 0) { diff --git a/sources/Makefile b/sources/Makefile index 2d33fbb..2b6240e 100644 --- a/sources/Makefile +++ b/sources/Makefile @@ -12,7 +12,7 @@ OBJ = SockRead.o SockWrite.o SigHand.o Mutex.o SharedMem.o LockFile.o DirScan.o FINAL = forktest errcode echo echod daytimed iterdaytimed daytime testfopen \ testren fortune fortuned mqfortune mqfortuned flock myls dirmonitor \ - readmon + readmon ipctestid $(LIB): $(OBJ) gcc -shared $^ -o $@ @@ -31,7 +31,7 @@ myls: myls.c $(CC) $(CFLAGJ) $^ -o $@ flock: Flock.c - $(CC) $(CFLAGJ) $^ -o $@ + $(CC) $^ -o $@ mqfortune: MQFortuneClient.c FortuneParse.c $(CC) $(CFLAGJ) $^ -o $@ @@ -46,10 +46,10 @@ fortuned: FortuneServer.c FortuneParse.c $(CC) $(CFLAGJ) $^ -o $@ barcode: BarCode.c - $(CC) $(CFLAGJ) $^ -o $@ + $(CC) $^ -o $@ barcodepage: BarCodePage.c - $(CC) $(CFLAGJ) $^ -o $@ + $(CC) $^ -o $@ getparam: getparam.c $(CC) $(CFLAGJ) $^ -o $@ @@ -81,6 +81,9 @@ iterdaytimed: ElemDaytimeTCPServer.c daytime: ElemDaytimeTCPClient.c $(CC) $(CFLAGJ) $^ -o $@ +ipctestid: IPCTestId.c + $(CC) $^ -o $@ + # Macro per la generazione della tarball dei sorgenti package: clean gapil_source.tgz diff --git a/sources/ReadMonitor.c b/sources/ReadMonitor.c index 80a77c7..9951358 100644 --- a/sources/ReadMonitor.c +++ b/sources/ReadMonitor.c @@ -25,7 +25,7 @@ * * Author: S. Piccardi Jan. 2003 * - * $Id: ReadMonitor.c,v 1.1 2003/01/10 09:28:49 piccardi Exp $ + * $Id: ReadMonitor.c,v 1.2 2003/01/12 00:24:28 piccardi Exp $ * *****************************************************************************/ #include @@ -90,7 +90,7 @@ int main(int argc, char *argv[]) * * ***********************************************************/ /* create needed IPC objects */ - key = ftok("./DirMonitor.c", 1); /* define a key */ + key = ftok("~/gapil/sources/DirMonitor.c", 1); /* define a key */ shmid = shmget(key, 4096, 0); /* get a shared memory ID */ if (shmid < 0) { perror("Cannot find shared memory"); -- 2.30.2