File lock e sincronizzazione con i file.
authorSimone Piccardi <piccardi@gnulinux.it>
Sun, 10 Nov 2002 18:28:51 +0000 (18:28 +0000)
committerSimone Piccardi <piccardi@gnulinux.it>
Sun, 10 Nov 2002 18:28:51 +0000 (18:28 +0000)
fileadv.tex
fileunix.tex
ipc.tex

index 454d91b237a8899888231001900ff6620f3c2a6e..a9ba273833e2f891f7e4bcacf9071ddb773be1de 100644 (file)
@@ -46,27 +46,28 @@ l'errore \macro{EAGAIN}.
 L'utilizzo di questa modalità di I/O permette di risolvere il problema
 controllando a turno i vari file descriptor, in un ciclo in cui si ripete
 l'accesso fintanto che esso non viene garantito.  Ovviamente questa tecnica,
-detta \textit{polling}, è estremamente inefficiente: si tiene costantemente
-impiegata la CPU solo per eseguire in continuazione delle system call che
-nella gran parte dei casi falliranno. Per evitare questo, come vedremo in
-\secref{sec:file_multiplexing}, è stata introdotta una nuova interfaccia di
-programmazione, che comporta comunque l'uso della modalità di I/O non
-bloccante.
+detta \textit{polling}\index{polling}, è estremamente inefficiente: si tiene
+costantemente impiegata la CPU solo per eseguire in continuazione delle system
+call che nella gran parte dei casi falliranno. Per evitare questo, come
+vedremo in \secref{sec:file_multiplexing}, è stata introdotta una nuova
+interfaccia di programmazione, che comporta comunque l'uso della modalità di
+I/O non bloccante.
 
 
 
 \subsection{L'I/O multiplexing}
 \label{sec:file_multiplexing}
 
-Per superare il problema di dover usare il \textit{polling} per controllare la
-possibilità di effettuare operazioni su un file aperto in modalità non
-bloccante, sia BSD che System V hanno introdotto delle nuove funzioni in grado
-di sospendere l'esecuzione di un processo in attesa che l'accesso diventi
-possibile.  Il primo ad introdurre questa modalità di operazione, chiamata
-usualmente \textit{I/O multiplexing}, è stato BSD,\footnote{la funzione è
-  apparsa in BSD4.2 e standardizzata in BSD4.4, ma è stata portata su tutti i
-  sistemi che supportano i \textit{socket}, compreso le varianti di System V.}
-con la funzione \func{select}, il cui prototipo è:
+Per superare il problema di dover usare il \textit{polling}\index{polling} per
+controllare la possibilità di effettuare operazioni su un file aperto in
+modalità non bloccante, sia BSD che System V hanno introdotto delle nuove
+funzioni in grado di sospendere l'esecuzione di un processo in attesa che
+l'accesso diventi possibile.  Il primo ad introdurre questa modalità di
+operazione, chiamata usualmente \textit{I/O multiplexing}, è stato
+BSD,\footnote{la funzione è apparsa in BSD4.2 e standardizzata in BSD4.4, ma è
+  stata portata su tutti i sistemi che supportano i \textit{socket}, compreso
+  le varianti di System V.}  con la funzione \func{select}, il cui prototipo
+è:
 \begin{functions}
   \headdecl{sys/time.h}
   \headdecl{sys/types.h}
index a36652d429b1a8a38295c7522b8833a220e21aef..eae73cbf386422747bb25bb8a8fb4b28be848120 100644 (file)
@@ -295,9 +295,10 @@ sempre il file descriptor con il valore pi
 
 \footnotetext[2]{la pagina di manuale di \func{open} segnala che questa
   opzione è difettosa su NFS, e che i programmi che la usano per stabilire un
-  file di lock possono incorrere in una race condition\index{race condition}.
-  Si consiglia come alternativa di usare un file con un nome univoco e la
-  funzione \func{link} per verificarne l'esistenza.}
+  file di lock (vedi \secref{sec:ipc_file_lock}) possono incorrere in una race
+  condition\index{race condition}.  Si consiglia come alternativa di usare un
+  file con un nome univoco e la funzione \func{link} per verificarne
+  l'esistenza.}
 
 \footnotetext[3]{\textit{Denial of Service}, si chiamano così attacchi miranti
   ad impedire un servizio causando una qualche forma di carico eccessivo per
diff --git a/ipc.tex b/ipc.tex
index 74d69db759a9199b5ae95afcc1a8c49d1d47de49..ee3270c3e067e7f67e2c65e8a1fc90b67ba962ed 100644 (file)
--- a/ipc.tex
+++ b/ipc.tex
@@ -879,20 +879,20 @@ molti altri devono poter leggere non pu
 Per questo nello sviluppo di System V vennero introdotti una serie di nuovi
 oggetti per la comunicazione fra processi ed una nuova interfaccia di
 programmazione, che fossero in grado di garantire una maggiore flessibilità.
-In questa sezione esamineremo come Linux supporta quello che viene ormai
-chiamato il \textsl{Sistema di comunicazione inter-processo} di System V, o
-\textit{System V IPC (Inter-Process Comunication)}.
+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})}.
 
 
 
 \subsection{Considerazioni generali}
 \label{sec:ipc_sysv_generic}
 
