+La funzione sgancia dallo spazio degli indirizzi del processo un segmento di
+memoria condivisa; questo viene identificato con l'indirizzo \param{shmaddr}
+restituito dalla precedente chiamata a \func{shmat} con il quale era stato
+agganciato al processo.
+
+In caso di successo la funzione aggiorna anche i seguenti campi di
+\struct{shmid\_ds}:
+\begin{itemize*}
+\item il tempo \var{shm\_dtime} dell'ultima operazione di sganciamento viene
+ impostato al tempo corrente.
+\item il \acr{pid} \var{shm\_lpid} dell'ultimo processo che ha operato sul
+ segmento viene impostato a quello del processo corrente.
+\item il numero \var{shm\_nattch} di processi agganciati al segmento viene
+ decrementato di uno.
+\end{itemize*}
+inoltre la regione di indirizzi usata per il segmento di memoria condivisa
+viene tolta dallo spazio di indirizzi del processo.
+
+Come esempio di uso di queste funzioni vediamo come implementare una serie di
+funzioni di libreria che ne semplifichino l'uso, automatizzando le operazioni
+più comuni; il codice, contenuto nel file \file{SharedMem.c}, è riportato in
+\figref{fig:ipc_sysv_shm_func}.
+
+\begin{figure}[!bht]
+ \footnotesize \centering
+ \begin{minipage}[c]{15cm}
+ \begin{lstlisting}{}
+/* Function ShmCreate Create a SysV shared memory segment */
+void * ShmCreate(key_t ipc_key, int shm_size, int perm, int fill)
+{
+ void * shm_ptr;
+ int shm_id; /* ID of the IPC shared memory segment */
+ shm_id = shmget(ipc_key, shm_size, IPC_CREAT|perm); /* get shm ID */
+ if (shm_id < 0) {
+ return NULL;
+ }
+ shm_ptr = shmat(shm_id, NULL, 0); /* map it into memory */
+ if (shm_ptr < 0) {
+ return NULL;
+ }
+ memset((void *)shm_ptr, fill, shm_size); /* fill segment */
+ return shm_ptr;
+}
+/* Function ShmFind: Find a SysV shared memory segment */
+void * ShmFind(key_t ipc_key, int shm_size)
+{
+ void * shm_ptr;
+ int shm_id; /* ID of the SysV shared memory segment */
+ shm_id = shmget(ipc_key, shm_size, 0); /* find shared memory ID */
+ if (shm_id < 0) {
+ return NULL;
+ }
+ shm_ptr = shmat(shm_id, NULL, 0); /* map it into memory */
+ if (shm_ptr < 0) {
+ return NULL;
+ }
+ return shm_ptr;
+}
+/* Function ShmRemove: Schedule removal for a SysV shared memory segment */
+int ShmRemove(key_t ipc_key, void * shm_ptr)
+{
+ int shm_id; /* ID of the SysV shared memory segment */
+ /* first detach segment */
+ if (shmdt(shm_ptr) < 0) {
+ return -1;
+ }
+ /* schedule segment removal */
+ shm_id = shmget(ipc_key, 0, 0); /* find shared memory ID */
+ if (shm_id < 0) {
+ if (errno == EIDRM) return 0;
+ return -1;
+ }
+ if (shmctl(shm_id, IPC_RMID, NULL) < 0) { /* ask for removal */
+ if (errno == EIDRM) return 0;
+ return -1;
+ }
+ return 0;
+}
+ \end{lstlisting}
+ \end{minipage}
+ \normalsize
+ \caption{Il codice delle funzioni che permettono di creare, trovare e
+ rimuovere un segmento di memoria condivisa.}
+ \label{fig:ipc_sysv_shm_func}
+\end{figure}
+
+La prima funzione (\texttt{\small 3--16}) è \func{ShmCreate} che, data una
+chiave, crea il segmento di memoria condivisa restituendo il puntatore allo
+stesso. La funzione comincia (\texttt{\small 6}) con il chiamare
+\func{shmget}, usando il flag \const{IPC\_CREATE} per creare il segmento
+qualora non esista, ed assegnandogli i privilegi specificati dall'argomento
+\var{perm} e la dimensione specificata dall'argomento \var{shm\_size}. In
+caso di errore (\texttt{\small 7--9}) si ritorna immediatamente un puntatore
+nullo, altrimenti (\texttt{\small 10}) si prosegue agganciando il segmento di
+memoria condivisa al processo con \func{shmat}. In caso di errore
+(\texttt{\small 11--13}) si restituisce di nuovo un puntatore nullo, infine
+(\texttt{\small 14}) si inizializza con \func{memset} il contenuto del
+segmento al valore costante specificato dall'argomento \var{fill}, e poi si
+ritorna il puntatore al segmento stesso.
+
+La seconda funzione (\texttt{\small 17--31}) è \func{ShmFind}, che, data una
+chiave, restituisce l'indirizzo del segmento ad essa associato. Anzitutto
+(\texttt{\small 22}) si richiede l'identificatore del segmento con
+\func{shmget}, ritornando (\texttt{\small 23--25}) un puntatore nullo in caso
+di errore. Poi si prosegue (\texttt{\small 26}) agganciando il segmento al
+processo con \func{shmat}, restituendo (\texttt{\small 27--29}) di nuovo un
+puntatore nullo in caso di errore, se invece non ci sono errori si restituisce
+il puntatore ottenuto da \func{shmat}.
+
+La terza funzione (\texttt{\small 32--51}) è \func{ShmRemove} che, data la
+chiave ed il puntatore associati al segmento di memoria condivisa, prima lo
+sgancia dal processo e poi lo rimuove. Il primo passo (\texttt{\small 37}) è
+la chiamata a \func{shmdt} per sganciare il segmento, restituendo
+(\texttt{\small 38--39}) un valore -1 in caso di errore. Il passo successivo
+(\texttt{\small 41}) è utilizzare \func{shmget} per ottenre l'identificatore
+associato al segmento data la chiave \var{key}. Al solito si restituisce un
+valore di -1 (\texttt{\small 42--45}) in caso di errore, mentre se tutto va
+bene si conclude restituendo un valore nullo.
+
+Benché la memoria condivisa costituisca il meccanismo di intercomunicazione
+fra processi più veloce, essa non è sempre il più appropriato, dato che, come
+abbiamo visto, si avrà comunque la necessità di una sincronizzazione degli
+accessi. Per questo motivo, quando la comunicazione fra processi è
+sequenziale, altri meccanismi come le pipe, le fifo o i socket, che non
+necessitano di sincronizzazione esplicita, sono da preferire. Essa diventa
+l'unico meccanismo possibile quando la comunicazione non è
+sequenziale\footnote{come accennato in \secref{sec:ipc_sysv_mq} per la
+ comunicazione non sequenziale si possono usare le code di messaggi,
+ attraverso l'uso del campo \var{mtype}, ma solo se quest'ultima può essere
+ effettuata in forma di messaggio.} o quando non può avvenire secondo una
+modalità predefinita.
+
+Un esempio classico di uso della memoria condivisa è quello del
+``\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, 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}{}
+/* 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;
+} *shmptr;
+key_t key;
+int mutex;
+/* main body */
+int main(int argc, char *argv[])
+{
+ int i, pause = 10;
+ ...
+ if ((argc - optind) != 1) { /* There must be remaing parameters */
+ 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);
+ key = ftok("~/gapil/sources/DirMonitor.c", 1); /* define a key */
+ shmptr = ShmCreate(key, 4096, 0666, 0); /* get a shared memory segment */
+ if (!shmptr) {
+ perror("Cannot create shared memory");
+ 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 */
+ 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 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
+aiuto a video ed all'impostazione della durata dell'intervallo con cui viene
+ripetuto il calcolo delle proprietà della directory) controlla (\texttt{\small
+ 20--23}) 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 24--26}) 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 27--29}) 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 30--39}) è quello di creare gli oggetti di
+intercomunicazione necessari. Si inizia costruendo (\texttt{\small 30}) 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 31}) la creazione di un segmento di memoria condivisa
+con usando la funzione \func{ShmCreate} illustrata in precedenza (una pagina
+di memoria è sufficiente per i dati che useremo), uscendo (\texttt{\small
+ 32--35}) qualora la creazione ed il successivo agganciamento al processo 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
+ 36--39}) 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.
+
+Completata l'inizializzazione e la creazione degli oggetti di
+intercomunicazione il programma entra nel ciclo principale (\texttt{\small
+ 40--49}) dove vengono eseguite indefinitamente le attività di monitoraggio.
+Il primo passo (\texttt{\small 41}) è eseguire \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 42--48}) all'interno di un
+ciclo infinito: si inizia (\texttt{\small 43}) 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
+ 44}) si cancellano i valori precedentemente immagazzinati nella memoria
+condivisa con \func{memset}, e si esegue (\texttt{\small 45}) un nuovo calcolo
+degli stessi utilizzando la funzione \func{DirScan}; infine (\texttt{\small
+ 46}) si sblocca il mutex con \func{MutexUnlock}, e si attende
+(\texttt{\small 47}) 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 */
+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);
+ ShmRemove(key, shmptr);
+ 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 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 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} 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 il codice (\texttt{\small
+ 17--23}) 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}) distacca e rimuove il segmento di
+memoria condivisa usando \func{ShmRemove}. Infine (\texttt{\small 21})
+rimuove il mutex con \func{MutexRemove} ed esce (\texttt{\small 22}).
+
+\begin{figure}[!htb]
+ \footnotesize \centering
+ \begin{minipage}[c]{15cm}
+ \begin{lstlisting}{}
+int main(int argc, char *argv[])
+{
+ key_t key;
+ ...
+ /* create needed IPC objects */
+ key = ftok("~/gapil/sources/DirMonitor.c", 1); /* define a key */
+ if (!(shmptr = ShmFind(key, 4096))) { /* get a shared memory segment */
+ perror("Cannot find shared memory");
+ exit(1);
+ }
+ if ((mutex = MutexFind(key)) == -1) { /* get the Mutex */
+ perror("Cannot find mutex");
+ exit(1);
+ }
+ /* main loop */
+ MutexLock(mutex); /* lock shared memory */
+ 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 caratteri\n", shmptr->tot_char);
+ printf("Ci sono %d device a blocchi\n", shmptr->tot_block);
+ printf("Totale %d file, per %d byte\n",
+ shmptr->tot_files, shmptr->tot_size);
+ MutexUnlock(mutex); /* unlock shared memory */
+}
+ \end{lstlisting}
+ \end{minipage}
+ \normalsize
+ \caption{Codice del programma client del monitor delle proprietà di una
+ directory, \file{ReadMonitor.c}.}
+ \label{fig:ipc_dirmonitor_client}
+\end{figure}
+
+Il codice del client usato per 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 conclusa la gestione delle opzioni a riga di comando il programma
+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}) richiede con \func{ShmFind} l'indirizzo della memoria
+condivisa agganciando al contempo il segmento al processo, Infine
+(\texttt{\small 17--20}) con \func{MutexFind} si richiede l'identificatore del
+mutex. Completata l'inizializzazione ed ottenuti i riferimenti agli oggetti
+di intercomunicazione necessari viene eseguito il corpo principale del
+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 libreria 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
+
+------ 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 che, passati nel peggiore dei casi almeno 10 secondi (o
+l'eventuale altro intervallo impostato per la rilettura dei dati) avremo:
+\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}%$
+
+A questo punto possiamo far uscire il server inviandogli un segnale di
+\const{SIGTERM} con il comando \code{killall dirmonitor}, a questo punto
+ripetendo la lettura, otterremo un errore:
+\begin{verbatim}
+[piccardi@gont sources]$ ./readmon
+Cannot find shared memory: No such file or directory
+\end{verbatim}%$
+e inoltre potremo anche verificare che anche gli oggetti di intercomunicazione
+visti in precedenza sono stati regolarmente 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}%$
+
+
+
+%% Per capire meglio il funzionamento delle funzioni facciamo ancora una volta
+%% riferimento alle strutture con cui il kernel implementa i segmenti di memoria
+%% condivisa; uno schema semplificato della struttura è illustrato in
+%% \figref{fig:ipc_shm_struct}.
+
+%% \begin{figure}[htb]
+%% \centering
+%% \includegraphics[width=10cm]{img/shmstruct}
+%% \caption{Schema dell'implementazione dei segmenti di memoria condivisa in
+%% Linux.}
+%% \label{fig:ipc_shm_struct}
+%% \end{figure}
+
+
+
+
+\section{Tecniche alternative}
+\label{sec:ipc_alternatives}
+
+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.
+
+
+\subsection{Alternative alle code di messaggi}
+\label{sec:ipc_mq_alternative}
+
+Le code di messaggi sono probabilmente il meno usato degli oggetti del
+\textit{SysV IPC}; esse infatti nacquero principalmente come meccanismo di
+comunicazione bidirezionale quando ancora le pipe erano unidirezionali; con la
+disponibilità di \func{socketpair} (vedi \secref{sec:ipc_socketpair}) o
+utilizzando una coppia di pipe, si può ottenere questo risultato senza
+incorrere nelle complicazioni introdotte dal \textit{SysV IPC}.
+
+In realtà, grazie alla presenza del campo \var{mtype}, le code di messaggi
+hanno delle caratteristiche ulteriori, consentendo una classificazione dei
+messaggi ed un accesso non rigidamente sequenziale; due caratteristiche che
+sono impossibili da ottenere con le pipe e i socket\index{socket} di
+\func{socketpair}. A queste esigenze però si può comunque ovviare in maniera
+diversa con un uso combinato della memoria condivisa e dei meccanismi di
+sincronizzazione, per cui alla fine l'uso delle code di messaggi classiche è
+relativamente poco diffuso.
+
+\subsection{I \textsl{file di lock}}
+\label{sec:ipc_file_lock}
+
+\index{file!di lock|(}
+Come illustrato in \secref{sec:ipc_sysv_sem} i semafori del \textit{SysV IPC}
+presentano una interfaccia inutilmente complessa e con alcuni difetti
+strutturali, per questo quando si ha una semplice esigenza di sincronizzazione
+per la quale basterebbe un semaforo binario (quello che abbiamo definito come
+\textit{mutex}), per indicare la disponibilità o meno di una risorsa, senza la
+necessità di un contatore come i semafori, si possono utilizzare metodi
+alternativi.
+
+La prima possibilità, utilizzata fin dalle origini di Unix, è quella di usare
+dei \textsl{file di lock} (per i quali esiste anche una opportuna directory,
+\file{/var/lock}, nel filesystem standard). Per questo si usa la
+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\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}
+(sono contenute in \file{LockFile.c}, un'altro dei sorgenti allegati alla
+guida) che permettono rispettivamente di creare e rimuovere un \textsl{file di
+ lock}. Come si può notare entrambe le funzioni sono elementari; la prima
+(\texttt{\small 4--10}) si limita ad aprire il file di lock (\texttt{\small
+ 9}) nella modalità descritta, mentre la seconda (\texttt{\small 11--17}) lo
+cancella con \func{unlink}.
+
+\begin{figure}[!htb]
+ \footnotesize \centering
+ \begin{minipage}[c]{15cm}
+ \begin{lstlisting}{}
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h> /* unix standard functions */
+/*
+ * Function LockFile:
+ */
+int LockFile(const char* path_name)
+{
+ return open(path_name, O_EXCL|O_CREAT);
+}
+/*
+ * Function UnlockFile:
+ */
+int UnlockFile(const char* path_name)
+{
+ return unlink(path_name);
+}
+ \end{lstlisting}
+ \end{minipage}
+ \normalsize
+ \caption{Il codice delle funzioni \func{LockFile} e \func{UnlockFile} che
+ permettono di creare e rimuovere un \textsl{file di lock}.}
+ \label{fig:ipc_file_lock}
+\end{figure}
+
+Uno dei limiti di questa tecnica è che, come abbiamo già accennato in
+\secref{sec:file_open}, questo comportamento di \func{open} può non funzionare
+(la funzione viene eseguita, ma non è garantita l'atomicità dell'operazione)
+se il filesystem su cui si va ad operare è su NFS; in tal caso si può adottare
+una tecnica alternativa che prevede l'uso della \func{link} per creare come
+\textsl{file di lock} un hard link ad un file esistente; se il link esiste già
+e la funzione fallisce, significa che la risorsa è bloccata e potrà essere
+sbloccata solo con un \func{unlink}, altrimenti il link è creato ed il lock
+acquisito; il controllo e l'eventuale acquisizione sono atomici; la soluzione
+funziona anche su NFS, ma ha un'altro difetto è che è quello di poterla usare
+solo se si opera all'interno di uno stesso filesystem.
+
+Un generale comunque l'uso di un \textsl{file di lock} presenta parecchi
+problemi, che non lo rendono una alternativa praticabile per la
+sincronizzazione: anzitutto in caso di terminazione imprevista del processo,
+si lascia allocata la risorsa (il \textsl{file di lock}) e questa deve essere
+sempre cancellata esplicitamente. Inoltre il controllo della disponibilità
+può essere eseguito solo con una tecnica di \textit{polling}\index{polling},
+ed è quindi molto inefficiente.
+
+La tecnica dei file di lock ha comunque una sua utilità, e può essere usata
+con successo quando l'esigenza è solo quella di segnalare l'occupazione di una
+risorsa, senza necessità di attendere che questa si liberi; ad esempio la si
+usa spesso per evitare interferenze sull'uso delle porte seriali da parte di
+più programmi: qualora si trovi un file di lock il programma che cerca di
+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}
+
+Dato che i file di lock\index{file!di lock} presentano gli inconvenienti
+illustrati in precedenza, la tecnica alternativa di sincronizzazione più
+comune è quella di fare ricorso al \textit{file locking}\index{file!locking}
+(trattato in \secref{sec:file_locking}) usando \func{fcntl} su un file creato
+per l'occasione per ottenere un write lock. In questo modo potremo usare il
+lock come un \textit{mutex}: per bloccare la risorsa basterà acquisire il
+lock, per sbloccarla basterà rilasciare il lock. Una richiesta fatta con un
+write lock metterà automaticamente il processo in stato di attesa, senza
+necessità di ricorrere al \textit{polling}\index{polling} per determinare la
+disponibilità della risorsa, e al rilascio della stessa da parte del processo
+che la occupava si otterrà il nuovo lock atomicamente.
+
+Questo approccio presenta il notevole vantaggio che alla terminazione di un
+processo tutti i lock acquisiti vengono rilasciati automaticamente (alla
+chiusura dei relativi file) e non ci si deve preoccupare di niente; inoltre
+non consuma risorse permanentemente allocate nel sistema. Lo svantaggio è che,
+dovendo fare ricorso a delle operazioni sul filesystem, esso è in genere
+leggermente più lento.
+
+\begin{figure}[!htb]
+ \footnotesize \centering
+ \begin{minipage}[c]{15cm}
+ \begin{lstlisting}{}
+/* Function CreateMutex: Create a mutex using file locking. */
+int CreateMutex(const char *path_name)
+{
+ return open(path_name, O_EXCL|O_CREAT);
+}
+/* Function UnlockMutex: unlock a file. */
+int FindMutex(const char *path_name)
+{
+ return open(path_name, O_RDWR);
+}
+/* Function LockMutex: lock mutex using file locking. */
+int LockMutex(int fd)
+{
+ struct flock lock; /* file lock structure */
+ /* set flock structure */
+ lock.l_type = F_WRLCK; /* set type: read or write */
+ lock.l_whence = SEEK_SET; /* start from the beginning of the file */
+ lock.l_start = 0; /* set the start of the locked region */
+ lock.l_len = 0; /* set the length of the locked region */
+ /* do locking */
+ return fcntl(fd, F_SETLKW, &lock);
+}
+/* Function UnlockMutex: unlock a file. */
+int UnlockMutex(int fd)
+{
+ struct flock lock; /* file lock structure */
+ /* set flock structure */
+ lock.l_type = F_UNLCK; /* set type: unlock */
+ lock.l_whence = SEEK_SET; /* start from the beginning of the file */
+ lock.l_start = 0; /* set the start of the locked region */
+ lock.l_len = 0; /* set the length of the locked region */
+ /* do locking */
+ return fcntl(fd, F_SETLK, &lock);
+}
+/* Function RemoveMutex: remove a mutex (unlinking the lock file). */
+int RemoveMutex(const char *path_name)
+{
+ return unlink(path_name);
+}
+/* Function ReadMutex: read a mutex status. */
+int ReadMutex(int fd)
+{
+ int res;
+ struct flock lock; /* file lock structure */
+ /* set flock structure */
+ lock.l_type = F_WRLCK; /* set type: unlock */
+ lock.l_whence = SEEK_SET; /* start from the beginning of the file */
+ lock.l_start = 0; /* set the start of the locked region */
+ lock.l_len = 0; /* set the length of the locked region */
+ /* do locking */
+ if ( (res = fcntl(fd, F_GETLK, &lock)) ) {
+ return res;
+ }
+ return lock.l_type;
+}
+ \end{lstlisting}
+ \end{minipage}
+ \normalsize
+ \caption{Il codice delle funzioni che permettono per la gestione dei
+ \textit{mutex} con il file locking\index{file!locking}.}
+ \label{fig:ipc_flock_mutex}
+\end{figure}
+
+Il codice delle varie funzioni usate per implementare un mutex utilizzando il
+file locking\index{file!locking} è riportato in \figref{fig:ipc_flock_mutex};
+si è mantenuta volutamente una struttura analoga alle precedenti funzioni che
+usano i semafori, anche se le due interfacce non possono essere completamente
+equivalenti, specie per quanto riguarda la rimozione del mutex.
+
+La prima funzione (\texttt{\small 1--5}) è \func{CreateMutex}, e serve a
+creare il mutex; la funzione è estremamente semplice, e si limita
+(\texttt{\small 4}) a creare, con una opportuna chiamata ad \func{open}, il
+file che sarà usato per il successivo file locking, assicurandosi che non
+esista già (nel qual caso segnala un errore); poi restituisce il file
+descriptor che sarà usato dalle altre funzioni per acquisire e rilasciare il
+mutex.
+
+La seconda funzione (\texttt{\small 6--10}) è \func{FindMutex}, che, come la
+precedente, è stata definita per mantenere una analogia con la corrispondente
+funzione basata sui semafori. Anch'essa si limita (\texttt{\small 9}) ad
+aprire il file da usare per il file locking, solo che in questo caso le
+opzioni di \func{open} sono tali che il file in questione deve esistere di
+già.
+
+La terza funzione (\texttt{\small 11--22}) è \func{LockMutex} e serve per
+acquisire il mutex. La funzione definisce (\texttt{\small 14}) e inizializza
+(\texttt{\small 16--19}) la struttura \var{lock} da usare per acquisire un
+write lock sul file, che poi (\texttt{\small 21}) viene richiesto con
+\func{fcntl}, restituendo il valore di ritorno di quest'ultima. Se il file è
+libero il lock viene acquisito e la funzione ritorna immediatamente;
+altrimenti \func{fcntl} si bloccherà (si noti che la si è chiamata con
+\func{F\_SETLKW}) fino al rilascio del lock.
+
+La quarta funzione (\texttt{\small 24--34}) è \func{UnlockMutex} e serve a
+rilasciare il mutex. La funzione è analoga alla precedente, solo che in questo
+caso si inizializza (\texttt{\small 28--31}) la struttura \var{lock} per il
+rilascio del lock, che viene effettuato (\texttt{\small 33}) con la opportuna
+chiamata a \func{fcntl}. Avendo usato il file locking in semantica POSIX (si
+riveda quanto detto \secref{sec:file_posix_lock}) solo il processo che ha
+precedentemente eseguito il lock può sbloccare il mutex.
+
+La quinta funzione (\texttt{\small 36--39}) è \func{RemoveMutex} e serve a
+cancellare il mutex. Anche questa funzione è stata definita per mantenere una
+analogia con le funzioni basate sui semafori, e si limita a cancellare
+(\texttt{\small 38}) il file con una chiamata ad \func{unlink}. Si noti che in
+questo caso la funzione non ha effetto sui mutex già ottenuti con precedenti
+chiamate a \func{FindMutex} o \func{CreateMutex}, che continueranno ad essere
+disponibili fintanto che i relativi file descriptor restano aperti. Pertanto
+per rilasciare un mutex occorrerà prima chiamare \func{UnlockMutex} oppure
+chiudere il file usato per il lock.
+
+La sesta funzione (\texttt{\small 41--55}) è \func{ReadMutex} e serve a
+leggere lo stato del mutex. In questo caso si prepara (\texttt{\small 46--49})
+la solita struttura \var{lock} come l'acquisizione del lock, ma si effettua
+(\texttt{\small 51}) la chiamata a \func{fcntl} usando il comando
+\const{F\_GETLK} per ottenere lo stato del lock, e si restituisce
+(\texttt{\small 52}) il valore di ritorno in caso di errore, ed il valore del
+campo \var{l\_type} (che descrive lo stato del lock) altrimenti
+(\texttt{\small 54}). Per questo motivo la funzione restituirà -1 in caso di
+errore e uno dei due valori \const{F\_UNLCK} o \const{F\_WRLCK}\footnote{non
+ si dovrebbe mai avere il 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, ad indicare che il mutex è, rispettivamente, libero o occupato.
+
+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 ad esempio che, al contrario di
+quanto avveniva con l'interfaccia basata sui semafori, chiamate multiple a
+\func{UnlockMutex} o \func{LockMutex} non si cumulano e non danno perciò
+nessun inconveniente.
+
+
+\subsection{Il \textit{memory mapping} anonimo}
+\label{sec:ipc_mmap_anonymous}
+
+Abbiamo già visto che quando i processi sono \textsl{correlati}\footnote{se
+ cioè hanno almeno un progenitore comune.} l'uso delle pipe può costituire
+una valida alternativa alle code di messaggi; nella stessa situazione si può
+evitare l'uso di una memoria condivisa facendo ricorso al cosiddetto
+\textit{memory mapping} anonimo.
+
+In \secref{sec:file_memory_map} abbiamo visto come sia possibile mappare il
+contenuto di un file nella memoria di un processo, e che, quando viene usato
+il flag \const{MAP\_SHARED}, le modifiche effettuate al contenuto del file
+vengono viste da tutti i processi che lo hanno mappato. Utilizzare questa
+tecnica per creare una memoria condivisa fra processi diversi è estremamente
+inefficiente, in quanto occorre passare attraverso il disco. Però abbiamo
+visto anche che se si esegue la mappatura con il flag \const{MAP\_ANONYMOUS}
+la regione mappata non viene associata a nessun file, anche se quanto scritto
+rimane in memoria e può essere riletto; allora, dato che un processo figlio
+mantiene nel suo spazio degli indirizzi anche le regioni mappate, esso sarà
+anche in grado di accedere a quanto in esse è contenuto.
+
+In questo modo diventa possibile creare una memoria condivisa fra processi
+diversi, purché questi abbiano almeno un progenitore comune che ha effettuato
+il \textit{memory mapping} anonimo.\footnote{nei sistemi derivati da SysV una
+ funzionalità simile a questa viene implementata mappando il file speciale
+ \file{/dev/zero}. In tal caso i valori scritti nella regione mappata non
+ vengono ignorati (come accade qualora si scriva direttamente sul file), ma
+ restano in memoria e possono essere riletti secondo le stesse modalità usate
+ nel \textit{memory mapping} anonimo.} Vedremo come utilizzare questa tecnica
+più avanti, quando realizzeremo una nuova versione del monitor visto in
+\secref{sec:ipc_sysv_shm} che possa restituisca i risultati via rete.