X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=ipc.tex;h=61eb8a076585b97d43cce044756ee03c90dfdbe4;hp=977f9f009beac97df2e29d5fdd1a8724472dc601;hb=305928c5c349fa8fd0131dcf5a095e98c610e6bb;hpb=9b0f636834e8d92e87131bbb99d7d846bab1421a diff --git a/ipc.tex b/ipc.tex index 977f9f0..61eb8a0 100644 --- a/ipc.tex +++ b/ipc.tex @@ -2940,23 +2940,286 @@ 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 (utilizzando le funzioni di +intrfaccia già descritte in \secref{sec:ipc_sysv_sem}) 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 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}. + +\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 */ + shmid = shmget(key, 4096, 0); /* get the shared memory ID */ + if (shmid < 0) { + perror("Cannot find shared memory"); + exit(1); + } + if ( (shmptr = shmat(shmid, NULL, 0)) == NULL ) { /* attach to process */ + perror("Cannot attach segment"); + exit(1); + } + if ((mutex = MutexFind(key)) == -1) { /* get the Mutex */ + perror("Cannot find mutex"); + exit(1); + } + /* main loop */ + MutexLock(mutex); /* lock shared memory */ + printf("File presenti %d file, per un totale di %d byte\n", + shmptr->tot_files, shmptr->tot_size); + printf("Ci sono %d file dati\n", shmptr->tot_regular); + printf("Ci sono %d directory\n", shmptr->tot_dir); + printf("Ci sono %d link\n", shmptr->tot_link); + printf("Ci sono %d fifo\n", shmptr->tot_fifo); + printf("Ci sono %d socket\n", shmptr->tot_sock); + printf("Ci sono %d device a carrateri\n", shmptr->tot_char); + printf("Ci sono %d device a blocchi\n", shmptr->tot_block); + MutexUnlock(mutex); /* unlock shared memory */ +} + \end{lstlisting} + \end{minipage} + \normalsize + \caption{Codice del programma client \file{ReadMonitor.c}.} + \label{fig:ipc_dirmonitor_client} +\end{figure} + +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 +richede pure 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. %% Per capire meglio il funzionamento delle funzioni facciamo ancora una volta @@ -2981,9 +3244,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} @@ -3026,12 +3289,13 @@ caratteristica della funzione \func{open} (illustrata in \secref{sec:file_open}) che prevede\footnote{questo è quanto dettato dallo standard POSIX.1, ciò non toglie che in alcune implementazioni questa tecnica possa non funzionare; in particolare per Linux, nel caso di NFS, si - è comunque soggetti alla possibilità di una race condition.} che essa -ritorni un errore quando usata con i flag di \const{O\_CREAT} e -\const{O\_EXCL}. In tal modo la creazione di un \textsl{file di lock} può -essere eseguita atomicamente, il processo che crea il file con successo si può -considerare come titolare del lock (e della risorsa ad esso associata) mentre -il rilascio si può eseguire con una chiamata ad \func{unlink}. + è comunque soggetti alla possibilità di una race + condition\index{race condition}.} che essa ritorni un errore quando usata +con i flag di \const{O\_CREAT} e \const{O\_EXCL}. In tal modo la creazione di +un \textsl{file di lock} può essere eseguita atomicamente, il processo che +crea il file con successo si può considerare come titolare del lock (e della +risorsa ad esso associata) mentre il rilascio si può eseguire con una chiamata +ad \func{unlink}. Un esempio dell'uso di questa funzione è mostrato dalle funzioni \func{LockFile} ed \func{UnlockFile} riportate in \figref{fig:ipc_file_lock} @@ -3063,7 +3327,6 @@ int UnlockFile(const char* path_name) { return unlink(path_name); } - \end{lstlisting} \end{minipage} \normalsize @@ -3100,6 +3363,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} @@ -3202,7 +3466,8 @@ caso di errore. Poi si passa ad inizializzare (\texttt{\small 34--38}) la struttura \var{lock} per il rilascio del lock, che viene effettuato (\texttt{\small 39--42}) subito dopo. - \subsection{Il \textit{memory mapping} anonimo} + +\subsection{Il \textit{memory mapping} anonimo} \label{sec:ipc_mmap_anonymous} Abbiamo già visto che quando i processi sono \textsl{correlati}\footnote{se