-La principale caratteristica del sistema di IPC di System V è quella di essere
-basato su oggetti permanenti che risiedono nel kernel. Questi, a differenza di
-quanto avviene per i file descriptor, non mantengono un contatore dei
-riferimenti, e non vengono cancellati dal sistema una volta che non sono più
-in uso. 
+La principale caratteristica del \textit{SysV IPC} è quella di essere basato
+su oggetti permanenti che risiedono nel kernel. Questi, a differenza di quanto
+avviene per i file descriptor, non mantengono un contatore dei riferimenti, e
+non vengono cancellati dal sistema una volta che non sono più in uso.
 
 Questo comporta due problemi: il primo è che, al contrario di quanto avviene
 per pipe e fifo, la memoria allocata per questi oggetti non viene rilasciata
@@ -903,13 +903,13 @@ file, un contatore del numero di riferimenti che ne indichi l'essere in uso,
 essi possono essere cancellati anche se ci sono dei processi che li stanno
 utilizzando, con tutte le conseguenze (negative) del caso.
 
-Un'ulteriore caratteristica negativa è che gli oggetti usati nel System V IPC
-vengono creati direttamente dal kernel, e sono accessibili solo specificando
-il relativo \textsl{identificatore}. Questo è un numero progressivo (un po'
-come il \acr{pid} dei processi) che il kernel assegna a ciascuno di essi
-quanto vengono creati (sul procedimento di assegnazione torneremo in
-\secref{sec:ipc_sysv_id_use}). L'identificatore viene restituito dalle
-funzioni che creano l'oggetto, ed è quindi locale al processo che le ha
+Un'ulteriore caratteristica negativa è che gli oggetti usati nel \textit{SysV
+  IPC} vengono creati direttamente dal kernel, e sono accessibili solo
+specificando il relativo \textsl{identificatore}. Questo è un numero
+progressivo (un po' come il \acr{pid} dei processi) che il kernel assegna a
+ciascuno di essi quanto vengono creati (sul procedimento di assegnazione
+torneremo in \secref{sec:ipc_sysv_id_use}). L'identificatore viene restituito
+dalle funzioni che creano l'oggetto, ed è quindi locale al processo che le ha
 eseguite. Dato che l'identificatore viene assegnato dinamicamente dal kernel
 non è possibile prevedere quale sarà, né utilizzare un qualche valore statico,
 si pone perciò il problema di come processi diversi possono accedere allo
@@ -922,7 +922,7 @@ primitivo \type{key\_t}, da specificare in fase di creazione dell'oggetto, e
 tramite la quale è possibile ricavare l'identificatore.\footnote{in sostanza
   si sposta il problema dell'accesso dalla classificazione in base
   all'identificatore alla classificazione in base alla chiave, una delle tante
-  complicazioni inutili presenti nell'IPC di System V.} Oltre la chiave, la
+  complicazioni inutili presenti nel \textit{SysV IPC}.} Oltre la chiave, la
 struttura, la cui definizione è riportata in \figref{fig:ipc_ipc_perm},
 mantiene varie proprietà ed informazioni associate all'oggetto.
 
@@ -973,7 +973,7 @@ file ed un numero di versione; il suo prototipo 
   
   \funcdecl{key\_t ftok(const char *pathname, int proj\_id)}
   
