\bodydesc{La funzione in caso di successo restituisce il numero di file
descriptor (anche nullo) che sono attivi, e -1 in caso di errore, nel qual
- caso \var{errno} viene impostata ai valori:
+ caso \var{errno} assumerà uno dei valori:
\begin{errlist}
\item[\macro{EBADF}] Si è specificato un file descriptor sbagliato in uno
degli insiemi.
System V ha introdotto una sua interfaccia per gestire l'\textit{I/O
multiplexing}, basata sulla funzione \func{poll},\footnote{la funzione è
prevista dallo standard XPG4, ed è stata introdotta in Linux come system
- call a partire dal kernel 2.1.23 e dalle \acr{libc} 5.4.28.} il cui prototipo è:
+ call a partire dal kernel 2.1.23 e dalle \acr{libc} 5.4.28.} il cui
+prototipo è:
\begin{prototype}{sys/poll.h}
{int poll(struct pollfd *ufds, unsigned int nfds, int timeout)}
\bodydesc{La funzione restituisce il numero di file descriptor con attività in
caso di successo, o 0 se c'è stato un timeout; in caso di errore viene
- restituito -1 ed \var{errno} viene impostata ai valori:
+ restituito -1 ed \var{errno} assumerà uno dei valori:
\begin{errlist}
\item[\macro{EBADF}] Si è specificato un file descriptor sbagliato in uno
degli insiemi.
\bodydesc{La funzione in caso di successo restituisce il numero di file
descriptor (anche nullo) che sono attivi, e -1 in caso di errore, nel qual
- caso \var{errno} viene impostata ai valori:
+ caso \var{errno} assumerà uno dei valori:
\begin{errlist}
\item[\macro{EBADF}] Si è specificato un file descriptor sbagliato in uno
degli insiemi.
-\subsection{L'\textsl{I/O asincrono}}
+\subsection{L'I/O asincrono}
\label{sec:file_asyncronous_io}
Una modalità alternativa all'uso dell'\textit{I/O multiplexing} è quella di
\secref{sec:sig_sigaction}).
Per far questo però occorre utilizzare le funzionalità dei segnali real-time
-(vedi \secref{sec:sig_real_time}) imopstando esplicitamente con il comando
+(vedi \secref{sec:sig_real_time}) impostando esplicitamente con il comando
\macro{F\_SETSIG} di \func{fcntl} un segnale real-time da inviare in caso di
I/O asincrono (il segnale predefinito è \macro{SIGIO}). In questo caso il
manipolatore tutte le volte che riceverà \macro{SI\_SIGIO} come valore del
di thread. Al momento\footnote{fino ai kernel della serie 2.4.x, nella serie
2.5.x è però iniziato un lavoro completo di riscrittura di tutto il sistema
di I/O, che prevede anche l'introduzione di un nuovo layer per l'I/O
- asincrono.} esiste una sola versione stabile di questa interfaccia, quella
-delle \acr{glibc}, che è realizzata completamente in user space. Esistono
-comunque vari progetti sperimentali (come il KAIO della SGI, o i patch di
-Benjamin La Haise) che prevedono un supporto diretto da parte del kernel.
+ asincrono (effettuato a partire dal 2.5.32).} esiste una sola versione
+stabile di questa interfaccia, quella delle \acr{glibc}, che è realizzata
+completamente in user space. Esistono comunque vari progetti sperimentali
+(come il KAIO della SGI, o i patch di Benjamin La Haise) che prevedono un
+supporto diretto da parte del kernel.
Lo standard prevede che tutte le operazioni di I/O asincrono siano controllate
attraverso l'uso di una apposita struttura \type{aiocb} (il cui nome sta per
\param{aiocbp}.
\bodydesc{Le funzioni restituiscono 0 in caso di successo, e -1 in caso di
- errore, nel qual caso \var{errno} viene impostata ai valori:
+ errore, nel qual caso \var{errno} assumerà uno dei valori:
\begin{errlist}
\item[\macro{EBADF}] Si è specificato un file descriptor sbagliato.
\item[\macro{ENOSYS}] La funzione non è implementata.
operazioni specificate da \param{list}.
\bodydesc{La funzione restituisce 0 se una (o più) operazioni sono state
- completate, e -1 in caso di errore nel qual caso \var{errno} viene
- impostata ai valori:
+ completate, e -1 in caso di errore nel qual caso \var{errno} assumerà uno
+ dei valori:
\begin{errlist}
\item[\macro{EAGAIN}] Nessuna operazione è stata completata entro
\param{timeout}.
secondo la modalità \param{mode}.
\bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di
- errore, nel qual caso \var{errno} viene impostata ai valori:
+ errore, nel qual caso \var{errno} assumerà uno dei valori:
\begin{errlist}
\item[\macro{EAGAIN}] Nessuna operazione è stata completata entro
\param{timeout}.
specificati da \param{vector}.
\bodydesc{Le funzioni restituiscono il numero di byte letti o scritti in
- caso di successo, e -1 in caso di errore, nel qual caso \var{errno} viene
- impostata ai valori:
+ caso di successo, e -1 in caso di errore, nel qual caso \var{errno}
+ assumerà uno dei valori:
\begin{errlist}
\item[\macro{EBADF}] si è specificato un file descriptor sbagliato.
\item[\macro{EINVAL}] si è specificato un valore non valido per uno degli
\param{count}. Ciascuna struttura dovrà essere inizializzata per
opportunamente per indicare i vari buffer da/verso i quali verrà eseguito il
trasferimento dei dati. Essi verranno letti (o scritti) nell'ordine in cui li
-si sono specificati nel vattore \var{vector}.
+si sono specificati nel vettore \var{vector}.
\subsection{File mappati in memoria}
\centering
\includegraphics[width=9.5cm]{img/mmap_layout}
\caption{Disposizione della memoria di un processo quando si esegue la
- mappatuara in memoria di un file.}
+ mappatura in memoria di un file.}
\label{fig:file_mmap_layout}
\end{figure}
\bodydesc{La funzione restituisce il puntatore alla zona di memoria mappata
in caso di successo, e \macro{MAP\_FAILED} (-1) in caso di errore, nel
- qual caso \var{errno} viene impostata ai valori:
+ qual caso \var{errno} assumerà uno dei valori:
\begin{errlist}
\item[\macro{EBADF}] Il file descriptor non è valido, e non si è usato
\macro{MAP\_ANONYMOUS}.
argomenti \param{fd} e \param{offset} sono
ignorati.\footnotemark\\
\macro{MAP\_ANON} & Sinonimo di \macro{MAP\_ANONYMOUS}, deprecato.\\
- \macro{MAP\_FILE} & Valore di compatibiità, deprecato.\\
+ \macro{MAP\_FILE} & Valore di compatibilità, deprecato.\\
\hline
\end{tabular}
\caption{Valori possibili dell'argomento \param{flag} di \func{mmap}.}
\centering
\includegraphics[width=10cm]{img/mmap_boundary}
\caption{Schema della mappatura in memoria di una sezione di file di
- dimensioni non corripondenti al bordo di una pagina.}
+ dimensioni non corrispondenti al bordo di una pagina.}
\label{fig:file_mmap_boundary}
\end{figure}
Sincronizza i contenuti di una sezione di un file mappato in memoria.
\bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di
- errore nel qual caso \var{errno} viene impostata ai valori:
+ errore nel qual caso \var{errno} assumerà uno dei valori:
\begin{errlist}
\item[\macro{EINVAL}] O \param{start} non è multiplo di \macro{PAGESIZE},
o si è specificato un valore non valido per \param{flags}.
Rilascia la mappatura sulla sezione di memoria specificata.
\bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di
- errore nel qual caso \var{errno} viene impostata ai valori:
+ errore nel qual caso \var{errno} assumerà uno dei valori:
\begin{errlist}
\item[\macro{EINVAL}] L'intervallo specificato non ricade in una zona
precedentemente mappata.
sovrapposizioni, e garantire la atomicità delle operazioni di scrittura.
+
\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 locking}, in
-quanto è il processo, e non il sistema, che si incarica di verificare se
-esiste una condizione di blocco per l'accesso ai file.
+unix-like è quella che viene usualmente chiamata \textit{advisory
+ locking},\footnote{Stevens in 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.
+
+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
+lettura che in scrittura) da parte di altri processi mentre lo si sta
+scrivendo. Si parla di \textsl{blocco esclusivo} appunto perché un solo
+processo alla volta può richiedere un \textit{exclusive lock} su un file per
+proteggere il suo accesso in scrittura.
+
+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
+System V), che è basata sulla funzione \func{fcntl}. I \textit{file lock}
+sono implementati in maniera completamente indipendente nelle due interfacce,
+che pertanto possono coesistere senza interferenze.
+
+Entrambe le interfacce prevedono la stessa procedura di funzionamento: si
+inizia sempre con il richiere l'opportuno \textit{file lock} (un
+\textit{exclusive lock} per una scrittura, uno \textit{shared lock} per una
+lettura) prima di eseguire l'accesso ad un file. Se il lock 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 lock. Si ricordi che
+la condizione per acquisire uno \textit{shared lock} è che il file non abbia
+già un \textit{exclusive lock} attivo, mentre per acquisire un
+\textit{exclusive lock} non deve essere presente nessun tipo di blocco.
+
+
+\subsection{La funzione \func{flock}}
+\label{sec:file_flock}
+
+
+La prima interfaccia per il 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} è \func{flock}, ed il suo prototipo è:
+\begin{prototype}{sys/file.h}{int flock(int fd, int operation)}
+
+ 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}
+ \item[\macro{EWOULDBLOCK}] Il file ha già un blocco attivo, e si è
+ specificato \macro{LOCK\_NB}.
+ \end{errlist}
+ }
+\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}.
+\begin{table}[htb]
+ \centering
+ \footnotesize
+ \begin{tabular}[c]{|l|l|}
+ \hline
+ \textbf{Valore} & \textbf{Significato} \\
+ \hline
+ \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\_NB} & Impedisce che la funzione si blocchi nella
+ richiesta di un \textit{file lock}.\\
+ \hline
+ \end{tabular}
+ \caption{Valori dell'argomento \param{operation} di \func{flock}.}
+ \label{tab:file_flock_operation}
+\end{table}
+
+I primi due valori, \macro{LOCK\_SH} e \macro{LOCK\_EX} permettono di
+richiedere un \textit{file lock}, ed ovviamente devono essere usati in maniera
+alternativa. Se si specifica anche \macro{LOCK\_NB} la funzione non si
+bloccherà qualora il lock non possa essere aqcuisito, ma ritornerà subito con
+un errore di \macro{EWOULDBLOCK}. Per rilasciare un lock si dovrà invece usare
+\macro{LOCK\_NB}.
+
+La semantica del file locking di BSD è diversa da quella del file locking
+POSIX, in particolare per quanto riguarda il comportamento dei lock nei
+confronti delle due funzioni \func{dup} e \func{fork}. Per capire queste
+differenze occore prima descrivere con maggiore dettaglio come viene
+realizzato il file locking nel kernel.
+
+In \figref{fig:file_flock_struct} si è riportato uno schema essenziale
+dell'implementazione del file locking in Linux; il punto fondamentale da
+capire è che un lock, qualunque sia l'interfaccia che si usa, anche se
+richiesto attraverso un file descriptor, agisce sempre su un file; perciò le
+informazioni relative agli eventuali \textit{file lock} sono mantenute a
+livello di inode,\footnote{in particolare, come accennato in
+ \figref{fig:file_flock_struct}, i \textit{file lock} sono mantenuti un una
+ \textit{linked list}\index{linked list} di strutture \var{file\_lock}. La
+ lista è referenziata dall'indirizzo di partenza mantenuto dal campo
+ \var{i\_flock} della struttura \var{inode} (per le definizioni esatte si
+ faccia riferimento al file \file{fs.h} nei sorgenti del kernel). Un bit del
+ campo \var{fl\_flags} di specifica se si tratta di un lock in semantica BSD
+ (\macro{FL\_FLOCK}) o POSIX (\macro{FL\_POSIX}).} dato che questo è l'unico
+riferimento in comune che possono avere due processi diversi che aprono lo
+stesso file.
+
+\begin{figure}[htb]
+ \centering
+ \includegraphics[width=13cm]{img/file_flock}
+ \caption{Schema dell'architettura del file locking, nel caso particolare
+ del suo utilizzo da parte dalla funzione \func{flock}.}
+ \label{fig:file_flock_struct}
+\end{figure}
+
+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 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.
+
+Questa struttura comporta 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 \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 delle funzioni \func{dup} e \func{fork}.
+
+Sarà cioè possibile rimuovere un file lock attraverso uno qualunque dei file
+descriptor che fanno riferimento alla stessa voce nella file table, quindi
+anche se questo è diverso da quello con cui lo si è
+creato,\footnote{attenzione, questo non vale se il file descriptor fa
+ riferimento allo stesso file, ma attraverso una voce diversa della file
+ table, come accade tutte le volte che si apre più volte lo stesso file.} o
+se si esegue la rimozione in un processo figlio; inoltre una volta tolto un
+file lock, la rimozione avrà effetto su tutti i file descriptor che
+condividono la stessa voce nella file table, e quindi, nel caso di file
+descriptor ereditati attraverso una \func{fork}, anche su processi diversi.
+
+Infine, per evitare che la terminazione imprevista di un processo lasci attivi
+dei file lock, è previsto che quando un file viene chiuso il kernel provveda
+anche a rimuovere tutti i lock ad esso associati. Anche in questo caso occorre
+tenere presente cosa succede quando si hanno file descriptor duplicati; in tal
+caso infatti il file non verrà effettivamente chiuso (ed il lock rimosso)
+fintanto che non viene rilasciata la relativa voce nella file table; la
+rimozione cioè avverrà solo quando tutti i file descriptor che fanno
+riferimento alla stessa voce sono stati chiusi, quindi, nel caso ci siano
+processi figli che mantengono ancora aperto un file descriptor, il lock non
+sarà rilasciato.
+
+
+\subsection{Il file locking POSIX}
+\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 funzionalità in
+\secref{sec:file_fcntl}, quando la si impiega per il \textit{file locking}
+però essa viene usata secondo il 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}
+ \item[\macro{EACCES}] L'operazione è proibita per la presenza di
+ \textit{file lock} da parte di altri processi.
+ \item[\macro{ENOLCK}] Il sistema non ha le risorse per il locking: ci sono
+ troppi segmenti di lock aperti, si è esaurita la tabella dei lock, o il
+ protocollo per il locking remoto è fallito.
+ \item[\macro{EDEADLK}] Si è richiesto un lock su una regione bloccata da
+ un altro processo che è a sua volta in attesa dello sblocco di un lock
+ mantenuto dal processo corrente; si avrebbe pertanto un
+ \textit{deadlock}. Non è garantito che il sistema riconosca sempre
+ questa situazione.
+ \item[\macro{EINTR}] La funzione è stata interrotta da un segnale prima di
+ poter acquisire un lock.
+ \end{errlist}
+ ed inoltre \macro{EBADF}, \macro{EFAULT}.
+ }
+\end{prototype}
+
+Si tenga presente che \func{flock} non è in grado di funzionare per i file
+manetenuti su NFS, in questo caso, se si ha la necessità di eseguire il
+\textit{file locking}, occorre usare l'interfaccia basata su \func{fcntl} che
+può funzionare anche attraverso NFS, a condizione che sia il client che il
+server supportino questa funzionalità.
+
+La standardizzatione operata con POSIX.1 ha adottato le API per il
+\textit{file locking} originarie di System V, basate sulla funzione
+
+
+
+Al contrario di \func{flock} con \func{fcntl} è possibile bloccare anche solo
+delle sezioni di un file. La funzione prende come argomento una struttura
+\var{flock} la cui definizione è riportata in \figref{fig:struct_flock}.
+
+
+\begin{figure}[!htb]
+ \footnotesize \centering
+ \begin{minipage}[c]{15cm}
+ \begin{lstlisting}[labelstep=0]{}%,frame=,indent=1cm]{}
+struct flock {
+ short int l_type; /* Type of lock: F_RDLCK, F_WRLCK, or F_UNLCK. */
+ short int l_whence; /* Where `l_start' is relative to (like `lseek'). */
+ off_t l_start; /* Offset where the lock begins. */
+ off_t l_len; /* Size of the locked area; zero means until EOF. */
+ pid_t l_pid; /* Process holding the lock. */
+};
+ \end{lstlisting}
+ \end{minipage}
+ \normalsize
+ \caption{La struttura \type{flock}, usata da \func{fcntl} per il file
+ locking.}
+ \label{fig:struct_flock}
+\end{figure}
Per poter utilizzare il \textit{mandatory locking} è stato introdotto un
utilizzo particolare del bit \acr{suid}. Se si ricorda quanto esposto in
-\secref{sec:file_suid_sgid}), esso viene di norma utlizzato per cambiare
-l'\textit{effective user ID} con cui viene eseguito un programma, ed è
-pertanto sempre associato alla presenza del permesso di esecuzione. Impostando
-questo bit su un file senza permesso di esecuzione in un sistema che supporta
-il \textit{mandatory locking}, fa sì che quest'ultimo venga attivato per il
-file in questione. In questo modo una combinaizone dei permessi
-originariamente non contemplata, in quanto senza significato, diventa
-l'indicazione della presenza o meno del \textit{mandatory locking}.
+\secref{sec:file_suid_sgid}), esso viene di norma utilizzato per cambiare
+l'userid effettivo con cui viene eseguito un programma, ed è pertanto sempre
+associato alla presenza del permesso di esecuzione. Impostando questo bit su
+un file senza permesso di esecuzione in un sistema che supporta il
+\textit{mandatory locking}, fa sì che quest'ultimo venga attivato per il file
+in questione. In questo modo una combinazione dei permessi originariamente non
+contemplata, in quanto senza significato, diventa l'indicazione della presenza
+o meno del \textit{mandatory locking}.