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 2975185b6e64412ae225c8e6cdff4a83ab8cbb71..ca655f9432357b967bc502f1383d0b324bd451d9 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}
 
 \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
 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}
 
 
 \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}
 \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
   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
 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
 
 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.
 
 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}{} 
   \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 
     \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
 \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}
 
 \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}
 
 
 \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
 
 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}
 
 
 \subsection{Code di messaggi}
index 541cd49126711950f28363517fa33b4377335c84..67dabab0f434f6e601cc51b99c3abc7f0dc50ed5 100644 (file)
@@ -23,7 +23,7 @@
  *
  * Author: S. Piccardi
  *
  *
  * 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 */
  *
  *****************************************************************************/
 #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);
 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);
 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
 inline int UnlockFile(const char* path_name);
 /*
  * Signal Handling Functions
index ddbb9b11354447b120502d9f6924db08a8c5e2fc..d0099982305d5d42c0b1c36ac3baead2afac66f1 100644 (file)
@@ -22,7 +22,7 @@
  *
  * Author: S. Piccardi Dec. 2002
  *
  *
  * 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 */
  *
  *****************************************************************************/
 #include <sys/sem.h>     /* IPC semaphore declarations */
@@ -109,3 +109,63 @@ int MutexUnlock(int sem_id)
 {
     return semop(sem_id, &sem_ulock, 1);
 }
 {
     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;
+}