Shared memory, esempio di LockFile, di Mutex con il file locking,
authorSimone Piccardi <piccardi@gnulinux.it>
Thu, 5 Dec 2002 23:38:22 +0000 (23:38 +0000)
committerSimone Piccardi <piccardi@gnulinux.it>
Thu, 5 Dec 2002 23:38:22 +0000 (23:38 +0000)
spiegazioni sul memory mapping anonimo e aggiunta delle relative funzioni al
codice.

ipc.tex
sources/Gapil.h
sources/Mutex.c

diff --git a/ipc.tex b/ipc.tex
index 2975185..ca655f9 100644 (file)
--- a/ipc.tex
+++ b/ipc.tex
@@ -2951,11 +2951,12 @@ viene tolta dallo spazio di indirizzi del processo.
 \section{Tecniche alternative}
 \label{sec:ipc_alternatives}
 
-Come abbiamo visto in \secref{sec:ipc_sysv_generic} il \textit{SysV IPC}
+Come abbiamo detto in \secref{sec:ipc_sysv_generic}, e ripreso nella
+descrizione dei signoli oggetti che ne fan parte, il \textit{SysV IPC}
 presenta numerosi problemi; in \cite{APUE}\footnote{in particolare nel
-  capitolo 14.}  Stevens effettua una accurata analisi (alcuni dei concetti
-sono già stati accennati in precedenza) ed elenca alcune possibili
-alternative, che vogliamo riprendere in questa sezione.
+  capitolo 14.}  Stevens ne eeffettua 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}
@@ -2978,7 +2979,7 @@ cui alla fine l'uso delle code di messaggi classiche 
 
 
 
-\subsection{La sincronizzazione con il \textit{file locking}}
+\subsection{I \textsl{file di lock}}
 \label{sec:ipc_file_lock}
 
 Come illustrato in \secref{sec:ipc_sysv_sem} i semafori del \textit{SysV IPC}
