Aggiunte e correzioni alle funzioni per i file di lock
[gapil.git] / ipc.tex
diff --git a/ipc.tex b/ipc.tex
index ee3270c3e067e7f67e2c65e8a1fc90b67ba962ed..7bf418608b81f17be4eaf00503d9878574f86576 100644 (file)
--- a/ipc.tex
+++ b/ipc.tex
@@ -882,7 +882,7 @@ programmazione, che fossero in grado di garantire una maggiore flessibilit
 In questa sezione esamineremo come Linux supporta quello che viene chiamato il
 \textsl{Sistema di comunicazione inter-processo} di System V, cui da qui in
 avanti faremo riferimento come \textit{SysV IPC} (dove IPC è la sigla di
-\textit{Inter-Process Comunication})}.
+\textit{Inter-Process Comunication}).
 
 
 
@@ -2439,6 +2439,152 @@ effettuare subito le operazioni che non prevedono un blocco del processo e di
 ignorare silenziosamente le altre; questo però comporta il fatto che il
 ripristino non è comunque garantito in tutte le occasioni.
 
+Come esempio di uso dell'interfaccia dei semafori vediamo come implementare
+con essa dei semplici \textit{mutex} (cioè semafori binari), tutto il codice
+in questione, contenuto nel file \file{wrappers.h} allegato ai sorgenti, è
+riportato in \figref{fig:ipc_mutex_create}. Utilizzeremo l'interfaccia per
+creare un insieme contenente un singolo semaforo, per il quale poi useremo un
+valore unitario per segnalare la disponibilità della risorsa, ed un valore
+nullo per segnalarne l'indisponibilità. 
+
+\begin{figure}[!bht]
+  \footnotesize \centering
+  \begin{minipage}[c]{15cm}
+    \begin{lstlisting}{} 
+/*
+ * Function MutexCreate: create a mutex/semaphore
+ */
+inline int MutexCreate(key_t ipc_key) 
+{
+    const union semun semunion={1};             /* semaphore union structure */
+    int sem_id, ret;
+    sem_id = semget(ipc_key, 1, IPC_CREAT|0666);         /* get semaphore ID */
+    if (sem_id == -1) {                              /* if error return code */
+        return sem_id;
+    }
+    ret = semctl(sem_id, 0, SETVAL, semunion);             /* init semaphore */
+    if (ret == -1) {
+        return ret;
+    }
+    return sem_id;
+}
+/*
+ * Function MutexFind: get the semaphore/mutex Id given the IPC key value
+ */
+inline int MutexFind(key_t ipc_key) 
+{
+    return semget(ipc_key,1,0);
+}
+/*
+ * Function MutexRead: read the current value of the mutex/semaphore
+ */
+inline int MutexRead(int sem_id) 
+{
+    return semctl(sem_id, 0, GETVAL);
+}
+/*
+ * Define sembuf structures to lock and unlock the semaphore 
+ */
+struct sembuf sem_lock={                                /* to lock semaphore */
+    0,                                   /* semaphore number (only one so 0) */
+    -1,                                    /* operation (-1 to use resource) */
+    SEM_UNDO};                                /* flag (set for undo at exit) */
+struct sembuf sem_ulock={                             /* to unlock semaphore */
+    0,                                   /* semaphore number (only one so 0) */
+    1,                                  /* operation (1 to release resource) */
+    SEM_UNO};                                       /* flag (in this case 0) */
+/*
+ * Function MutexLock: to lock a mutex/semaphore
+ */
+inline int MutexLock(int sem_id) 
+{
+    return semop(sem_id, &sem_lock, 1);
+}
+/*
+ * Function MutexUnlock: to unlock a mutex/semaphore
+ */
+inline int MutexUnlock(int sem_id) 
+{
+    return semop(sem_id, &sem_ulock, 1);
+}
+    \end{lstlisting}
+  \end{minipage} 
+  \normalsize 
+  \caption{Il codice delle funzioni che permettono di creare o recuperare
+    l'identificatore di un semaforo da utilizzare come \textit{mutex}.}
+  \label{fig:ipc_mutex_create}
+\end{figure}
+
+La prima funzione (\texttt{\small 1--17}) è \func{MutexCreate} che data una
+chiave crea il semaforo usato per il mutex e lo inizializza, restituendone
+l'identificatore. Il primo passo (\texttt{\small 8}) è chiamare \func{semget}
+con \macro{IPC\_CREATE} per creare il semaforo qualora non esista,
+assegnandogli i privilegi di lettura e scrittura per tutti. In caso di errore
+(\texttt{\small 9--11}) si ritorna subito il risultato di \func{semget},
+altrimenti (\texttt{\small 12}) si inizializza il semaforo chiamando
+\func{semctl} con il comando \macro{SETVAL}, utilizzando l'unione
+\var{semunion} dichiarata ed avvalorata in precedenza (\texttt{\small 6}) ad 1
+per significare che risorsa è libera. In caso di errore (\texttt{\small
+  13--16}) si restituisce il valore di ritorno di \func{semctl}, altrimenti si
+ritorna l'identificatore del semaforo.
+
+La seconda funzione (\texttt{\small 18--24}) è \func{MutexFind}, che data una
+chiave, restituisce l'identificatore del semaforo ad essa associato. La
+comprensione del suo funzionamento è immediata in quanto è solo un
+\textit{wrapper}\footnote{si chiama così una funzione usata per fare da
+  \textsl{involucro} alla chiamata di un altra, usata in genere per
+  semplificare un'interfaccia (come in questo caso) o per utilizzare con la
+  stessa funzione diversi substrati (librerie, ecc.)  che possono fornire le
+  stesse funzionalità.} di \func{semget} per cercare l'identificatore
+associato alla chiave, restituendo direttamente il valore di ritorno della
+funzione.
+
+La terza funzione (\texttt{\small 25--31}) è \func{MutexRead} che, dato
+l'identificatore, restituisce il valore del mutex. Anche in questo caso la
+funzione è un \textit{wrapper} per la chiamata di \func{semctl}, questa volta
+con il comando \macro{GETVAL}, che permette di restituire il valore del
+semaforo.
+
+La quarta e la quinta funzione (\texttt{\small 43--56}) sono \func{MutexLock},
+e \func{MutexUnlock}, che permettono rispettivamente di bloccare e sbloccare
+il mutex. Entrambe fanno da wrapper per \func{semop}, utilizzando le due
+strutture \var{sem\_lock} e \var{sem\_unlock} definite in precedenza
+(\texttt{\small 32--42}). Si noti come per queste ultime si sia fatto uso
+dell'opzione \macro{SEM\_UNDO} per evitare che il semaforo resti bloccato in
+caso di terminazione imprevista del processo. Si noti infine come, essendo
+tutte le funzioni riportate in \figref{fig:ipc_mutex_create} estremamente
+semplici, se si sono definite tutte come \ctyp{inline}.\footnote{la direttiva
+  \func{inline} viene usata per dire al compilatore di non trattare la
+  funzione cui essa fa riferimento come una funzione, ma di inserire il codice
+  direttamente nel testo del programma.  Anche se i compilatori più moderni
+  sono in grado di effettuare da soli queste manipolazioni (impostando le
+  opportune ottimizzazioni) questa è una tecnica usata per migliorare le
+  prestazioni per le funzioni piccole ed usate di frequente, in tal caso
+  infatti le istruzioni per creare un nuovo frame nello stack per chiamare la
+  funzione costituirebbero una parte rilevante del codice, appesantendo
+  inutilmente il programma. Originariamente questa era fatto utilizzando delle
+  macro, ma queste hanno tutta una serie di problemi di sintassi nel passaggio
+  degli argomenti (si veda ad esempio \cite{PratC} che in questo modo possono
+  essere evitati.}
+
+
+Chiamare \func{MutexLock} decrementa il valore del semaforo: se questo è
+libero (ha già valore 1) sarà bloccato (valore nullo), se è bloccato la
+chiamata a \func{semop} si bloccherà fintanto che la risorsa non venga
+rilasciata. Chiamando \func{MutexUnlock} il valore del semaforo sarà
+incrementato di uno, sbloccandolo qualora fosse bloccato.  Si noti che occorre
+eseguire sempre prima \func{MutexLock} e poi \func{MutexUnlock}, perché se per
+un qualche errore si esegue più volte quest'ultima il valore del semaforo
+crescerebbe oltre 1, e \func{MutexLock} non avrebbe più l'effetto aspettato
+(bloccare la risorsa quando questa è considerata libera). Si tenga presente
+che usare \func{MutexRead} per controllare il valore dei mutex prima di
+proseguire non servirebbe comunque, dato che l'operazione non sarebbe atomica.
+Vedremo in \secref{sec:ipc_posix_sem} come è possibile ottenere un'interfaccia
+analoga senza questo problemi usando il file locking.
+
+
+
+
 
 \subsection{Memoria condivisa}
 \label{sec:ipc_sysv_shm}
@@ -2494,7 +2640,7 @@ lettura si deve essere sicuri che i dati restano coerenti e non vengono
 sovrascritti da un accesso in scrittura sullo stesso segmento da parte di un
 altro processo; per questo in genere la memoria condivisa viene sempre
 utilizzata in abbinamento ad un meccanismo di sincronizzazione, il che, di
-norma, significa insime a dei semafori.
+norma, significa insieme a dei semafori.
 
 \begin{figure}[!htb]
   \footnotesize \centering
@@ -2531,7 +2677,7 @@ invece:
   inizializzato al valore di \param{size}.
 \item il campo \var{shm\_ctime}, che esprime il tempo di creazione del
   segmento, viene inizializzato al tempo corrente.
-\item i campi \var{shm\_atime} e \var{shm\_atime}, che esprimno
+\item i campi \var{shm\_atime} e \var{shm\_atime}, che esprimono
   rispettivamente il tempo dell'ultima volta che il segmento è stato
   agganciato o sganciato da un processo, vengono inizializzati a zero.
 \item il campo \var{shm\_lpid}, che esprime il \acr{pid} del processo che ha
@@ -2711,7 +2857,7 @@ In caso di successo la funzione aggiorna anche i seguenti campi di
 \end{itemize*} 
 
 Come accennato in \secref{sec:proc_fork} un segmento di memoria condivisa
-agganciato ad un precesso viene ereditato da un figlio attraverso una
+agganciato ad un processo viene ereditato da un figlio attraverso una
 \func{fork}, dato che quest'ultimo riceve una copia dello spazio degli
 indirizzi del padre. Invece, dato che attraverso una \func{exec} viene
 eseguito un diverso programma con uno spazio di indirizzi completamente
@@ -2720,9 +2866,9 @@ automaticamente sganciati. Lo stesso avviene all'uscita del processo
 attraverso una \func{exit}.
 
 
-Una volta che un segmento di memoria condivisa non serve più si può sganciarlo
-esplicitamente dal processo usando la seconda funzione dell'interfaccia,
-\func{shmdt}, il cui  il suo prototipo è:
+Una volta che un segmento di memoria condivisa non serve più, si può
+sganciarlo esplicitamente dal processo usando l'altra funzione
+dell'interfaccia, \func{shmdt}, il cui prototipo è:
 \begin{functions}
   \headdecl{sys/types.h} 
   \headdecl{sys/shm.h}
@@ -2730,7 +2876,7 @@ esplicitamente dal processo usando la seconda funzione dell'interfaccia,
   \funcdecl{int shmdt(const void *shmaddr)}
   Sgancia dal processo un segmento di memoria condivisa.
   
-  \bodydesc{La funzione restituisce 0 in caso di succes so, e -1 in caso di
+  \bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di
     errore, la funzione fallisce solo quando non c'è un segmento agganciato
     all'indirizzo \func{shmaddr}, con \var{errno} che assume il valore
     \macro{EINVAL}.}
@@ -2741,7 +2887,6 @@ 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.
 
-
 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
@@ -2780,13 +2925,14 @@ 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 di \func{socketpair};
-queste esigenze però si può comunque ovviare in maniera diversa con un uso
+messaggi ed un accesso non rigidamente sequenziale; due caratteristiche che
+sono impossibili da ottenere con le pipe e i 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 è poco diffuso.
 
 
+
 \subsection{La sincronizzazione con il \textit{file locking}}
 \label{sec:ipc_file_lock}
 
@@ -2794,14 +2940,14 @@ 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}, che indica la disponibilità o meno di una risorsa, e non ha
-associato un contatore come i semafori) si possono utilizzare metodi
+\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
+dei \textsl{file di lock}\index{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
@@ -2810,62 +2956,132 @@ 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ò
 considerare come titolare del lock (e della risorsa ad esso associata) mentre
-il rilascio si può eseguire con una chiamata ad \func{unlink}.
-
-Questa tecnica ha però parecchi problemi, che non la rendono una alternativa
-praticabile; anzitutto anche in questo caso in caso di terminazione imprevista
-del processo lascia allocata la risorsa (il file di lock) che deve comunque
-essere cancellata esplicitamente. Inoltre il controllo della disponibilità può
-essere fatto solo con una tecnica di polling, che è molto inefficiente.
-Modalità alternative prevedono l'uso di \func{link}, che fallisce se il nome
-esiste già, ma i problemi sono gli stessi.
-
-Per questo motivo la tecnica più pulita è quella di utilizzare \func{fcntl} su
-un file creato per l'occazione per ottenere un write lock sul primo byte del
-file (che non è detto debba esistere); 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; l'accesso sarà controllabile, senza
-necessità di ricorrere al \textit{polling}\index{polling}.
+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; sempre in \file{wrapper.h} si sono
+predisposte due funzioni, \func{LockFile} e \func{UnlockFile}, da utilizzare
+allo scopo.
+
+Dato che i file di lock presentano gli inconvenienti illustrati in precedenza,
+la tecnica alternativa più comune è quella di fare ricorso al \textit{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}[!bht]
+  \footnotesize \centering
+  \begin{minipage}[c]{15cm}
+    \begin{lstlisting}{} 
+
+    \end{lstlisting}
+  \end{minipage} 
+  \normalsize 
+  \caption{Il codice delle funzioni che permettono di creare un
+    \textit{mutex} utilizzando il file locking.}
+  \label{fig:ipc_flock_mutex}
+\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
 
 
-Una possibile alternativa all'uso dei semafori come meccanismo di
-sincronizzazione è quello di fare ricorso al \textit{file locking} visto in
-\secref{sec:file_locking}. 
 
 
 \subsection{Il \textit{memory mapping} anonimo}
 \label{sec:ipc_mmap_anonymous}
 
-Abbiamo visto in \secref{sec:file_memory_map} come sia possibile 
+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
 
 
 \section{La comunicazione fra processi di POSIX}
 \label{sec:ipc_posix}
 
-Lo standard POSIX.1b ha introdotto dei nuovi meccanismi di comunicazione,
-rifacendosi a quelli di System V, introducendo una nuova interfaccia che
-evitasse i principali problemi evidenziati in coda a
-\secref{sec:ipc_sysv_generic}.  
+Per superare i numerosi problemi del \textit{SysV IPC}, evidenziati per i suoi
+aspetti generali in coda a \secref{sec:ipc_sysv_generic} e per i singoli
+oggetti nei paragrafi successivi, lo standard POSIX.1b ha introdotto dei nuovi
+meccanismi di comunicazione, che vanno sotto il nome di POSIX IPC, definendo
+una interfaccia completamente nuova, che tratteremo in questa sezione.
 
 
 
 \subsection{Considerazioni generali}
 \label{sec:ipc_posix_generic}
 
+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}.
 
 
 \subsection{Code di messaggi}
 \label{sec:ipc_posix_mq}
 
+Le code di messaggi non sono supportate a livello del kernel, esse però
+possono essere implementate, usando la memoria condivisa ed i mutex, con
+funzioni di libreria. In generale esse sono comunque poco usate, i socket, nei
+casi in cui sono sufficienti, sono più comodi, e negli altri casi la
+comunicazione può essere gestita direttamente con la stessa metodologia usata
+per implementare le code di messaggi. Per questo ci limiteremo ad una
+descrizione essenziale. 
+
+
 
 \subsection{Semafori}
 \label{sec:ipc_posix_sem}
 
+Dei semafori POSIX esistono sostanzialmente due implementazioni; una è fatta a
+livello di libreria ed è fornita dalla libreria dei thread; questa però li
+implementa solo a livello di thread e non di processi. Esiste una 
+
 
 \subsection{Memoria condivisa}
 \label{sec:ipc_posix_shm}
 
+La memoria condivisa è l'unico degli oggetti di IPC POSIX già presente nel
+kernel ufficiale. 
+
+
 %%% Local Variables: 
 %%% mode: latex
 %%% TeX-master: "gapil"