Locking e deadlocking
authorSimone Piccardi <piccardi@gnulinux.it>
Fri, 15 Nov 2002 00:04:58 +0000 (00:04 +0000)
committerSimone Piccardi <piccardi@gnulinux.it>
Fri, 15 Nov 2002 00:04:58 +0000 (00:04 +0000)
fileadv.tex

index 8b72462..bff4ab7 100644 (file)
@@ -1236,30 +1236,35 @@ sovrapposizioni, e garantire la atomicit
 \subsection{L'\textit{advisory locking}}
 \label{sec:file_record_locking}
 
-La prima modalità di file locking che è stata implementata nei sistemi
-unix-like è quella che viene usualmente chiamata \textit{advisory
+La prima modalità di \textit{file locking} che è stata implementata nei
+sistemi unix-like è quella che viene usualmente chiamata \textit{advisory
   locking},\footnote{Stevens in \cite{APUE} fa riferimento a questo argomento
   come al \textit{record locking}, dizione utilizzata anche dal manuale delle
   \acr{glibc}; nelle pagine di manuale si parla di \textit{discretionary file
     lock} per \func{fcntl} e di \textit{advisory locking} per \func{flock},
-  mentre questo nome viene usato anche da Stevens per riferirsi al
-  \textit{file locking} di POSIX. Dato che la dizione \textit{record locking}
-  è quantomeno ambigua in quanto non esiste niente che possa fare riferimento
-  al concetto di \textit{record}, alla fine si è scelto di mantenere il nome
-  \textit{advisory locking}.} in quanto sono i singoli processi, e non il
-sistema, che si incaricano di asserire e verificare se esistono delle
-condizioni di blocco per l'accesso ai file.  Questo significa che le funzioni
-\func{read} o \func{write} non risentono affatto della presenza di un
-eventuale blocco, e che sta ai vari processi controllare esplicitamente lo
-stato dei file condivisi prima di accedervi, implementando un opportuno
-protocollo.
-
-In generale si distinguono due tipologie di blocco per un file: la prima è il
-cosiddetto \textit{shared lock}, detto anche \textit{read lock} in quanto
-serve a bloccare l'accesso in scrittura su un file affinché non venga
-modificato mentre lo si legge. Si parla di \textsl{blocco condiviso} in quanto
-più processi possono richiedere contemporaneamente uno \textit{shared lock}
-su un file per proteggere il loro accesso in lettura.
+  mentre questo nome viene usato da Stevens per riferirsi al \textit{file
+    locking} POSIX. Dato che la dizione \textit{record locking} è quantomeno
+  ambigua, in quanto in un sistema Unix non esiste niente che possa fare
+  riferimento al concetto di \textit{record}, alla fine si è scelto di
+  mantenere il nome \textit{advisory locking}.} in quanto sono i singoli
+processi, e non il sistema, che si incaricano di asserire e verificare se
+esistono delle condizioni di blocco per l'accesso ai file.  Questo significa
+che le funzioni \func{read} o \func{write} non risentono affatto della
+presenza di un eventuale \textit{lock}, e che sta ai vari processi controllare
+esplicitamente lo stato dei file condivisi prima di accedervi, implementando
+un opportuno protocollo.
+
+In generale si distinguono due tipologie di \textit{file lock}:\footnote{di
+  seguito ci riferiremo sempre ai blocchi di accesso ai file con la
+  nomenclatura inglese di \textit{file lock}, o più brevemente con
+  \textit{lock}, per evitare confuzioni linguistiche con il blocco di un
+  processo (cioè la condizione in cui il processo viene posto in stato di
+  \textit{sleep}).} la prima è il cosiddetto \textit{shared lock}, detto anche
+\textit{read lock} in quanto serve a bloccare l'accesso in scrittura su un
+file affinché non venga modificato mentre lo si legge. Si parla appunto di
+\textsl{blocco condiviso} in quanto più processi possono richiedere
+contemporaneamente uno \textit{shared lock} su un file per proteggere il loro
+accesso in lettura.
 
 La seconda tipologia è il cosiddetto \textit{exclusive lock}, detto anche
 \textit{write lock} in quanto serve a bloccare l'accesso su un file (sia in
@@ -1268,7 +1273,6 @@ scrivendo. Si parla di \textsl{blocco esclusivo} appunto perch
 processo alla volta può richiedere un \textit{exclusive lock} su un file per
 proteggere il suo accesso in scrittura. 
 
-
 \begin{table}[htb]
   \centering
   \footnotesize
@@ -1287,8 +1291,6 @@ proteggere il suo accesso in scrittura.
   \label{tab:file_file_lock}
 \end{table}
 
-
-
 In Linux sono disponibili due interfacce per utilizzare l'\textit{advisory
   locking}, la prima è quella derivata da BSD, che è basata sulla funzione
 \func{flock}, la seconda è quella standardizzata da POSIX.1 (derivata da
@@ -1330,10 +1332,11 @@ rimuovere un \textit{file lock} 
   }
 \end{prototype}
 