@@ -2998,36 +2999,79 @@ usa la caratteristica della funzione \func{open} (illustrata in
   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 \macro{O\_CREAT} e
-\macro{O\_EXCL}. In tal modo la creazione di un file di lock può essere
-eseguita atomicamente, il processo che crea il file con successo si può
+\macro{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}.\footnote{abbiamo già accennato in \secref{sec:file_open} che
-  questa tecnica può non funzionare se il filesystem su cui si va ad operare è
-  su NFS; in tal caso si può adottare una tecnica alternativa che prevede
-  l'uso di \func{link} per creare come 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; il difetto di questa soluzione è che funziona
-  solo se si opera all'interno di uno stesso filesystem.}
-
-L'uso di un file di lock presenta però parecchi problemi, che non lo rendono
-una alternativa praticabile per la sincronizzazione: anzitutto anche in questo
-caso in caso di terminazione imprevista del processo lascia allocata la
-risorsa (il file di lock) e questa deve essere sempre cancellata
-esplicitamente.  Inoltre il controllo della disponibilità può essere fatto
-solo con una tecnica di \textit{polling}\index{polling}, che è molto
-inefficiente.
-
-La tecnica può comunque 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; in \file{LockFile.c} (un'altro dei
-sorgenti allegati alla guida) si sono predisposte due funzioni,
-\func{LockFile} e \func{UnlockFile}, da utilizzare allo scopo.
+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
+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 anche in questo caso, in caso di terminazione
+imprevista del processo, si lascia allocata la risorsa (il 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 non di meno ha 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.
+
+\subsection{La sincronizzazione con il \textit{file locking}}
+\label{sec:ipc_lock_file}
 
 Dato che i file di lock presentano gli inconvenienti illustrati in precedenza,
 la tecnica alternativa più comune è quella di fare ricorso al \textit{file
@@ -3047,11 +3091,54 @@ non consuma risorse permanentemente allocate nel sistema, lo svantaggio 
 dovendo fare ricorso a delle operazioni sul filesystem esso è in genere
 leggermente più lento.
 
-\begin{figure}[!bht]
+\begin{figure}[!htb]
   \footnotesize \centering
   \begin{minipage}[c]{15cm}
     \begin{lstlisting}{} 
-
+/*
+ * Function LockMutex: lock a file (creating it if not existent).  
+ */
+int LockMutex(const char *path_name)
+{
+    int fd, res;
+    struct flock lock;                                /* file lock structure */
+    /* first open the file (creating it if not existent) */
+    if ( (fd = open(path_name, O_EXCL|O_CREAT)) < 0) {    /* first open file */
+        return fd;
+    }
+    /* 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 */
+    if ( (res = fcntl(fd, F_SETLKW, &lock)) < 0 ) {
+        return res;
+    }
+    return 0;
+}
+/*
+ * Function UnLockMutex: unlock a file.  
+ */
+int UnlockMutex(const char *path_name)
+{
+    int fd, res;
+    struct flock lock;                                /* file lock structure */
+    /* first open the file */
+    if ( (fd = open(path_name, O_RDWR)) < 0) {            /* first open file */
+        return fd;
+    }
+    /* 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 */
+    if ( (res = fcntl(fd, F_SETLK, &lock)) < 0 ) {
+        return res;
+    }
+    return 0;
+}
     \end{lstlisting}
   \end{minipage} 
   \normalsize 
@@ -3061,21 +3148,60 @@ leggermente pi
 \end{figure}
 
 Il codice per implementare un mutex utilizzando il file locking è riportato in
-\figref{fig:ipc_flock_mutex}; come nel precedente caso dei mutex implementato
-con i semafori le funzioni sono tre
-
-
-
-
-\subsection{Il \textit{memory mapping} anonimo}
+\figref{fig:ipc_flock_mutex}; a differenza del precedente caso in cui si sono
+usati i semafori le funzioni questa volta sono sufficienti due funzioni,
+\func{LockMutex} e \func{UnlockMutex}, usate rispettivamente per acquisire e
+rilasciare il mutex.
+
+La prima funzione (\texttt{\small 1--22}) serve per acquisire il mutex.
+Anzitutto si apre (\texttt{\small 9--11}), creandolo se non esiste, il file
+specificato dall'argomento \param{pathname}. In caso di errore si ritorna
+immediatamente, altrimenti si prosegue impostando (\texttt{\small 12--16}) la
+struttura \var{lock} in modo da poter acquisire un write lock sul file. Infine
+si richiede (\texttt{\small 17--20}) il file lock (restituendo il codice di
+ritorno di \func{fcntl} caso di errore). Se il file è libero il lock è
+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 seconda funzione (\texttt{\small 23--44}) serve a rilasciare il mutex. Di
+nuovo si apre (\texttt{\small 30--33}) il file specificato dall'argomento
+\param{pathname} (che stavolta deve esistere), ritornando immediatamente in
+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}
 \label{sec:ipc_mmap_anonymous}
 
-Abbiamo visto in \secref{sec:file_memory_map} come sia possibile mappare il
-contenuto di un file nella memoria di un processo. Una della opzioni possibili
-utilizzabili con Linux è quella del \textit{memory mapping}
-anonimo\footnote{in altri sistemi una funzionalità simile a questa viene
-  implementata mappando il file speciale \file{/dev/zero}.}, in tal caso
-infatti
+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.
+
+Abbiamo visto in \secref{sec:file_memory_map} che è possibile mappare il
+contenuto di un file nella memoria di un processo, e che, quando viene usato
+il flag \macro{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 \macro{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
+  nele \textit{memory mapping} anonimo.} Un esempio di utilizzo di questa
+tecnica è mostrato in 
+
 
 
 \section{La comunicazione fra processi di POSIX}
@@ -3094,9 +3220,9 @@ una interfaccia completamente nuova, che tratteremo in questa sezione.
 
 Il Linux non tutti gli oggetti del POSIX IPC sono supportati nel kernel
 ufficiale; solo la memoria condivisa è presente, ma solo a partire dal kernel
-2.4.x, per gli altri oggetti esistono patch e librerie non
-ufficiali. Nonostante questo è importante esaminare questa interfaccia per la
-sua netta superiorità nei confronti di quella del \textit{SysV IPC}.
+2.4.x, per gli altri oggetti esistono patch e librerie non ufficiali.
+Nonostante questo è importante esaminare questa interfaccia per la sua netta
+superiorità nei confronti di quella del \textit{SysV IPC}.
 
 
 \subsection{Code di messaggi}
index 541cd49..67dabab 100644 (file)
@@ -23,7 +23,7 @@
  *
  * Author: S. Piccardi
  *
- * $Id: Gapil.h,v 1.2 2002/12/03 22:30:11 piccardi Exp $
+ * $Id: Gapil.h,v 1.3 2002/12/05 23:38:22 piccardi Exp $
  *
  *****************************************************************************/
 #include <sys/sem.h>                           /* IPC semaphore declarations */
@@ -63,10 +63,15 @@ 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 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 */
+inline int UnlockFile(const char* path_name);
+
 
-/* Function LockFile: create a lock file. See FileLock.c*/
+/* Function LockFile: create a lock file. See FileLock.c */
 inline int LockFile(const char* path_name);
-/* Function UnLockFile: remove a lock file. See FileLock.c*/
+/* Function UnlockFile: remove a lock file. See FileLock.c */
 inline int UnlockFile(const char* path_name);
 /*
  * Signal Handling Functions
index ddbb9b1..d009998 100644 (file)
@@ -22,7 +22,7 @@
  *
  * Author: S. Piccardi Dec. 2002
  *
- * $Id: Mutex.c,v 1.2 2002/12/03 22:30:11 piccardi Exp $
+ * $Id: Mutex.c,v 1.3 2002/12/05 23:38:22 piccardi Exp $
  *
  *****************************************************************************/
 #include <sys/sem.h>     /* IPC semaphore declarations */
@@ -109,3 +109,63 @@ int MutexUnlock(int sem_id)
 {
     return semop(sem_id, &sem_ulock, 1);
 }
+/***************************************************************************** 
+ *
+ * File locking mutex 
+ *
+ * Create a mutex usinf file locking. Use file locking to lock a file
+ * as a mutex request, and unlock it as a mutex release.
+ *
+ * Author: S. Piccardi Dec. 2002
+ *
+ *****************************************************************************/
+/*
+ * Function LockMutex: lock a file (creating it if not existent).  
+ *
+ * Input:  a filename
+ * Output: a return code  (0 OK, -1 KO)
+ */
+int LockMutex(const char *path_name)
+{
+    int fd, res;
+    struct flock lock;                                /* file lock structure */
+    /* first open the file (creating it if not existent) */
+    if ( (fd = open(path_name, O_EXCL|O_CREAT)) < 0) {    /* first open file */
+       return fd;
+    }
+    /* 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 */
+    if ( (res = fcntl(fd, F_SETLKW, &lock)) < 0 ) {
+       return res;
+    }
+    return 0;
+}
+/*
+ * Function UnlockMutex: unlock a file.  
+ *
+ * Input:  a filename
+ * Output: a return code  (0 OK, -1 KO)
+ */
+int UnlockMutex(const char *path_name)
+{
+    int fd, res;
+    struct flock lock;                                /* file lock structure */
+    /* first open the file */
+    if ( (fd = open(path_name, O_RDWR)) < 0) {            /* first open file */
+       return fd;
+    }
+    /* 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 */
+    if ( (res = fcntl(fd, F_SETLK, &lock)) < 0 ) {
+       return res;
+    }
+    return 0;
+}