\textit{exclusive lock} per una scrittura, uno \textit{shared lock} per una
lettura) prima di eseguire l'accesso ad un file. Se il blocco viene acquisito
il processo prosegue l'esecuzione, altrimenti (a meno di non aver richiesto un
-comportamento non bloccante) viene posto in stato di sleep. Una volta finite
-le operazioni sul file si deve provvedere a rimuovere il blocco.
+comportamento non bloccante) viene posto in stato di \textit{sleep}. Una volta
+finite le operazioni sul file si deve provvedere a rimuovere il blocco.
La situazione delle varie possibilità che si possono verificare è riassunta in
tab.~\ref{tab:file_file_lock}, dove si sono riportati, a seconda delle varie
&Nessun \textit{lock}&\textit{Read lock}&\textit{Write lock}\\
\hline
\hline
- \textit{Read lock} & SI & SI & NO \\
- \textit{Write lock}& SI & NO & NO \\
+ \textit{Read lock} & esecuzione & esecuzione & blocco \\
+ \textit{Write lock}& esecuzione & blocco & blocco \\
\hline
\end{tabular}
\caption{Tipologie di \textit{file locking}.}
\label{sec:file_flock}
La prima interfaccia per il \textit{file locking}, quella derivata da BSD,
-permette di eseguire un blocco solo su un intero file; la funzione usata per
-richiedere e rimuovere un \textit{file lock} è \funcd{flock}, ed il suo
-prototipo è:
+permette di eseguire un blocco solo su un intero file; la funzione di sistema
+usata per richiedere e rimuovere un \textit{file lock} è \funcd{flock}, ed il
+suo prototipo è:
\begin{funcproto}{
\fhead{sys/file.h}
{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
caso \var{errno} assumerà uno dei valori:
\begin{errlist}
- \item[\errcode{EWOULDBLOCK}] il file ha già un blocco attivo, e si è
- specificato \const{LOCK\_NB}.
+ \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale
+ nell'attesa dell'acquisizione di un \textit{file lock}.
+ \item[\errcode{EINVAL}] si è specificato un valore non valido
+ per \param{operation}.
+ \item[\errcode{ENOLCK}] il kernel non ha memoria sufficiente per gestire il
+ \textit{file lock}.
+ \item[\errcode{EWOULDBLOCK}] il file ha già un blocco attivo, e si è
+ specificato \const{LOCK\_NB}.
\end{errlist}
+ ed inoltre \errval{EBADF} nel suo significato generico.
}
\end{funcproto}
fondamentale da capire è che un \textit{file lock}, qualunque sia
l'interfaccia che si usa, anche se richiesto attraverso un file descriptor,
agisce sempre su di un file; perciò le informazioni relative agli eventuali
-\textit{file lock} sono mantenute dal kernel a livello di
-inode\itindex{inode},\footnote{in particolare, come accennato in
- fig.~\ref{fig:file_flock_struct}, i \textit{file lock} sono mantenuti in una
- \itindex{linked~list} \textit{linked list} di strutture
- \kstruct{file\_lock}. La lista è referenziata dall'indirizzo di partenza
- mantenuto dal campo \var{i\_flock} della struttura \kstruct{inode} (per le
- definizioni esatte si faccia riferimento al file \file{include/linux/fs.h}
- nei sorgenti del kernel). Un bit del campo \var{fl\_flags} di specifica se
- si tratta di un lock in semantica BSD (\const{FL\_FLOCK}) o POSIX
- (\const{FL\_POSIX}).} dato che questo è l'unico riferimento in comune che
-possono avere due processi diversi che aprono lo stesso file.
+\textit{file lock} sono mantenute dal kernel a livello di \itindex{inode}
+\textit{inode}, dato che questo è l'unico riferimento in comune che possono
+avere due processi diversi che aprono lo stesso file.
+
+In particolare, come accennato in fig.~\ref{fig:file_flock_struct}, i
+\textit{file lock} sono mantenuti in una \itindex{linked~list} \textit{linked
+ list} di strutture \kstruct{file\_lock}. La lista è referenziata
+dall'indirizzo di partenza mantenuto dal campo \var{i\_flock} della struttura
+\kstruct{inode} (per le definizioni esatte si faccia riferimento al file
+\file{include/linux/fs.h} nei sorgenti del kernel). Un bit del campo
+\var{fl\_flags} di specifica se si tratta di un lock in semantica BSD
+(\const{FL\_FLOCK}) o POSIX (\const{FL\_POSIX}) o un \textit{file lease}
+(\const{FL\_LEASE}, vedi sez.~\ref{sec:file_asyncronous_lease}).
\begin{figure}[!htb]
\centering
- \includegraphics[width=14cm]{img/file_flock}
+ \includegraphics[width=12cm]{img/file_flock}
\caption{Schema dell'architettura del \textit{file locking}, nel caso
particolare del suo utilizzo da parte dalla funzione \func{flock}.}
\label{fig:file_flock_struct}
La richiesta di un \textit{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 \kstruct{file\_lock}.}
-Nel caso dei blocchi 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
-fig.~\ref{fig:file_flock_struct}, associando ad ogni nuovo \textit{file lock}
-un puntatore\footnote{il puntatore è mantenuto nel campo \var{fl\_file} di
- \kstruct{file\_lock}, e viene utilizzato solo per i \textit{file lock} creati
- con la semantica BSD.} alla voce nella \itindex{file~table} \textit{file
- table} da cui si è richiesto il blocco, che così ne identifica il titolare.
+un nuovo elemento (cioè l'aggiunta di una nuova struttura
+\kstruct{file\_lock}). Nel caso dei blocchi 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 fig.~\ref{fig:file_flock_struct}, associando ad ogni nuovo \textit{file
+ lock} un puntatore alla voce nella \itindex{file~table} \textit{file table}
+da cui si è richiesto il blocco, che così ne identifica il titolare. Il
+puntatore è mantenuto nel campo \var{fl\_file} di \kstruct{file\_lock}, e
+viene utilizzato solo per i \textit{file lock} creati con la semantica BSD.
Questa struttura prevede che, quando si richiede la rimozione di un
\textit{file lock}, il kernel acconsenta solo se la richiesta proviene da un
\label{sec:file_posix_lock}
La seconda interfaccia per l'\textit{advisory locking} disponibile in Linux è
-quella standardizzata da POSIX, basata sulla funzione \func{fcntl}. Abbiamo
-già trattato questa funzione nelle sue molteplici possibilità di utilizzo in
-sez.~\ref{sec:file_fcntl_ioctl}. Quando la si impiega per il \textit{file
- locking} essa viene usata solo secondo il seguente prototipo:
-\begin{prototype}{fcntl.h}{int fcntl(int fd, int cmd, struct flock *lock)}
-
- Applica o rimuove un \textit{file lock} sul file \param{fd}.
-
- \bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di
- errore, nel qual caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
+quella standardizzata da POSIX, basata sulla funzione di sistema
+\func{fcntl}. Abbiamo già trattato questa funzione nelle sue molteplici
+possibilità di utilizzo in sez.~\ref{sec:file_fcntl_ioctl}. Quando la si
+impiega per il \textit{file locking} essa viene usata solo secondo il seguente
+prototipo:
+
+\begin{funcproto}{
+\fhead{fcntl.h}
+\fdecl{int fcntl(int fd, int cmd, struct flock *lock)}
+\fdesc{Applica o rimuove un \textit{file lock}.}
+}
+
+{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
+ caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
\item[\errcode{EACCES}] l'operazione è proibita per la presenza di
\textit{file lock} da parte di altri processi.
- \item[\errcode{ENOLCK}] il sistema non ha le risorse per il blocco: ci
- sono troppi segmenti di \textit{lock} aperti, si è esaurita la tabella
- dei \textit{file lock}, o il protocollo per il blocco remoto è fallito.
\item[\errcode{EDEADLK}] si è richiesto un \textit{lock} su una regione
bloccata da un altro processo che è a sua volta in attesa dello sblocco
di un \textit{lock} mantenuto dal processo corrente; si avrebbe pertanto
riconosca sempre questa situazione.
\item[\errcode{EINTR}] la funzione è stata interrotta da un segnale prima
di poter acquisire un \textit{file lock}.
- \end{errlist}
- ed inoltre \errval{EBADF}, \errval{EFAULT}.
- }
-\end{prototype}
+ \item[\errcode{ENOLCK}] il sistema non ha le risorse per il blocco: ci
+ sono troppi segmenti di \textit{lock} aperti, si è esaurita la tabella
+ dei \textit{file lock}, o il protocollo per il blocco remoto è fallito.
+ \end{errlist}
+ ed inoltre \errval{EBADF}, \errval{EFAULT} nel loro significato generico.}
+\end{funcproto}
Al contrario di quanto avviene con l'interfaccia basata su \func{flock} con
\func{fcntl} è possibile bloccare anche delle singole sezioni di un file, fino
\begin{figure}[!htb]
\footnotesize \centering
- \begin{minipage}[c]{\textwidth}
+ \begin{minipage}[c]{0.90\textwidth}
\includestruct{listati/flock.h}
\end{minipage}
\normalsize
\label{fig:struct_flock}
\end{figure}
-
I primi tre campi della struttura, \var{l\_whence}, \var{l\_start} e
\var{l\_len}, servono a specificare la sezione del file a cui fa riferimento
il blocco: \var{l\_start} specifica il byte di partenza, \var{l\_len} la
contare \var{l\_start}. Il valore di \var{l\_whence} segue la stessa semantica
dell'omonimo argomento di \func{lseek}, coi tre possibili valori
\const{SEEK\_SET}, \const{SEEK\_CUR} e \const{SEEK\_END}, (si vedano le
-relative descrizioni in sez.~\ref{sec:file_lseek}).
+relative descrizioni in tab.~\ref{tab:lseek_whence_values}).
Si tenga presente che un \textit{file lock} può essere richiesto anche per una
regione al di là della corrente fine del file, così che una eventuale
regione a partire da un certo punto fino alla fine del file, coprendo
automaticamente quanto eventualmente aggiunto in coda allo stesso.
+Lo standard POSIX non richiede che \var{l\_len} sia positivo, ed a partire dal
+kernel 2.4.21 è possibile anche indicare valori di \var{l\_len} negativi, in
+tal caso l'intervallo coperto va da \var{l\_start}$+$\var{l\_len} a
+\var{l\_start}$-1$, mentre per un valore positivo l'intervallo va da
+\var{l\_start} a \var{l\_start}$+$\var{l\_len}$-1$. Si può però usare un
+valore negativo soltanto se l'inizio della regione indicata non cade prima
+dell'inizio del file, con un valore positivo invece si può anche indicare una
+regione che eccede la dimensione corrente del file, e questa verrà coperta in
+una sua futura estensione.
+
+Il tipo di \textit{file lock} richiesto viene specificato dal campo
+\var{l\_type}, esso può assumere i tre valori definiti dalle costanti
+riportate in tab.~\ref{tab:file_flock_type}, che permettono di richiedere
+rispettivamente uno \textit{shared lock}, un \textit{esclusive lock}, e la
+rimozione di un blocco precedentemente acquisito. Infine il campo \var{l\_pid}
+viene usato solo in caso di lettura, quando si chiama \func{fcntl} con
+\const{F\_GETLK}, e riporta il \ids{PID} del processo che detiene il
+\textit{file lock}.
+
\begin{table}[htb]
\centering
\footnotesize
\label{tab:file_flock_type}
\end{table}
-Il tipo di \textit{file lock} richiesto viene specificato dal campo
-\var{l\_type}, esso può assumere i tre valori definiti dalle costanti
-riportate in tab.~\ref{tab:file_flock_type}, che permettono di richiedere
-rispettivamente uno \textit{shared lock}, un \textit{esclusive lock}, e la
-rimozione di un blocco precedentemente acquisito. Infine il campo \var{l\_pid}
-viene usato solo in caso di lettura, quando si chiama \func{fcntl} con
-\const{F\_GETLK}, e riporta il \ids{PID} del processo che detiene il
-\textit{file lock}.
-
Oltre a quanto richiesto tramite i campi di \struct{flock}, l'operazione
effettivamente svolta dalla funzione è stabilita dal valore dall'argomento
\param{cmd} che, come già riportato in sez.~\ref{sec:file_fcntl_ioctl},
-specifica l'azione da compiere; i valori relativi al \textit{file locking}
-sono tre:
+specifica l'azione da compiere; i valori utilizzabili relativi al \textit{file
+ locking} sono tre:
\begin{basedescript}{\desclabelwidth{2.0cm}}
\item[\const{F\_GETLK}] verifica se il \textit{file lock} specificato dalla
struttura puntata da \param{lock} può essere acquisito: in caso negativo
campo \var{l\_type} con il valore \const{F\_UNLCK}.
\item[\const{F\_SETLK}] se il campo \var{l\_type} della struttura puntata da
\param{lock} è \const{F\_RDLCK} o \const{F\_WRLCK} richiede il
- corrispondente \textit{file lock}, se è \const{F\_UNLCK} lo rilascia. Nel
+ corrispondente \textit{file lock}, se è \const{F\_UNLCK} lo rilascia; nel
caso la richiesta non possa essere soddisfatta a causa di un blocco
preesistente la funzione ritorna immediatamente con un errore di
\errcode{EACCES} o di \errcode{EAGAIN}.
\item[\const{F\_SETLKW}] è identica a \const{F\_SETLK}, ma se la richiesta di
non può essere soddisfatta per la presenza di un altro blocco, mette il
processo in stato di attesa fintanto che il blocco precedente non viene
- rilasciato. Se l'attesa viene interrotta da un segnale la funzione ritorna
+ rilasciato; se l'attesa viene interrotta da un segnale la funzione ritorna
con un errore di \errcode{EINTR}.
\end{basedescript}
sez.~\ref{sec:file_flock}) esaminiamo più in dettaglio come viene gestito dal
kernel. Lo schema delle strutture utilizzate è riportato in
fig.~\ref{fig:file_posix_lock}; come si vede esso è molto simile all'analogo
-di fig.~\ref{fig:file_flock_struct}:\footnote{in questo caso nella figura si
- sono evidenziati solo i campi di \kstruct{file\_lock} significativi per la
- semantica POSIX, in particolare adesso ciascuna struttura contiene, oltre al
- \ids{PID} del processo in \var{fl\_pid}, la sezione di file che viene
- bloccata grazie ai campi \var{fl\_start} e \var{fl\_end}. La struttura è
- comunque la stessa, solo che in questo caso nel campo \var{fl\_flags} è
- impostato il bit \const{FL\_POSIX} ed il campo \var{fl\_file} non viene
- usato.} il blocco è sempre associato \itindex{inode} all'inode, solo che in
-questo caso la titolarità non viene identificata con il riferimento ad una
-voce nella \itindex{file~table} \textit{file table}, ma con il valore del
-\ids{PID} del processo.
+di fig.~\ref{fig:file_flock_struct}. In questo caso nella figura si sono
+evidenziati solo i campi di \kstruct{file\_lock} significativi per la
+semantica POSIX, in particolare adesso ciascuna struttura contiene, oltre al
+\ids{PID} del processo in \var{fl\_pid}, la sezione di file che viene bloccata
+grazie ai campi \var{fl\_start} e \var{fl\_end}. La struttura è comunque la
+stessa, solo che in questo caso nel campo \var{fl\_flags} è impostato il bit
+\const{FL\_POSIX} ed il campo \var{fl\_file} non viene usato. Il blocco è
+sempre associato \itindex{inode} all'\textit{inode}, solo che in questo caso
+la titolarità non viene identificata con il riferimento ad una voce nella
+\itindex{file~table} \textit{file table}, ma con il valore del \ids{PID} del
+processo.
\begin{figure}[!htb]
\centering \includegraphics[width=12cm]{img/file_posix_lock}
richiedono dei blocchi su regioni che si sovrappongono fra loro all'interno
stesso processo. Siccome il controllo, come nel caso della rimozione, si basa
solo sul \ids{PID} del processo che chiama la funzione, queste richieste
-avranno sempre successo.
-
-Nel caso della semantica BSD, essendo i lock relativi a tutto un file e non
-accumulandosi,\footnote{questa ultima caratteristica è vera in generale, se
- cioè si richiede più volte lo stesso \textit{file lock}, o più blocchi sulla
- stessa sezione di file, le richieste non si cumulano e basta una sola
- richiesta di rilascio per cancellare il blocco.} la cosa non ha alcun
-effetto; la funzione ritorna con successo, senza che il kernel debba
-modificare la lista dei \textit{file lock}. In questo caso invece si possono
-avere una serie di situazioni diverse: ad esempio è possibile rimuovere con
-una sola chiamata più \textit{file lock} distinti (indicando in una regione
-che si sovrapponga completamente a quelle di questi ultimi), o rimuovere solo
-una parte di un blocco preesistente (indicando una regione contenuta in quella
-di un altro blocco), creando un buco, o coprire con un nuovo blocco altri
-\textit{file lock} già ottenuti, e così via, a secondo di come si
-sovrappongono le regioni richieste e del tipo di operazione richiesta. Il
-comportamento seguito in questo caso che la funzione ha successo ed esegue
-l'operazione richiesta sulla regione indicata; è compito del kernel
+avranno sempre successo. Nel caso della semantica BSD, essendo i lock
+relativi a tutto un file e non accumulandosi,\footnote{questa ultima
+ caratteristica è vera in generale, se cioè si richiede più volte lo stesso
+ \textit{file lock}, o più blocchi sulla stessa sezione di file, le richieste
+ non si cumulano e basta una sola richiesta di rilascio per cancellare il
+ blocco.} la cosa non ha alcun effetto; la funzione ritorna con successo,
+senza che il kernel debba modificare la lista dei \textit{file lock}.
+
+Con i \textit{file lock} POSIX invece si possono avere una serie di situazioni
+diverse: ad esempio è possibile rimuovere con una sola chiamata più
+\textit{file lock} distinti (indicando in una regione che si sovrapponga
+completamente a quelle di questi ultimi), o rimuovere solo una parte di un
+blocco preesistente (indicando una regione contenuta in quella di un altro
+blocco), creando un buco, o coprire con un nuovo blocco altri \textit{file
+ lock} già ottenuti, e così via, a secondo di come si sovrappongono le
+regioni richieste e del tipo di operazione richiesta.
+
+Il comportamento seguito in questo caso è che la funzione ha successo ed
+esegue l'operazione richiesta sulla regione indicata; è compito del kernel
preoccuparsi di accorpare o dividere le voci nella lista dei \textit{file
lock} per far si che le regioni bloccate da essa risultanti siano coerenti
con quanto necessario a soddisfare l'operazione richiesta.
\footnotesize \centering
\begin{minipage}[c]{\codesamplewidth}
\includecodesample{listati/Flock.c}
- \end{minipage}
+ \end{minipage}
\normalsize
\caption{Sezione principale del codice del programma \file{Flock.c}.}
\label{fig:file_flock_code}
\textit{file locking}; cominciamo con l'eseguire un \textit{read lock} su un
file, ad esempio usando all'interno di un terminale il seguente comando:
-\vspace{1mm}
-\begin{minipage}[c]{12cm}
-\begin{verbatim}
-[piccardi@gont sources]$ ./flock -r Flock.c
+\begin{Console}
+[piccardi@gont sources]$ \textbf{./flock -r Flock.c}
Lock acquired
-\end{verbatim}%$
-\end{minipage}\vspace{1mm}
-\par\noindent
+\end{Console}
+%$
il programma segnalerà di aver acquisito un blocco e si bloccherà; in questo
caso si è usato il \textit{file locking} POSIX e non avendo specificato niente
riguardo alla sezione che si vuole bloccare sono stati usati i valori
eseguire lo stesso comando in un altro terminale, e avremo lo stesso
risultato. Se invece proviamo ad eseguire un \textit{write lock} avremo:
-\vspace{1mm}
-\begin{minipage}[c]{12cm}
-\begin{verbatim}
-[piccardi@gont sources]$ ./flock -w Flock.c
+\begin{Console}
+[piccardi@gont sources]$ \textbf{./flock -w Flock.c}
Failed lock: Resource temporarily unavailable
-\end{verbatim}%$
-\end{minipage}\vspace{1mm}
-\par\noindent
+\end{Console}
+%$
come ci aspettiamo il programma terminerà segnalando l'indisponibilità del
blocco, dato che il file è bloccato dal precedente \textit{read lock}. Si noti
che il risultato è lo stesso anche se si richiede il blocco su una sola parte
del file con il comando:
-\vspace{1mm}
-\begin{minipage}[c]{12cm}
-\begin{verbatim}
-[piccardi@gont sources]$ ./flock -w -s0 -l10 Flock.c
+\begin{Console}
+[piccardi@gont sources]$ \textbf{./flock -w -s0 -l10 Flock.c}
Failed lock: Resource temporarily unavailable
-\end{verbatim}%$
-\end{minipage}\vspace{1mm}
-\par\noindent
+\end{Console}
+%$
se invece blocchiamo una regione con:
-\vspace{1mm}
-\begin{minipage}[c]{12cm}
-\begin{verbatim}
-[piccardi@gont sources]$ ./flock -r -s0 -l10 Flock.c
+\begin{Console}
+[piccardi@gont sources]$ \textbf{./flock -r -s0 -l10 Flock.c}
Lock acquired
-\end{verbatim}%$
-\end{minipage}\vspace{1mm}
-\par\noindent
+\end{Console}
+%$
una volta che riproviamo ad acquisire il \textit{write lock} i risultati
dipenderanno dalla regione richiesta; ad esempio nel caso in cui le due
regioni si sovrappongono avremo che:
-\vspace{1mm}
-\begin{minipage}[c]{12cm}
-\begin{verbatim}
-[piccardi@gont sources]$ ./flock -w -s5 -l15 Flock.c
+\begin{Console}
+[piccardi@gont sources]$ \textbf{./flock -w -s5 -l15 Flock.c}
Failed lock: Resource temporarily unavailable
-\end{verbatim}%$
-\end{minipage}\vspace{1mm}
-\par\noindent
+\end{Console}
+%$
ed il blocco viene rifiutato, ma se invece si richiede una regione distinta
avremo che:
-\vspace{1mm}
-\begin{minipage}[c]{12cm}
-\begin{verbatim}
-[piccardi@gont sources]$ ./flock -w -s11 -l15 Flock.c
+\begin{Console}
+[piccardi@gont sources]$ \textbf{./flock -w -s11 -l15 Flock.c}
Lock acquired
-\end{verbatim}%$
-\end{minipage}\vspace{1mm}
-\par\noindent
+\end{Console}
+%$
ed il blocco viene acquisito. Se a questo punto si prova ad eseguire un
\textit{read lock} che comprende la nuova regione bloccata in scrittura:
-\vspace{1mm}
-\begin{minipage}[c]{12cm}
-\begin{verbatim}
-[piccardi@gont sources]$ ./flock -r -s10 -l20 Flock.c
+\begin{Console}
+[piccardi@gont sources]$ \textbf{./flock -r -s10 -l20 Flock.c}
Failed lock: Resource temporarily unavailable
-\end{verbatim}%$
-\end{minipage}\vspace{1mm}
-\par\noindent
+\end{Console}
+%$
come ci aspettiamo questo non sarà consentito.
Il programma di norma esegue il tentativo di acquisire il lock in modalità non
bloccante, riproviamo allora a ripetere le prove precedenti con questa
opzione:
-\vspace{1mm}
-\begin{minipage}[c]{12cm}
-\begin{verbatim}
-[piccardi@gont sources]$ ./flock -r -b -s0 -l10 Flock.c Lock acquired
-\end{verbatim}%$
-\end{minipage}\vspace{1mm}
-\par\noindent
+\begin{Console}
+[piccardi@gont sources]$ \textbf{./flock -r -b -s0 -l10 Flock.c} Lock acquired
+\end{Console}
+%$
il primo comando acquisisce subito un \textit{read lock}, e quindi non cambia
nulla, ma se proviamo adesso a richiedere un \textit{write lock} che non potrà
essere acquisito otterremo:
-\vspace{1mm}
-\begin{minipage}[c]{12cm}
-\begin{verbatim}
-[piccardi@gont sources]$ ./flock -w -s0 -l10 Flock.c
-\end{verbatim}%$
-\end{minipage}\vspace{1mm}
-\par\noindent
+\begin{Console}
+[piccardi@gont sources]$ \textbf{./flock -w -s0 -l10 Flock.c}
+\end{Console}
+%$
il programma cioè si bloccherà nella chiamata a \func{fcntl}; se a questo
punto rilasciamo il precedente blocco (terminando il primo comando un
\texttt{C-c} sul terminale) potremo verificare che sull'altro terminale il
blocco viene acquisito, con la comparsa di una nuova riga:
-\vspace{1mm}
-\begin{minipage}[c]{12cm}
-\begin{verbatim}
-[piccardi@gont sources]$ ./flock -w -s0 -l10 Flock.c
+\begin{Console}
+[piccardi@gont sources]$ \textbf{./flock -w -s0 -l10 Flock.c}
Lock acquired
-\end{verbatim}%$
-\end{minipage}\vspace{3mm}
-\par\noindent
+\end{Console}
+%$
Un'altra cosa che si può controllare con il nostro programma è l'interazione
fra i due tipi di blocco; se ripartiamo dal primo comando con cui si è
succede quando si cerca di ottenere un blocco in scrittura con la semantica
BSD:
-\vspace{1mm}
-\begin{minipage}[c]{12cm}
-\begin{verbatim}
-[root@gont sources]# ./flock -f -w Flock.c
+\begin{Console}
+[root@gont sources]# \textbf{./flock -f -w Flock.c}
Lock acquired
-\end{verbatim}
-\end{minipage}\vspace{1mm}
-\par\noindent
+\end{Console}
+%$
che ci mostra come i due tipi di blocco siano assolutamente indipendenti; per
questo motivo occorre sempre tenere presente quale fra le due semantiche
disponibili stanno usando i programmi con cui si interagisce, dato che i
blocchi applicati con l'altra non avrebbero nessun effetto.
-
-
-\subsection{La funzione \func{lockf}}
-\label{sec:file_lockf}
+% \subsection{La funzione \func{lockf}}
+% \label{sec:file_lockf}
Abbiamo visto come l'interfaccia POSIX per il \textit{file locking} sia molto
più potente e flessibile di quella di BSD, questo comporta anche una maggiore
complessità per via delle varie opzioni da passare a \func{fcntl}. Per questo
-motivo è disponibile anche una interfaccia semplificata (ripresa da System V)
-che utilizza la funzione \funcd{lockf}, il cui prototipo è:
-\begin{prototype}{sys/file.h}{int lockf(int fd, int cmd, off\_t len)}
-
- Applica, controlla o rimuove un \textit{file lock} sul file \param{fd}.
-
- \bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di
- errore, nel qual caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EWOULDBLOCK}] non è possibile acquisire il lock, e si è
- selezionato \const{LOCK\_NB}, oppure l'operazione è proibita perché il
- file è mappato in memoria.
- \item[\errcode{ENOLCK}] il sistema non ha le risorse per il blocco: ci
- sono troppi segmenti di \textit{lock} aperti, si è esaurita la tabella
- dei \textit{file lock}.
- \end{errlist}
- ed inoltre \errval{EBADF}, \errval{EINVAL}.
- }
-\end{prototype}
+motivo è disponibile anche una interfaccia semplificata che utilizza la
+funzione \funcd{lockf},\footnote{la funzione è ripresa da System V e per
+ poterla utilizzare è richiesta che siano definite le opportune macro, una
+ fra \macro{\_BSD\_SOURCE} o \macro{\_SVID\_SOURCE}, oppure
+ \macro{\_XOPEN\_SOURCE} ad un valore di almeno 500, oppure
+ \macro{\_XOPEN\_SOURCE} e \macro{\_XOPEN\_SOURCE\_EXTENDED}.} il cui
+prototipo è:
-Il comportamento della funzione dipende dal valore dell'argomento \param{cmd},
-che specifica quale azione eseguire; i valori possibili sono riportati in
-tab.~\ref{tab:file_lockf_type}.
+\begin{funcproto}{
+\fhead{unistd.h}
+\fdecl{int lockf(int fd, int cmd, off\_t len)}
+\fdesc{Applica, controlla o rimuove un \textit{file lock}.}
+}
-\begin{table}[htb]
+{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
+ caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
+ \item[\errcode{EAGAIN}] il file è bloccato, e si sono richiesti
+ \const{F\_TLOCK} o \const{F\_TEST} (in alcuni casi può dare anche
+ \errcode{EACCESS}.
+ \item[\errcode{EBADF}] \param{fd} non è un file descriptor aperto o si sono
+ richiesti \const{F\_LOCK} o \const{F\_TLOCK} ma il file non è scrivibile.
+ \item[\errcode{EINVAL}] si è usato un valore non valido per \param{cmd}.
+ \end{errlist}
+ ed inoltre \errcode{EDEADLK} e \errcode{ENOLCK} con lo stesso significato
+ che hanno con \funcd{fcntl}.
+}
+\end{funcproto}
+
+La funzione opera sul file indicato dal file descriptor \param{fd}, che deve
+essere aperto in scrittura, perché utilizza soltanto \textit{lock}
+esclusivi. La sezione di file bloccata viene controllata dal valore
+di \param{len}, che indica la lunghezza della stessa, usando come riferimento
+la posizione corrente sul file. La sezione effettiva varia a secondo del
+segno, secondo lo schema illustrato in fig.~\ref{fig:file_lockf_boundary}, se
+si specifica un valore nullo il file viene bloccato a partire dalla posizione
+corrente fino alla sua fine presente o futura (nello schema corrisponderebbe
+ad un valore infinito positivo).
+
+\begin{figure}[!htb]
\centering
- \footnotesize
- \begin{tabular}[c]{|l|p{7cm}|}
- \hline
- \textbf{Valore} & \textbf{Significato} \\
- \hline
- \hline
- \const{LOCK\_SH}& Richiede uno \textit{shared lock}. Più processi possono
- mantenere un blocco condiviso sullo stesso file.\\
- \const{LOCK\_EX}& Richiede un \textit{exclusive lock}. Un solo processo
- alla volta può mantenere un blocco esclusivo su un file.\\
- \const{LOCK\_UN}& Sblocca il file.\\
- \const{LOCK\_NB}& Non blocca la funzione quando il blocco non è disponibile,
- si specifica sempre insieme ad una delle altre operazioni
- con un OR aritmetico dei valori.\\
- \hline
- \end{tabular}
- \caption{Valori possibili per l'argomento \param{cmd} di \func{lockf}.}
- \label{tab:file_lockf_type}
-\end{table}
+ \includegraphics[width=10cm]{img/lockf_boundary}
+ \caption{Schema della sezione di file bloccata con \func{lockf}.}
+ \label{fig:file_lockf_boundary}
+\end{figure}
+
+Il comportamento della funzione viene controllato dal valore
+dell'argomento \param{cmd}, che specifica quale azione eseguire, i soli valori
+consentiti sono i seguenti:
-Qualora il blocco non possa essere acquisito, a meno di non aver specificato
-\const{LOCK\_NB}, la funzione si blocca fino alla disponibilità dello stesso.
-Dato che la funzione è implementata utilizzando \func{fcntl} la semantica
-delle operazioni è la stessa di quest'ultima (pertanto la funzione non è
-affatto equivalente a \func{flock}).
+\begin{basedescript}{\desclabelwidth{2.0cm}}
+\item[\const{F\_LOCK}] Richiede un \textit{lock} esclusivo sul file, e blocca
+ il processo chiamante se, anche parzialmente, la sezione indicata si
+ sovrappone ad una che è già stata bloccata da un altro processo; in caso di
+ sovrapposizione con un altro blocco già ottenuto le sezioni vengono unite.
+\item[\const{F\_TLOCK}] Richiede un \textit{exclusive lock}, in maniera
+ identica a\const{F\_LOCK} ma in caso di indisponibilità non blocca il
+ processo restituendo un errore di \errval{EAGAIN}.
+\item[\const{F\_ULOCK}] Rilascia il blocco sulla sezione indicata, questo può
+ anche causare la suddivisione di una sezione bloccata in precedenza nelle
+ due parti eccedenti nel caso si sia indicato un intervallo più limitato.
+\item[\const{F\_TEST}] Controlla la presenza di un blocco sulla sezione di
+ file indicata, \func{lockf} ritorna $0$ se la sezione è libera o bloccata
+ dal processo stesso, o $-1$ se è bloccata da un altro processo, nel qual
+ caso \var{errno} assume il valore \errval{EAGAIN} (ma su alcuni sistemi può
+ essere restituito anche \errval{EACCESS}).
+\end{basedescript}
+
+La funzione è semplicemente una diversa interfaccia al \textit{file locking}
+POSIX ed è realizzata utilizzando \func{fcntl}; pertanto la semantica delle
+operazioni è la stessa di quest'ultima e quindi la funzione presenta lo stesso
+comportamento riguardo gli effetti della chiusura dei file, degli effetti sui
+file duplicati e nel passaggio attraverso \func{fork} ed \func{exec}. Per
+questo motivo la funzione non è affatto equivalente a \func{flock} e può
+essere usata senza interferenze insieme a quest'ultima.
dimensioni del file vada a sovrapporsi ad una regione bloccata).
L'ultimo aspetto della interazione del \textit{mandatory locking} con le
-funzioni di accesso ai file è quello relativo ai file mappati in memoria (che
-abbiamo trattato in sez.~\ref{sec:file_memory_map}); anche in tal caso
-infatti, quando si esegue la mappatura con l'opzione \const{MAP\_SHARED}, si
-ha un accesso al contenuto del file. Lo standard SVID prevede che sia
-impossibile eseguire il memory mapping di un file su cui sono presenti dei
+funzioni di accesso ai file è quello relativo ai file mappati in memoria (vedi
+sez.~\ref{sec:file_memory_map}); anche in tal caso infatti, quando si esegue
+la mappatura con l'opzione \const{MAP\_SHARED}, si ha un accesso al contenuto
+del file. Lo standard SVID prevede che sia impossibile eseguire il
+\textit{memory mapping} di un file su cui sono presenti dei
blocchi\footnote{alcuni sistemi, come HP-UX, sono ancora più restrittivi e lo
impediscono anche in caso di \textit{advisory locking}, anche se questo
comportamento non ha molto senso, dato che comunque qualunque accesso
qual caso la funzione fallisce con il solito \errcode{EAGAIN}) che comporta la
possibilità di modificare il file.
+Si tenga conto infine che su Linux l'implementazione corrente del
+\textit{mandatory locking} è difettosa e soffre di una \textit{race
+ condition}, per cui una scrittura con \func{write} che si sovrapponga alla
+richiesta di un \textit{read lock} può modificare i dati anche dopo che questo
+è stato ottenuto, ed una lettura con \func{read} può restituire dati scritti
+dopo l'ottenimento di un \textit{write lock}. Lo stesso tipo di problema si
+può presentare anche con l'uso di file mappati in memoria; pertanto allo stato
+attuale delle cose è sconsigliabile fare affidamento sul \textit{mandatory
+ locking}.
+
+
\itindend{file~locking}
\itindend{mandatory~locking}
\item[\errcode{ENOMEM}] non c'è memoria sufficiente per creare un nuovo file
descriptor di \func{signalfd}.
\item[\errcode{ENODEV}] il kernel non può montare internamente il
- dispositivo per la gestione anonima degli inode associati al file
- descriptor.
+ dispositivo per la gestione anonima degli \itindex{inode} \textit{inode}
+ associati al file descriptor.
\end{errlist}
ed inoltre \errval{EMFILE} e \errval{ENFILE}.
}
\label{fig:fiforeporter_code_init}
\end{figure}
-Il primo passo (\texttt{\small 19--20}) è la crezione di un file descriptor
+Il primo passo (\texttt{\small 19--20}) è la creazione di un file descriptor
\texttt{epfd} di \itindex{epoll} \textit{epoll} con \func{epoll\_create} che è
quello che useremo per il controllo degli altri. É poi necessario
disabilitare la ricezione dei segnali (nel caso \signal{SIGINT},
cui ci siano dati pronti in lettura sulla fifo e che il file descriptor pronto
corrisponda (\texttt{\small 26}) a \var{fifofd}. Di nuovo si effettueranno le
letture in un ciclo (\texttt{\small 28--39}) ripetendole fin tanto che la
-funzione \func{read} non resituisce un errore di \errcode{EAGAIN}
+funzione \func{read} non restituisce un errore di \errcode{EAGAIN}
(\texttt{\small 29--35}).\footnote{il procedimento è lo stesso adottato per il
file descriptor associato al segnale, in cui si esce dal programma in caso
di errore reale, in questo caso però alla fine dei dati prima di uscire si
\item[\errcode{ENOMEM}] non c'è memoria sufficiente per creare un nuovo file
descriptor di \func{signalfd}.
\item[\errcode{ENODEV}] il kernel non può montare internamente il
- dispositivo per la gestione anonima degli inode associati al file
- descriptor.
+ dispositivo per la gestione anonima degli \itindex{inode} \textit{inode}
+ associati al file descriptor.
\end{errlist}
ed inoltre \errval{EMFILE} e \errval{ENFILE}.
}
\const{IN\_ACCESS} &$\bullet$& C'è stato accesso al file in
lettura.\\
\const{IN\_ATTRIB} &$\bullet$& Ci sono stati cambiamenti sui dati
- dell'inode (o sugli attributi
- estesi, vedi
+ dell'\itindex{inode} \textit{inode}
+ (o sugli attributi estesi, vedi
sez.~\ref{sec:file_xattr}).\\
\const{IN\_CLOSE\_WRITE} &$\bullet$& È stato chiuso un file aperto in
scrittura.\\
% http://lwn.net/Articles/432757/
-% LocalWords: dell'I locking multiplexing cap sez system call socket BSD
+% LocalWords: dell'I locking multiplexing cap sez system call socket BSD GID
% LocalWords: descriptor client deadlock NONBLOCK EAGAIN polling select kernel
% LocalWords: pselect like sys unistd int fd readfds writefds exceptfds struct
% LocalWords: timeval errno EBADF EINTR EINVAL ENOMEM sleep tab signal void of
% LocalWords: ENFILE lenght segment violation SIGSEGV FIXED msync munmap copy
% LocalWords: DoS Denial Service EXECUTABLE NORESERVE LOCKED swapping stack fs
% LocalWords: GROWSDOWN ANON POPULATE prefaulting SIGBUS fifo VME fork old SFD
-% LocalWords: exec atime ctime mtime mprotect addr mremap address new
+% LocalWords: exec atime ctime mtime mprotect addr mremap address new Failed
% LocalWords: long MAYMOVE realloc VMA virtual Ingo Molnar remap pages pgoff
% LocalWords: dall' fault cache linker prelink advisory discrectionary lock fl
% LocalWords: flock shared exclusive operation dup inode linked NFS cmd ENOLCK
-% LocalWords: EDEADLK whence SEEK CUR type pid GETLK SETLK SETLKW all'inode HP
+% LocalWords: EDEADLK whence SEEK CUR type pid GETLK SETLK SETLKW HP EACCESS
% LocalWords: switch bsd lockf mandatory SVr sgid group root mount mand TRUNC
% LocalWords: SVID UX Documentation sendfile dnotify inotify NdA ppoll fds add
% LocalWords: init EMFILE FIONREAD ioctl watch char pathname uint mask ENOSPC
-% LocalWords: dell'inode CLOSE NOWRITE MOVE MOVED FROM TO rm wd event page ctl
+% LocalWords: CLOSE NOWRITE MOVE MOVED FROM TO rm wd event page ctl acquired
% LocalWords: attribute Universe epoll Solaris kqueue level triggered Jonathan
% LocalWords: Lemon BSDCON edge Libenzi kevent backporting epfd EEXIST ENOENT
% LocalWords: MOD wait EPOLLIN EPOLLOUT EPOLLRDHUP SOCK EPOLLPRI EPOLLERR one
% LocalWords: MERGEABLE EOVERFLOW prealloca hole FALLOC KEEP stat fstat union
% LocalWords: conditions sigwait CLOEXEC signalfd sizemask SIGKILL SIGSTOP ssi
% LocalWords: sigwaitinfo FifoReporter Windows ptr sigqueue named timerfd TFD
-% LocalWords: clockid CLOCK MONOTONIC REALTIME itimerspec interval
-% LocalWords: ABSTIME gettime
+% LocalWords: clockid CLOCK MONOTONIC REALTIME itimerspec interval Resource
+% LocalWords: ABSTIME gettime temporarily unavailable SIGINT SIGQUIT SIGTERM
%%% Local Variables:
%%% mode: latex
%%% TeX-master: "gapil"
%%% End:
+% LocalWords: sigfd fifofd break siginf names starting echo Message from Got
+% LocalWords: message kill received means exit