-La funzione può essere usata per acquisire o rilasciare un blocco a seconda di
-quanto specificato tramite il valore dell'argomento \param{operation}, questo
-viene interpretato come maschera binaria, e deve essere passato utilizzando le
-costanti riportate in \tabref{tab:file_flock_operation}.
+La funzione può essere usata per acquisire o rilasciare un \textit{file lock}
+a seconda di quanto specificato tramite il valore dell'argomento
+\param{operation}, questo viene interpretato come maschera binaria, e deve
+essere passato utilizzando le costanti riportate in
+\tabref{tab:file_flock_operation}.
 
 \begin{table}[htb]
   \centering
@@ -1345,7 +1348,7 @@ costanti riportate in \tabref{tab:file_flock_operation}.
     \hline
     \macro{LOCK\_SH} & Asserisce uno \textit{shared lock} sul file.\\ 
     \macro{LOCK\_EX} & Asserisce un \textit{esclusive lock} sul file.\\
-    \macro{LOCK\_UN} & Sblocca il file.\\
+    \macro{LOCK\_UN} & Rilascia il \textit{file lock}.\\
     \macro{LOCK\_NB} & Impedisce che la funzione si blocchi nella
                        richiesta di un \textit{file lock}.\\
     \hline    
@@ -1395,18 +1398,18 @@ La richiesta di un file lock prevede una scansione della lista per determinare
 se l'acquisizione è possibile, ed in caso positivo l'aggiunta di un nuovo
 elemento.\footnote{cioè una nuova struttura \var{file\_lock}.}  Nel caso dei
 lock creati con \func{flock} la semantica della funzione prevede che sia
-\func{dup} che \func{fork} non creino ulteriori istanze di un \textit{file
-  lock} quanto piuttosto degli ulteriori riferimenti allo stesso. Questo viene
-realizzato dal kernel secondo lo schema di \figref{fig:file_flock_struct},
-associando ad ogni nuovo \textit{file lock} un puntatore\footnote{il puntatore
-  è mantenuto nel campo \var{fl\_file} di \var{file\_lock}, e viene utilizzato
-  solo per i lock creati con la semantica BSD.} alla voce nella \textit{file
-  table} da cui si è richiesto il blocco, che così ne identifica il titolare.
+\func{dup} che \func{fork} non creino ulteriori istanze di un file lock quanto
+piuttosto degli ulteriori riferimenti allo stesso. Questo viene realizzato dal
+kernel secondo lo schema di \figref{fig:file_flock_struct}, associando ad ogni
+nuovo \textit{file lock} un puntatore\footnote{il puntatore è mantenuto nel
+  campo \var{fl\_file} di \var{file\_lock}, e viene utilizzato solo per i lock
+  creati con la semantica BSD.} alla voce nella \textit{file table} da cui si
+è richiesto il lock, che così ne identifica il titolare.
 
 Questa struttura prevede che, quando si richiede la rimozione di un file lock,
 il kernel acconsenta solo se la richiesta proviene da un file descriptor che
 fa riferimento ad una voce nella file table corrispondente a quella registrata