-  Restituisce una chiave per identificare un oggetto del System V IPC.
+  Restituisce una chiave per identificare un oggetto del \textit{SysV IPC}.
   
   \bodydesc{La funzione restituisce la chiave in caso di successo e -1
     altrimenti, nel qual caso \var{errno} sarà uno dei possibili codici di
@@ -1011,11 +1011,11 @@ creato da chi ci si aspetta.
 
 Questo è, insieme al fatto che gli oggetti sono permanenti e non mantengono un
 contatore di riferimenti per la cancellazione automatica, il principale
-problema del sistema di IPC di System V. Non esiste infatti una modalità
-chiara per identificare un oggetto, come sarebbe stato se lo si fosse
-associato ad in file, e tutta l'interfaccia è inutilmente complessa.  Per
-questo ne è stata effettuata una revisione completa nello standard POSIX.1b,
-che tratteremo in \secref{sec:ipc_posix}.
+problema del \textit{SysV IPC}. Non esiste infatti una modalità chiara per
+identificare un oggetto, come sarebbe stato se lo si fosse associato ad in
+file, e tutta l'interfaccia è inutilmente complessa.  Per questo ne è stata
+effettuata una revisione completa nello standard POSIX.1b, che tratteremo in
+\secref{sec:ipc_posix}.
 
 
 \subsection{Il controllo di accesso}
@@ -1116,10 +1116,10 @@ un identificatore pu
 Il sistema dispone sempre di un numero fisso di oggetti di IPC,\footnote{fino
   al kernel 2.2.x questi valori, definiti dalle costanti \macro{MSGMNI},
   \macro{SEMMNI} e \macro{SHMMNI}, potevano essere cambiati (come tutti gli
-  altri limiti relativi al \textit{System V IPC}) solo con una ricompilazione
-  del kernel, andando a modificarne la definizione nei relativi header file.
-  A partire dal kernel 2.4.x è possibile cambiare questi valori a sistema
-  attivo scrivendo sui file \file{shmmni}, \file{msgmni} e \file{sem} di
+  altri limiti relativi al \textit{SysV IPC}) solo con una ricompilazione del
+  kernel, andando a modificarne la definizione nei relativi header file.  A
+  partire dal kernel 2.4.x è possibile cambiare questi valori a sistema attivo
+  scrivendo sui file \file{shmmni}, \file{msgmni} e \file{sem} di
   \file{/proc/sys/kernel} o con l'uso di \texttt{syscntl}.} e per ciascuno di
 essi viene mantenuto in \var{seq} un numero di sequenza progressivo che viene
 incrementato di uno ogni volta che l'oggetto viene cancellato. Quando
@@ -1217,7 +1217,7 @@ mantenuta staticamente all'interno del sistema.
 \subsection{Code di messaggi}
 \label{sec:ipc_sysv_mq}
 
-Il primo oggetto introdotto dal \textit{System V IPC} è quello delle code di
+Il primo oggetto introdotto dal \textit{SysV IPC} è quello delle code di
 messaggi.  Le code di messaggi sono oggetti analoghi alle pipe o alle fifo,
 anche se la loro struttura è diversa. La funzione che permette di ottenerne
 una è \func{msgget} ed il suo prototipo è:
@@ -1651,6 +1651,24 @@ modificati:
 \item Il valore \var{msg\_rtime}, che viene impostato al tempo corrente.
 \end{itemize*}
 
+Le code di messaggi presentano il solito problema di tutti gli oggetti del
+SysV IPC; essendo questi permanenti restano nel sistema occupando risorse
+anche quando un processo è terminato, al contrario delle pipe per le quali
+tutte le risorse occupate vengono rilasciate quanto l'ultimo processo che le
+utilizzava termina. Questo comporta che in caso di errori si può saturare il
+sistema, e che devono comunque essere esplicitamente previste delle funzioni
+di rimozione in caso di interruzioni o uscite dal programma (come vedremo in
+\figref{fig:ipc_mq_fortune_server}).
+
+L'altro problema è non facendo uso di file descriptor le tecniche di
+\textit{I/O multiplexing} descritte in \secref{sec:file_multiplexing} non
+possono essere utilizzate, e non si ha a disposizione niente di analogo alle
+funzioni \func{select} e \func{poll}. Questo rende molto scomodo usare più di
+una di queste strutture alla volta; ad esempio non si può scrivere un server
+che aspetti un messaggio su più di una coda senza fare ricorso ad una tecnica
+di \textit{polling}\index{polling} che esegua un ciclo di attesa su ciascuna
+di esse.
+
 Come esempio dell'uso delle code di messaggi possiamo riscrivere il nostro
 server di \textit{fortunes} usando queste al posto delle fifo. In questo caso
 useremo una sola coda di messaggi, usando il tipo di messaggio per comunicare
@@ -1886,7 +1904,7 @@ della risorsa; in generale per
 utilizzando il valore del contatore come indicatore del ``numero di risorse''
 ancora disponibili.
 
-Il sistema di comunicazione interprocesso di System V IPC prevede anche i
+Il sistema di comunicazione interprocesso di \textit{SysV IPC} prevede anche i
 semafori, ma gli oggetti utilizzati non sono semafori singoli, ma gruppi di
 semafori detti \textsl{insiemi} (o \textit{semaphore set}); la funzione che
 permette di creare o ottenere l'identificatore di un insieme di semafori è
@@ -1929,8 +1947,8 @@ richiesta dell'identificatore di un insieme gi
 Purtroppo questa implementazione complica inutilmente lo schema elementare che
 abbiamo descritto, dato che non è possibile definire un singolo semaforo, ma
 se ne deve creare per forza un insieme.  Ma questa in definitiva è solo una
-complicazione inutile, il problema è che i semafori del System V IPC soffrono
-di altri due, ben più gravi, difetti.
+complicazione inutile, il problema è che i semafori del \textit{SysV IPC}
+soffrono di altri due, ben più gravi, difetti.
 
 Il primo difetto è che non esiste una funzione che permetta di creare ed
 inizializzare un semaforo in un'unica chiamata; occorre prima creare l'insieme
@@ -1938,8 +1956,8 @@ dei semafori con \func{semget} e poi inizializzarlo con \func{semctl}, si
 perde così ogni possibilità di eseguire atomicamente questa operazione.
 
 Il secondo difetto deriva dalla caratteristica generale degli oggetti del
-System V IPC di essere risorse globali di sistema, che non vengono cancellate
-quando nessuno le usa più; ci si così a trova a dover affrontare
+\textit{SysV IPC} di essere risorse globali di sistema, che non vengono
+cancellate quando nessuno le usa più; ci si così a trova a dover affrontare
 esplicitamente il caso in cui un processo termina per un qualche errore,
 lasciando un semaforo occupato, che resterà tale fino al successivo riavvio
 del sistema. Come vedremo esistono delle modalità per evitare tutto ciò, ma
@@ -1988,8 +2006,8 @@ semaforo), per quanto riguarda gli altri campi invece:
 Ciascun semaforo dell'insieme è realizzato come una struttura di tipo
 \var{sem} che ne contiene i dati essenziali, la sua definizione\footnote{si è
   riportata la definizione originaria del kernel 1.0, che contiene la prima
-  realizzazione del System V IPC in Linux. In realtà questa struttura ormai è
-  ridotta ai soli due primi membri, e gli altri vengono calcolati
+  realizzazione del \textit{SysV IPC} in Linux. In realtà questa struttura
+  ormai è ridotta ai soli due primi membri, e gli altri vengono calcolati
   dinamicamente. La si è utilizzata a scopo di esempio, perché indica tutti i
   valori associati ad un semaforo, restituiti dalle funzioni di controllo, e
   citati dalle pagine di manuale.} è riportata in \figref{fig:ipc_sem}. Questa
