From b8248eaf35d824e8816c76ad9f9561c76d67b915 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Sat, 4 Jan 2003 17:24:30 +0000 Subject: [PATCH] Nouvi esempi finiti. E scritto un po' di roba --- filedir.tex | 16 ++-- ipc.tex | 219 +++++++++++++++++++++++++++++++++++++++---- sources/DirMonitor.c | 92 +++++++++++++++--- sources/DirScan.c | 4 +- sources/Gapil.h | 10 +- sources/Makefile | 16 +++- sources/Mutex.c | 14 ++- sources/myls.c | 3 +- 8 files changed, 328 insertions(+), 46 deletions(-) diff --git a/filedir.tex b/filedir.tex index efdefa2..fe34045 100644 --- a/filedir.tex +++ b/filedir.tex @@ -830,10 +830,12 @@ il nome del relativo campo; nel nostro caso sono definite le macro Per quanto riguarda il significato dei campi opzionali, il campo \var{d\_type} indica il tipo di file (fifo, directory, link simbolico, ecc.); i suoi -possibili valori sono riportati in \tabref{tab:file_dtype_macro}; per la -conversione da e verso l'analogo valore mantenuto dentro il campo -\var{st\_mode} di \struct{stat} sono definite anche due macro di conversione -\macro{IFTODT} e \macro{DTTOIF}: +possibili valori\footnote{fino alla versione 2.1 delle \acr{glibc} questo + campo, pur presente nella struttura, non è implementato, e resta sempre al + valore \const{DT\_UNKNOWN}.} sono riportati in +\tabref{tab:file_dtype_macro}; per la conversione da e verso l'analogo valore +mantenuto dentro il campo \var{st\_mode} di \struct{stat} sono definite anche +due macro di conversione \macro{IFTODT} e \macro{DTTOIF}: \begin{functions} \funcdecl{int IFTODT(mode\_t MODE)} Converte il tipo di file dal formato di \var{st\_mode} a quello di \var{d\_type}. @@ -846,9 +848,9 @@ Il campo \var{d\_off} contiene invece la posizione della voce successiva della directory, mentre il campo \var{d\_reclen} la lunghezza totale della voce letta. Con questi due campi diventa possibile, determinando la posizione delle varie voci, spostarsi all'interno dello stream usando la funzione -\func{seekdir},\footnote{sia questa funzione, che la corrispondente - \func{telldir}, sono estensioni prese da BSD, non previste dallo standard - POSIX.} il cui prototipo è: +\func{seekdir},\footnote{sia questa funzione che \func{telldir}, sono + estensioni prese da BSD, non previste dallo standard POSIX.} il cui +prototipo è: \begin{prototype}{dirent.h}{void seekdir(DIR *dir, off\_t offset)} Cambia la posizione all'interno di un \textit{directory stream}. \end{prototype} diff --git a/ipc.tex b/ipc.tex index c7aa4fc..3151c40 100644 --- a/ipc.tex +++ b/ipc.tex @@ -2940,23 +2940,210 @@ sequenziale\footnote{come accennato in \secref{sec:ipc_sysv_mq} per la modalità predefinita. Un esempio classico di uso della memoria condivisa è quello del -``\textit{monitor}'', in cui essa viene per scambiare informazioni fra un -processo server che vi scrive dei dati di interesse generale che ha -ottenuto, e tutti i processi client interessati agli stessi dati che così -possono leggerli in maniera completamente asincrona. Con questo schema di -funzionamento da una parte si evita che ciascun processo client debba -compiere l'operazione, potenzialmente onerosa, di ricavare e trattare i dati, -e dall'altra si evita al processo server di dover gestire l'invio a tutti -i client di tutti i dati (non potendo il server sapere quali di essi servono -effettivamente al singolo client). +``\textit{monitor}'', in cui viene per scambiare informazioni fra un processo +server, che vi scrive dei dati di interesse generale che ha ottenuto, e i +processi client interessati agli stessi dati che così possono leggerli in +maniera completamente asincrona. Con questo schema di funzionamento da una +parte si evita che ciascun processo client debba compiere l'operazione, +potenzialmente onerosa, di ricavare e trattare i dati, e dall'altra si evita +al processo server di dover gestire l'invio a tutti i client di tutti i dati +(non potendo il server sapere quali di essi servono effettivamente al singolo +client). Nel nostro caso implementeremo un ``\textsl{monitor}'' di una directory: un processo si incaricherà di tenere sotto controllo alcuni parametri relativi ad -una directory (il numero dei file contenuti, la dimensione totale, ecc.) che -saranno salvati in un segmento di memoria condivisa cui altri processi -potranno accedere per ricavare la parte di informazione che interessa. +una directory (il numero dei file contenuti, la dimensione totale, quante +directory, link simbolici, file normali, ecc.) che saranno salvati in un +segmento di memoria condivisa cui altri processi potranno accedere per +ricavare la parte di informazione che interessa. + +In \figref{fig:ipc_dirmonitor_main} si è riportata la sezione principale del +corpo del programma server, insieme alle definizioni delle altre funzioni +usate nel programma e delle variabili globali, omettendo tutto quello che +riguarda la gestione delle opzioni e la stampa delle istruzioni di uso a +video; al solito il codice completo si trova con i sorgenti allegati nel file +\file{DirMonitor.c}. + +\begin{figure}[!htb] + \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; + int tot_files; + int tot_regular; + int tot_fifo; + int tot_link; + int tot_dir; + int tot_block; + int tot_char; + int tot_sock; +}; +struct DirProp *shmptr; +int shmid; +int mutex; +/* main body */ +int main(int argc, char *argv[]) +{ + int i; + key_t key; + ... + if ((argc - optind) != 1) { /* There must be remaing parameters */ + printf("Wrong number of arguments %d\n", argc - optind); + usage(); + } + 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 */ + shmid = shmget(key, 4096, IPC_CREAT|0666); /* get a shared memory */ + if (shmid < 0) { + perror("Cannot create shared memory"); + exit(1); + } + if ( (shmptr = shmat(shmid, NULL, 0)) == NULL ) { /* attach to process */ + perror("Cannot attach segment"); + } + if ((mutex = MutexCreate(key)) == -1) { /* get a Mutex */ + perror("Cannot create mutex"); + exit(1); + } + /* main loop, monitor directory properties each 10 sec */ + while (1) { + MutexLock(mutex); /* lock shared memory */ + memset(shmptr, 0, sizeof(struct DirProp)); /* erase previous data */ + DirScan(argv[1], ComputeValues); /* execute scan */ + MutexUnlock(mutex); /* unlock shared memory */ + sleep(pause); /* sleep until next watch */ + } +} + \end{lstlisting} + \end{minipage} + \normalsize + \caption{Codice della funzione principale del programma \file{DirMonitor.c}.} + \label{fig:ipc_dirmonitor_main} +\end{figure} + +Il programma usa delle variabili globali (\texttt{\small 4--18}) 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 +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, che utilizzaremo 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}. +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}{} +... +/* + * Routine to print file name and size inside DirScan + */ +int ComputeValues(struct dirent * direntry) +{ + struct stat data; + stat(direntry->d_name, &data); /* get stat data */ + shmptr->tot_size += data.st_size; + shmptr->tot_files++; + if (S_ISREG(data.st_mode)) shmptr->tot_regular++; + if (S_ISFIFO(data.st_mode)) shmptr->tot_fifo++; + if (S_ISLNK(data.st_mode)) shmptr->tot_link++; + if (S_ISDIR(data.st_mode)) shmptr->tot_dir; + if (S_ISBLK(data.st_mode)) shmptr->tot_block; + if (S_ISCHR(data.st_mode)) shmptr->tot_char; + if (S_ISSOCK(data.st_mode)) shmptr->tot_sock; + return 0; +} +/* + * Signal Handler to manage termination + */ +void HandSIGTERM(int signo) { + MutexLock(mutex); + if (shmdt(shmptr)) { + perror("Error detaching shared memory"); + exit(1); + } + if (shmctl(shmid, IPC_RMID, NULL)) { + perror("Cannot remove shared memory segment"); + exit(1); + } + MutexRemove(mutex); + exit(0); +} + \end{lstlisting} + \end{minipage} + \normalsize + \caption{Codice delle funzione ausiliarie usate da \file{DirMonitor.c}.} + \label{fig:ipc_dirmonitor_sub} +\end{figure} + +Si noti come per il calcolo dei valori da mantenere nella memoria condivisa si +sia usata ancora una volta la funzione \func{DirScan}, già utilizzata (e +descritta in dettaglio) in \secref{sec:file_dir_read}, che ci permette di +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 +ciascuna voce, per ottenerne i dati, che poi utilizza per incrementare i vari +contatori. + +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 +sono per ciascun tipo. + +In \figref{fig:ipc_dirmonitor_sub} è riportato inoltre (\texttt{\small + 23--35}) il codice del gestore di segnali usato per terminare il programma, +che si incarica di cancellare tutti gli oggetti non più necessari. + +Anzitutto (\texttt{\small 24}) si acquisisce il mutex per evitare di operare +mentre un client sta ancora leggendo i dati, dopo di che si distacca +(\texttt{\small 25--28}) il segmento e lo si cancella (\texttt{\small + 29--32}). Infine (\texttt{\small 33}) si rimuove il mutex e si esce. %% Per capire meglio il funzionamento delle funzioni facciamo ancora una volta @@ -2981,9 +3168,9 @@ potranno accedere per ricavare la parte di informazione che interessa. Come abbiamo detto in \secref{sec:ipc_sysv_generic}, e ripreso nella descrizione dei singoli oggetti che ne fan parte, il \textit{SysV IPC} presenta numerosi problemi; in \cite{APUE}\footnote{in particolare nel - capitolo 14.} Stevens ne effettua una accurata analisi (alcuni dei -concetti sono già stati accennati in precedenza) ed elenca alcune possibili -tecniche alternative, che vogliamo riprendere in questa sezione. + capitolo 14.} Stevens ne effettua una accurata analisi (alcuni dei concetti +sono già stati accennati in precedenza) ed elenca alcune possibili tecniche +alternative, che vogliamo riprendere in questa sezione. \subsection{Alternative alle code di messaggi} @@ -3064,7 +3251,6 @@ int UnlockFile(const char* path_name) { return unlink(path_name); } - \end{lstlisting} \end{minipage} \normalsize @@ -3101,6 +3287,7 @@ pi accedere alla seriale si limita a segnalare che la risorsa non è disponibile.\index{file!di lock|)} + \subsection{La sincronizzazione con il \textit{file locking}} \label{sec:ipc_lock_file} diff --git a/sources/DirMonitor.c b/sources/DirMonitor.c index c8fad95..7e6b288 100644 --- a/sources/DirMonitor.c +++ b/sources/DirMonitor.c @@ -25,7 +25,7 @@ * * Author: S. Piccardi Jan. 2003 * - * $Id: DirMonitor.c,v 1.1 2003/01/03 23:16:05 piccardi Exp $ + * $Id: DirMonitor.c,v 1.2 2003/01/04 17:24:30 piccardi Exp $ * *****************************************************************************/ #include @@ -35,30 +35,48 @@ #include #include "Gapil.h" +#include "macros.h" /* Help printing routine */ void usage(void); /* computation function for DirScan */ int ComputeValues(struct dirent * direntry); +void HandSIGTERM(int signo); -/* global variable for shared memory segment */ -int shmid; /* Shared memory identifier */ +/* global variables for shared memory segment */ +struct DirProp { + int tot_size; + int tot_files; + int tot_regular; + int tot_fifo; + int tot_link; + int tot_dir; + int tot_block; + int tot_char; + int tot_sock; +}; +struct DirProp *shmptr; +int shmid; +int mutex; int main(int argc, char *argv[]) { - int i; - int mutex; + int i, pause = 10; + key_t key; /* * Input section: decode command line parameters * Use getopt function */ opterr = 0; /* don't want writing to stderr */ - while ( (i = getopt(argc, argv, "hs:l:wrbf")) != -1) { + while ( (i = getopt(argc, argv, "hp:")) != -1) { switch (i) { /* * Handling options */ - case 'h': /* help option */ + case 'p': /* set pause (in sec.) */ + pause = strtol(optarg, NULL, 10); + break; + case 'h': /* help option */ printf("Wrong -h option use\n"); usage(); return -1; @@ -81,18 +99,48 @@ int main(int argc, char *argv[]) printf("Wrong number of arguments %d\n", argc - optind); usage(); } - - DirScan(argv[1], ComputeValues); - exit(0); + 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 */ + shmid = shmget(key, 4096, IPC_CREAT|0666); /* get a shared memory */ + if (shmid < 0) { + perror("Cannot create shared memory"); + exit(1); + } + if ( (shmptr = shmat(shmid, NULL, 0)) == NULL ) { /* attach to process */ + perror("Cannot attach segment"); + } + if ((mutex = MutexCreate(key)) == -1) { /* get a Mutex */ + perror("Cannot create mutex"); + exit(1); + } + /* main loop, monitor directory properties each 10 sec */ + while (1) { + MutexLock(mutex); /* lock shared memory */ + memset(shmptr, 0, sizeof(struct DirProp)); /* erase previous data */ + DirScan(argv[1], ComputeValues); /* execute scan */ + MutexUnlock(mutex); /* unlock shared memory */ + sleep(pause); /* sleep until next watch */ + } } /* * Routine to print file name and size inside DirScan */ -int do_ls(struct dirent * direntry) +int ComputeValues(struct dirent * direntry) { struct stat data; stat(direntry->d_name, &data); /* get stat data */ - printf("File: %s \t size: %d\n", direntry->d_name, data.st_size); + shmptr->tot_size += data.st_size; + shmptr->tot_files++; + if (S_ISREG(data.st_mode)) shmptr->tot_regular++; + if (S_ISFIFO(data.st_mode)) shmptr->tot_fifo++; + if (S_ISLNK(data.st_mode)) shmptr->tot_link++; + if (S_ISDIR(data.st_mode)) shmptr->tot_dir; + if (S_ISBLK(data.st_mode)) shmptr->tot_block; + if (S_ISCHR(data.st_mode)) shmptr->tot_char; + if (S_ISSOCK(data.st_mode)) shmptr->tot_sock; return 0; } /* @@ -101,7 +149,25 @@ int do_ls(struct dirent * direntry) void usage(void) { printf("Program myls: list file in a directory \n"); printf("Usage:\n"); - printf(" myls [-h] dirname \n"); + printf(" DirMonitor [-h] [-p sec] dirname \n"); printf(" -h print this help\n"); + printf(" -p sec set watch interval to sec seconds \n"); exit(1); } +/* + * Signal Handler to manage termination + */ +void HandSIGTERM(int signo) { + MutexLock(mutex); + debug("Terminated by %s\n", strsignal(signo)); + if (shmdt(shmptr)) { + perror("Error detaching shared memory"); + exit(1); + } + if (shmctl(shmid, IPC_RMID, NULL)) { + perror("Cannot remove shared memory segment"); + exit(1); + } + MutexRemove(mutex); + exit(0); +} diff --git a/sources/DirScan.c b/sources/DirScan.c index 38fb555..4b3f061 100644 --- a/sources/DirScan.c +++ b/sources/DirScan.c @@ -22,13 +22,15 @@ * * Author: S. Piccardi Jan. 2003 * - * $Id: DirScan.c,v 1.1 2003/01/03 23:16:05 piccardi Exp $ + * $Id: DirScan.c,v 1.2 2003/01/04 17:24:30 piccardi Exp $ * *****************************************************************************/ #include #include #include /* directory */ #include /* C standard library */ +#include +#include /* * Function DirScan: diff --git a/sources/Gapil.h b/sources/Gapil.h index 67dabab..84e7211 100644 --- a/sources/Gapil.h +++ b/sources/Gapil.h @@ -23,7 +23,7 @@ * * Author: S. Piccardi * - * $Id: Gapil.h,v 1.3 2002/12/05 23:38:22 piccardi Exp $ + * $Id: Gapil.h,v 1.4 2003/01/04 17:24:30 piccardi Exp $ * *****************************************************************************/ #include /* IPC semaphore declarations */ @@ -33,6 +33,7 @@ #include /* unix standard functions */ #include /* file control (lock) functions */ #include /* signal handling declarations */ +#include /* directory scan functions */ /* * Definition of semun struct; used to implement a MutexXXXX API To * create a Mutex use an underlaying semaphore and init it; we put @@ -63,6 +64,8 @@ inline int MutexRead(int sem_id); inline int MutexLock(int sem_id); /* Function MutexUnlock: to unlock a mutex/semaphore. See Mutex.c */ inline int MutexUnlock(int sem_id); +/* Function MutexRemove: remove the mutex/semphore. See Mutex.c */ +inline int MutexRemove(int sem_id); /* Function LockMutex: acquire a mutex (using file locking). See Mutex.c */ inline int LockFile(const char* path_name); /* Function UnlockMutex: release a mutex (using file locking). See Mutex.c */ @@ -88,3 +91,8 @@ void HandSigCHLD(int sig); ssize_t SockRead(int fd, void *buf, size_t count); /* Function SockWrite: to read from a socket. See SockWrite.c */ ssize_t SockWrite(int fd, const void *buf, size_t count); +/* + * File miscellaneous + */ +/* Function DirScan: simple scan for a directory */ +int DirScan(char * dirname, int(*compute)(struct dirent *)); diff --git a/sources/Makefile b/sources/Makefile index 1311095..2d33fbb 100644 --- a/sources/Makefile +++ b/sources/Makefile @@ -8,21 +8,27 @@ CFLAGJ= -L./ -lgapil LIB = libgapil.so -OBJ = SockRead.o SockWrite.o SigHand.o Mutex.o SharedMem.o LockFile.o +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 + testren fortune fortuned mqfortune mqfortuned flock myls dirmonitor \ + readmon $(LIB): $(OBJ) gcc -shared $^ -o $@ - $(OBJ): Gapil.h all: $(FINAL) $(LIB) -myls: myls.c DirScan.c - $(CC) $^ -o $@ +dirmonitor: DirMonitor.c + $(CC) $(CFLAGJ) $^ -o $@ + +readmon: ReadMonitor.c + $(CC) $(CFLAGJ) $^ -o $@ + +myls: myls.c + $(CC) $(CFLAGJ) $^ -o $@ flock: Flock.c $(CC) $(CFLAGJ) $^ -o $@ diff --git a/sources/Mutex.c b/sources/Mutex.c index d009998..076c593 100644 --- a/sources/Mutex.c +++ b/sources/Mutex.c @@ -22,7 +22,7 @@ * * Author: S. Piccardi Dec. 2002 * - * $Id: Mutex.c,v 1.3 2002/12/05 23:38:22 piccardi Exp $ + * $Id: Mutex.c,v 1.4 2003/01/04 17:24:30 piccardi Exp $ * *****************************************************************************/ #include /* IPC semaphore declarations */ @@ -65,7 +65,7 @@ int MutexCreate(key_t ipc_key) */ int MutexFind(key_t ipc_key) { - return semget(ipc_key,1,0); + return semget(ipc_key, 1, 0); } /* * Function MutexRead: read the current value of the mutex/semaphore @@ -109,6 +109,16 @@ int MutexUnlock(int sem_id) { return semop(sem_id, &sem_ulock, 1); } +/* + * Function MutexRemove: remove a mutex/semaphore + * + * Input: a semaphore id # + * Return: return code of semctl + */ +int MutexRemove(int sem_id) +{ + return semctl(sem_id, 0, IPC_RMID); +} /***************************************************************************** * * File locking mutex diff --git a/sources/myls.c b/sources/myls.c index bd22144..bc2e1a9 100644 --- a/sources/myls.c +++ b/sources/myls.c @@ -22,7 +22,7 @@ * * Author: S. Piccardi Jan. 2003 * - * $Id: myls.c,v 1.1 2003/01/03 23:16:05 piccardi Exp $ + * $Id: myls.c,v 1.2 2003/01/04 17:24:30 piccardi Exp $ * *****************************************************************************/ #include @@ -31,6 +31,7 @@ #include /* C standard library */ #include +#include "Gapil.h" /* * Program myls * -- 2.30.2