-nel blocco.  Allora se ricordiamo quanto visto in \secref{sec:file_dup} e
+nel lock.  Allora se ricordiamo quanto visto in \secref{sec:file_dup} e
 \secref{sec:file_sharing}, e cioè che i file descriptor duplicati e quelli
 ereditati in un processo figlio puntano sempre alla stessa voce nella file
 table, si può capire immediatamente quali sono le conseguenze nei confronti
@@ -1474,11 +1477,11 @@ essa viene usata solo secondo il prototipo:
 
 Al contrario di quanto avviene con l'interfaccia basata su \func{flock} con
 \func{fcntl} è possibile bloccare anche delle singole sezioni di un file;
-inoltre la funzione permette di leggere le informazioni relative ai blocchi
-esistenti.  Per questo la funzione utilizza come terzo argomento una apposita
-struttura \var{flock} (la cui definizione è riportata in
-\figref{fig:struct_flock}) che contiene tutte le specifiche di un dato file
-lock.
+inoltre la funzione permette di ottenere informazioni relative ai lock
+esistenti.  Per realizzare tutto questo la funzione utilizza come terzo
+argomento una apposita struttura \var{flock} (la cui definizione è riportata
+in \figref{fig:struct_flock}) nella quale inserire tutti i dati relativi ad un
+determinato lock.
 
 \begin{figure}[!bht]
   \footnotesize \centering
@@ -1525,7 +1528,6 @@ precedentemente acquisito. Infine il campo \var{l\_pid} viene usato solo in
 caso di lettura, quando si chiama \func{fcntl} con \macro{F\_GETLK}, e riporta
 il \acr{pid} del processo che detiene il lock.
 
-
 \begin{table}[htb]
   \centering
   \footnotesize
@@ -1536,7 +1538,7 @@ il \acr{pid} del processo che detiene il lock.
     \hline
     \macro{F\_RDLCK} & Richiede un blocco condiviso (\textit{read lock}).\\
     \macro{F\_WRLCK} & Richiede un blocco esclusivo (\textit{write lock}).\\
-    \macro{F\_UNLCK} & Richiede l'eliminazione di un lock.\\
+    \macro{F\_UNLCK} & Richiede l'eliminazione di un file lock.\\
     \hline    
   \end{tabular}
   \caption{Valori possibili per il campo \var{l\_type} di \func{flock}.}
@@ -1565,16 +1567,16 @@ specifica l'azione da compiere; i valori relativi al file locking sono tre:
   con un errore di \macro{EINTR}.
 \end{basedescript}
 
-Si noti che il comando \macro{F\_GETLK} non serve a rilevare una presenza
-generica di lock su un file, perché se ne esistono di compatibili con quello
-richiesto, la funzione ritorna comunque impostando \var{l\_type} a
-\macro{F\_UNLCK}. Inoltre a seconda del valore di \var{l\_type} si potrà
-controllare l'esistenza di un qualunque tipo di lock (con \macro{F\_WRLCK}) o
-dei soli lock esclusivi (con \macro{F\_RDLCK}). Si consideri poi che può
-esserci più di un lock che impedisce l'acquisizione di quello richiesto, ma la
-funzione ne riporterà sempre soltanto uno, impostando opportunamente
+Si noti che per quanto detto il comando \macro{F\_GETLK} non serve a rilevare
+una presenza generica di lock su un file, perché se ne esistono altri
+compatibili con quello richiesto, la funzione ritorna comunque impostando
+\var{l\_type} a \macro{F\_UNLCK}.  Inoltre a seconda del valore di
+\var{l\_type} si potrà controllare o l'esistenza di un qualunque tipo di lock
+(con \macro{F\_WRLCK}) o di write lock (con \macro{F\_RDLCK}). Si consideri
+poi che può esserci più di un lock che impedisce l'acquisizione di quello
+richiesto, ma la funzione ne riporterà sempre soltanto uno, impostando
 \var{l\_whence} a \macro{SEEK\_SET} ed i valori \var{l\_start} e \var{l\_len}