@@ -2360,8 +2378,8 @@ occorre fare riferimento all'implementazione usata in Linux, che 
 in maniera semplificata nello schema di \figref{fig:ipc_sem_schema}.  Si è
 presa come riferimento l'architettura usata fino al kernel 2.2.x che è più
 semplice (ed illustrata in dettaglio in \cite{tlk}); nel kernel 2.4.x la
-struttura del System V IPC è stata modificata, ma le definizioni relative a
-queste strutture restano per compatibilità.\footnote{in particolare con le
+struttura del \textit{SysV IPC} è stata modificata, ma le definizioni relative
+queste strutture restano per compatibilità.\footnote{in particolare con le
   vecchie versioni delle librerie del C, come le libc5.}
 
 \begin{figure}[htb]
@@ -2425,7 +2443,7 @@ ripristino non 
 \subsection{Memoria condivisa}
 \label{sec:ipc_sysv_shm}
 
-Il terzo oggetto introdotto dal \textit{System V IPC} è quello dei segmenti di
+Il terzo oggetto introdotto dal \textit{SysV IPC} è quello dei segmenti di
 memoria condivisa. La funzione che permette di ottenerne uno è \func{shmget},
 ed il suo prototipo è:
 \begin{functions}
@@ -2743,42 +2761,72 @@ condivisa; uno schema semplificato della struttura 
 \section{Tecniche alternative}
 \label{sec:ipc_alternatives}
 
-Come abbiamo visto in \secref{sec:ipc_sysv_generic} il sistema di IPC di
-System V 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
+Come abbiamo visto in \secref{sec:ipc_sysv_generic} 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.
 
 
 \subsection{Alternative alle code di messaggi}
 \label{sec:ipc_mq_alternative}
  
-Le code di messaggi sono probabilmente il meno usato degli oggetti di IPC di
-System V; esse infatti nacquero principalmente come meccanismo di
+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}) si può
 ottenere lo stesso risultato senza incorrere nelle complicazioni introdotte
-dal sistema di IPC di System V.
+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 cose che sono
-impossibili da ottenere con le pipe e i socket di \func{socketpair}.
-È però possibile implementare un meccanismo analogo attraverso l'uso di
-memoria condivisa e di meccanismi di sincronizzazione, (un esempio di
-reimplementazione di code di messaggi usando il \textit{memory mapping} e i
-semafori si trova in \cite{UNP2}). pertanto non è
+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}
 
-
-
-
-
-
+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
+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.} 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ò
+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}.
 
 
 Una possibile alternativa all'uso dei semafori come meccanismo di