-per indicare quale è la regione effettivamente bloccata.
+per indicare quale è la regione bloccata.
 
 Infine si tenga presente che effettuare un controllo con il comando
 \macro{F\_GETLK} e poi tentare l'acquisizione con \macro{F\_SETLK} non è una
@@ -1583,17 +1585,41 @@ chiamate) per cui si deve sempre verificare il codice di ritorno di
 \func{fcntl}\footnote{controllare il codice di ritorno delle funzioni invocate
   è comunque una buona norma di programmazione, che permette di evitare un
   sacco di errori difficili da tracciare proprio perché non vengono rilevati.}
-quando la si invoca con \macro{F\_SETLK} per controllare che il lock sia stato
-effettivamente acquisito.
-
-Un'ultima considerazione riguardo il funzionamento del file locking POSIX
-riguarda la loro caratteristica di operare su delle sezioni di file: è
-possibile con una sola chiamata rimuovere più lock separati (indicando in
-\var{flock} una regione che li copra tutti), o rimuovere solo una parte di un
-lock preesistente (indicando una sezione contenuta in un altro lock), o di
-coprire con un nuovo lock altri lock già ottenuti. In tutti questi casi il
-kernel si preoccupa di accorpare o suddividere le regioni bloccate,
-aggiornando opportunamente le strutture interne usate per il file locking.
+quando la si invoca con \macro{F\_SETLK}, per controllare che il lock sia
+stato effettivamente acquisito.
+
+Occorre infine considerare come interagiscono operazioni su lock che si
+estendono su regioni che si sovrappongono fra loro (ovviamente si fa
+riferimento ai lock detenuti dallo stesso processo); ad esempio è possibile
+con una sola chiamata rimuovere più lock separati (indicando in \var{flock}
+una regione che li copra tutti), o rimuovere solo una parte di un lock
+preesistente (indicando una sezione contenuta in un altro lock), di coprire
+con un nuovo lock altri lock già ottenuti. In tutti questi casi il kernel si
+preoccupa di accorpare o suddividere le regioni bloccate, a seconda di quanto
+necessario per soddisfare l'operazione richiesta, aggiornando opportunamente
+le strutture interne usate per il file locking.
+
+\begin{figure}[htb]
+  \centering \includegraphics[width=13cm]{img/file_posix_lock}
+  \caption{Schema di una situazione di \textit{deadlock}.}
+  \label{fig:file_flock_dead}
+\end{figure}
+
+
+Non operando a livello di interi file, il file locking POSIX introduce
+un'ulteriore complicazione; consideriamo la situazione illustrata in
+\figref{fig:file_flock_dead}, in cui il processo A blocca la regione 1 e il
+processo B la regione 2. Supponiamo che successivamente il processo A richieda
+un lock sulla regione 2 che non può essere acquisito per il preesistente lock
+del processo 2; il processo 1 si bloccherà fintanto che il processo 2 non
+rilasci il blocco. Ma cosa accade se il processo 2 nel frattempo tenta a sua
+volta di ottenere un lock sulla regione A? Questa è una tipica situazione che
+porta ad un \textit{deadlock}\index{deadlock}, dato che a quel punto anche il
+processo 2 si bloccherebbe, e niente potrebbe sbloccare l'altro processo. Per
+questo motivo il kernel si incarica di rilevare situazioni di questo tipo, ed
+impedirle restituendo un errore di \macro{EDEADLK} alla funzione che cerca di
+acquisire un lock che porterebbe ad un \textit{deadlock}.
+
 
 \begin{figure}[htb]
   \centering \includegraphics[width=13cm]{img/file_posix_lock}