-\begin{table}[htb]
- \centering
- \footnotesize
- \begin{tabular}[c]{|l|c|c|c|}
- \hline
- \textbf{Richiesta} & \multicolumn{3}{|c|}{\textbf{Stato del file}}\\
- \cline{2-4}
- &Nessun \textit{lock}&\textit{Read lock}&\textit{Write lock}\\
- \hline
- \hline
- \textit{Read lock} & SI & SI & NO \\
- \textit{Write lock}& SI & NO & NO \\
- \hline
- \end{tabular}
- \caption{Tipologie di \textit{file locking}.}
- \label{tab:file_file_lock}
-\end{table}
-
-Si tenga presente infine che il controllo di accesso e la gestione dei
-permessi viene effettuata quando si apre un file, l'unico controllo residuo
-che si può avere riguardo il \textit{file locking} è che il tipo di blocco che
-si vuole ottenere su un file deve essere compatibile con le modalità di
-apertura dello stesso (in lettura per un \textit{read lock} e in scrittura per
-un \textit{write 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 \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 è:
-\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[\errcode{EWOULDBLOCK}] il file ha già un blocco attivo, e si è
- specificato \const{LOCK\_NB}.
- \end{errlist}
- }
-\end{prototype}
-
-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 costruendo il valore con un OR aritmetico delle costanti
-riportate in tab.~\ref{tab:file_flock_operation}.
-
-\begin{table}[htb]
- \centering
- \footnotesize
- \begin{tabular}[c]{|l|p{6cm}|}
- \hline
- \textbf{Valore} & \textbf{Significato} \\
- \hline
- \hline
- \const{LOCK\_SH} & Richiede uno \textit{shared lock} sul file.\\
- \const{LOCK\_EX} & Richiede un \textit{esclusive lock} sul file.\\
- \const{LOCK\_UN} & Rilascia il \textit{file lock}.\\
- \const{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, \const{LOCK\_SH} e \const{LOCK\_EX} permettono di
-richiedere un \textit{file lock}, ed ovviamente devono essere usati in maniera
-alternativa. Se si specifica anche \const{LOCK\_NB} la funzione non si
-bloccherà qualora il \textit{file lock} non possa essere acquisito, ma
-ritornerà subito con un errore di \errcode{EWOULDBLOCK}. Per rilasciare un
-\textit{file lock} si dovrà invece usare \const{LOCK\_UN}.
-
-Si tenga presente che non esiste una modalità per eseguire atomicamente un
-cambiamento del tipo di blocco (da \textit{shared lock} a \textit{esclusive
- lock}), il blocco deve essere prima rilasciato e poi richiesto, ed è sempre
-possibile che nel frattempo abbia successo un'altra richiesta pendente,
-facendo fallire la riacquisizione.
-
-Si tenga presente infine che \func{flock} non è supportata per i file
-mantenuti su NFS, in questo caso, se si ha la necessità di utilizzare il
-\textit{file locking}, occorre usare l'interfaccia del \textit{file locking}
-POSIX basata su \func{fcntl} che è in grado di funzionare anche attraverso
-NFS, a condizione ovviamente che sia il client che il server supportino questa
-funzionalità.
-
-La semantica del \textit{file locking} di BSD inoltre è diversa da quella del
-\textit{file locking} POSIX, in particolare per quanto riguarda il
-comportamento dei \textit{file lock} nei confronti delle due funzioni
-\func{dup} e \func{fork}. Per capire queste differenze occorre descrivere con
-maggiore dettaglio come viene realizzato dal kernel il \textit{file locking}
-per entrambe le interfacce.
-
-In fig.~\ref{fig:file_flock_struct} si è riportato uno schema essenziale
-dell'implementazione del \textit{file locking} in stile BSD su Linux. Il punto
-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\index{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
- \struct{file\_lock}. La lista è referenziata dall'indirizzo di partenza
- mantenuto dal campo \var{i\_flock} della struttura \struct{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 (\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.
-
-\begin{figure}[htb]
- \centering
- \includegraphics[width=15.5cm]{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}
-\end{figure}
-
-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 \struct{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
- \struct{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.
-
-Questa struttura prevede che, quando si richiede la rimozione di un
-\textit{file lock}, il kernel acconsenta solo se la richiesta proviene da un
-file descriptor che fa riferimento ad una voce nella \itindex{file~table}
-\textit{file table} corrispondente a quella registrata nel blocco. Allora se
-ricordiamo quanto visto in sez.~\ref{sec:file_dup} e
-sez.~\ref{sec:file_sharing}, e cioè che i file descriptor duplicati e quelli
-ereditati in un processo figlio puntano sempre alla stessa voce nella
-\itindex{file~table} \textit{file table}, si può capire immediatamente quali
-sono le conseguenze nei confronti delle funzioni \func{dup} e \func{fork}.
-
-Sarà così possibile rimuovere un \textit{file lock} attraverso uno qualunque
-dei file descriptor che fanno riferimento alla stessa voce nella
-\itindex{file~table} \textit{file table}, 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 \itindex{file~table} \textit{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 \textit{file lock} su un file, la
-rimozione avrà effetto su tutti i file descriptor che condividono la stessa
-voce nella \itindex{file~table} \textit{file table}, e quindi, nel caso di
-file descriptor ereditati attraverso una \func{fork}, anche per processi
-diversi.
-
-Infine, per evitare che la terminazione imprevista di un processo lasci attivi
-dei \textit{file lock}, quando un file viene chiuso il kernel provvede anche a
-rimuovere tutti i blocchi 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 blocco rimosso)
-fintanto che non viene rilasciata la relativa voce nella \itindex{file~table}
-\textit{file table}; e questo avverrà solo quando tutti i file descriptor che
-fanno riferimento alla stessa voce sono stati chiusi. Quindi, nel caso ci
-siano duplicati o processi figli che mantengono ancora aperto un file
-descriptor, il \textit{file lock} non viene rilasciato.
-
-
-\subsection{Il \textit{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 possibilità di utilizzo in
-sez.~\ref{sec:file_fcntl}. 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}
- \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
- un \itindex{deadlock} \textit{deadlock}. Non è garantito che il sistema
- 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}
-
-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
-al singolo byte. Inoltre la funzione permette di ottenere alcune informazioni
-relative agli eventuali blocchi preesistenti. Per poter fare tutto questo la
-funzione utilizza come terzo argomento una apposita struttura \struct{flock}
-(la cui definizione è riportata in fig.~\ref{fig:struct_flock}) nella quale
-inserire tutti i dati relativi ad un determinato blocco. Si tenga presente poi
-che un \textit{file lock} fa sempre riferimento ad una regione, per cui si
-potrà avere un conflitto anche se c'è soltanto una sovrapposizione parziale
-con un'altra regione bloccata.
-
-\begin{figure}[!bht]
- \footnotesize \centering
- \begin{minipage}[c]{15cm}
- \includestruct{listati/flock.h}
- \end{minipage}
- \normalsize
- \caption{La struttura \structd{flock}, usata da \func{fcntl} per il
- \textit{file locking}.}
- \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
-lunghezza della sezione e infine \var{l\_whence} imposta il riferimento da cui
-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}).
-
-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
-estensione dello stesso resti coperta dal blocco. Inoltre se si specifica un
-valore nullo per \var{l\_len} il blocco si considera esteso fino alla
-dimensione massima del file; in questo modo è possibile bloccare una qualunque
-regione a partire da un certo punto fino alla fine del file, coprendo
-automaticamente quanto eventualmente aggiunto in coda allo stesso.
-
-\begin{table}[htb]
- \centering
- \footnotesize
- \begin{tabular}[c]{|l|l|}
- \hline
- \textbf{Valore} & \textbf{Significato} \\
- \hline
- \hline
- \const{F\_RDLCK} & Richiede un blocco condiviso (\textit{read lock}).\\
- \const{F\_WRLCK} & Richiede un blocco esclusivo (\textit{write lock}).\\
- \const{F\_UNLCK} & Richiede l'eliminazione di un \textit{file lock}.\\
- \hline
- \end{tabular}
- \caption{Valori possibili per il campo \var{l\_type} di \struct{flock}.}
- \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 \acr{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}, specifica
-l'azione da compiere; i valori 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
- sovrascrive la struttura \param{flock} con i valori relativi al blocco già
- esistente che ne blocca l'acquisizione, altrimenti si limita a impostarne il
- 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
- 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
- con un errore di \errcode{EINTR}.
-\end{basedescript}
-
-Si noti che per quanto detto il comando \const{F\_GETLK} non serve a rilevare
-una presenza generica di blocco su un file, perché se ne esistono altri
-compatibili con quello richiesto, la funzione ritorna comunque impostando
-\var{l\_type} a \const{F\_UNLCK}. Inoltre a seconda del valore di
-\var{l\_type} si potrà controllare o l'esistenza di un qualunque tipo di
-blocco (se è \const{F\_WRLCK}) o di \textit{write lock} (se è
-\const{F\_RDLCK}). Si consideri poi che può esserci più di un blocco che
-impedisce l'acquisizione di quello richiesto (basta che le regioni si
-sovrappongano), ma la funzione ne riporterà sempre soltanto uno, impostando
-\var{l\_whence} a \const{SEEK\_SET} ed i valori \var{l\_start} e \var{l\_len}
-per indicare quale è la regione bloccata.
-
-Infine si tenga presente che effettuare un controllo con il comando
-\const{F\_GETLK} e poi tentare l'acquisizione con \const{F\_SETLK} non è una
-operazione atomica (un altro processo potrebbe acquisire un blocco fra le due
-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 \const{F\_SETLK}, per controllare che il blocco sia
-stato effettivamente acquisito.
-
-\begin{figure}[htb]
- \centering \includegraphics[width=9cm]{img/file_lock_dead}
- \caption{Schema di una situazione di \itindex{deadlock} \textit{deadlock}.}
- \label{fig:file_flock_dead}
-\end{figure}
-
-Non operando a livello di interi file, il \textit{file locking} POSIX
-introduce un'ulteriore complicazione; consideriamo la situazione illustrata in
-fig.~\ref{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 \itindex{deadlock} \textit{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 \errcode{EDEADLK} alla funzione che
-cerca di acquisire un blocco che porterebbe ad un \itindex{deadlock}
-\textit{deadlock}.
-
-Per capire meglio il funzionamento del \textit{file locking} in semantica
-POSIX (che differisce alquanto rispetto da quello di BSD, visto
-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 \struct{file\_lock} significativi per la
- semantica POSIX, in particolare adesso ciascuna struttura contiene, oltre al
- \acr{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 \index{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
-\acr{pid} del processo.
-
-\begin{figure}[!bht]
- \centering \includegraphics[width=13cm]{img/file_posix_lock}
- \caption{Schema dell'architettura del \textit{file locking}, nel caso
- particolare del suo utilizzo secondo l'interfaccia standard POSIX.}
- \label{fig:file_posix_lock}
-\end{figure}
-
-Quando si richiede un \textit{file lock} il kernel effettua una scansione di
-tutti i blocchi presenti sul file\footnote{scandisce cioè la
- \itindex{linked~list} \textit{linked list} delle strutture
- \struct{file\_lock}, scartando automaticamente quelle per cui
- \var{fl\_flags} non è \const{FL\_POSIX}, così che le due interfacce restano
- ben separate.} per verificare se la regione richiesta non si sovrappone ad
-una già bloccata, in caso affermativo decide in base al tipo di blocco, in
-caso negativo il nuovo blocco viene comunque acquisito ed aggiunto alla lista.
-
-Nel caso di rimozione invece questa viene effettuata controllando che il
-\acr{pid} del processo richiedente corrisponda a quello contenuto nel blocco.
-Questa diversa modalità ha delle conseguenze precise riguardo il comportamento
-dei \textit{file lock} POSIX. La prima conseguenza è che un \textit{file lock}
-POSIX non viene mai ereditato attraverso una \func{fork}, dato che il processo
-figlio avrà un \acr{pid} diverso, mentre passa indenne attraverso una
-\func{exec} in quanto il \acr{pid} resta lo stesso. Questo comporta che, al
-contrario di quanto avveniva con la semantica BSD, quando un processo termina
-tutti i \textit{file lock} da esso detenuti vengono immediatamente rilasciati.
-
-La seconda conseguenza è che qualunque file descriptor che faccia riferimento
-allo stesso file (che sia stato ottenuto con una \func{dup} o con una
-\func{open} in questo caso non fa differenza) può essere usato per rimuovere
-un blocco, dato che quello che conta è solo il \acr{pid} del processo. Da
-questo deriva una ulteriore sottile differenza di comportamento: dato che alla
-chiusura di un file i blocchi ad esso associati vengono rimossi, nella
-semantica POSIX basterà chiudere un file descriptor qualunque per cancellare
-tutti i blocchi relativi al file cui esso faceva riferimento, anche se questi
-fossero stati creati usando altri file descriptor che restano aperti.
-
-Dato che il controllo sull'accesso ai blocchi viene eseguito sulla base del
-\acr{pid} del processo, possiamo anche prendere in considerazione un altro
-degli aspetti meno chiari di questa interfaccia e cioè cosa succede quando si
-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 \acr{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
-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.
-
-\begin{figure}[!htb]
- \footnotesize \centering
- \begin{minipage}[c]{15cm}
- \includecodesample{listati/Flock.c}
- \end{minipage}
- \normalsize
- \caption{Sezione principale del codice del programma \file{Flock.c}.}
- \label{fig:file_flock_code}
-\end{figure}
-
-Per fare qualche esempio sul \textit{file locking} si è scritto un programma che
-permette di bloccare una sezione di un file usando la semantica POSIX, o un
-intero file usando la semantica BSD; in fig.~\ref{fig:file_flock_code} è
-riportata il corpo principale del codice del programma, (il testo completo è
-allegato nella directory dei sorgenti).
-
-La sezione relativa alla gestione delle opzioni al solito si è omessa, come la
-funzione che stampa le istruzioni per l'uso del programma, essa si cura di
-impostare le variabili \var{type}, \var{start} e \var{len}; queste ultime due
-vengono inizializzate al valore numerico fornito rispettivamente tramite gli
-switch \code{-s} e \cmd{-l}, mentre il valore della prima viene impostato con
-le opzioni \cmd{-w} e \cmd{-r} si richiede rispettivamente o un \textit{write
- lock} o \textit{read lock} (i due valori sono esclusivi, la variabile
-assumerà quello che si è specificato per ultimo). Oltre a queste tre vengono
-pure impostate la variabile \var{bsd}, che abilita la semantica omonima quando
-si invoca l'opzione \cmd{-f} (il valore preimpostato è nullo, ad indicare la
-semantica POSIX), e la variabile \var{cmd} che specifica la modalità di
-richiesta del \textit{file lock} (bloccante o meno), a seconda dell'opzione
-\cmd{-b}.
-
-Il programma inizia col controllare (\texttt{\small 11--14}) che venga passato
-un argomento (il file da bloccare), che sia stato scelto (\texttt{\small
- 15--18}) il tipo di blocco, dopo di che apre (\texttt{\small 19}) il file,
-uscendo (\texttt{\small 20--23}) in caso di errore. A questo punto il
-comportamento dipende dalla semantica scelta; nel caso sia BSD occorre
-reimpostare il valore di \var{cmd} per l'uso con \func{flock}; infatti il
-valore preimpostato fa riferimento alla semantica POSIX e vale rispettivamente
-\const{F\_SETLKW} o \const{F\_SETLK} a seconda che si sia impostato o meno la
-modalità bloccante.
-
-Nel caso si sia scelta la semantica BSD (\texttt{\small 25--34}) prima si
-controlla (\texttt{\small 27--31}) il valore di \var{cmd} per determinare se
-si vuole effettuare una chiamata bloccante o meno, reimpostandone il valore
-opportunamente, dopo di che a seconda del tipo di blocco al valore viene
-aggiunta la relativa opzione (con un OR aritmetico, dato che \func{flock}
-vuole un argomento \param{operation} in forma di maschera binaria. Nel caso
-invece che si sia scelta la semantica POSIX le operazioni sono molto più
-immediate, si prepara (\texttt{\small 36--40}) la struttura per il lock, e lo
-esegue (\texttt{\small 41}).
-
-In entrambi i casi dopo aver richiesto il blocco viene controllato il
-risultato uscendo (\texttt{\small 44--46}) in caso di errore, o stampando un
-messaggio (\texttt{\small 47--49}) in caso di successo. Infine il programma si
-pone in attesa (\texttt{\small 50}) finché un segnale (ad esempio un \cmd{C-c}
-dato da tastiera) non lo interrompa; in questo caso il programma termina, e
-tutti i blocchi vengono rilasciati.
-
-Con il programma possiamo fare varie verifiche sul funzionamento del
-\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
-Lock acquired
-\end{verbatim}%$
-\end{minipage}\vspace{1mm}
-\par\noindent
-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
-preimpostati che bloccano tutto il file. A questo punto se proviamo ad
-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
-Failed lock: Resource temporarily unavailable
-\end{verbatim}%$
-\end{minipage}\vspace{1mm}
-\par\noindent
-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
-Failed lock: Resource temporarily unavailable
-\end{verbatim}%$
-\end{minipage}\vspace{1mm}
-\par\noindent
-se invece blocchiamo una regione con:
-
-\vspace{1mm}
-\begin{minipage}[c]{12cm}
-\begin{verbatim}
-[piccardi@gont sources]$ ./flock -r -s0 -l10 Flock.c
-Lock acquired
-\end{verbatim}%$
-\end{minipage}\vspace{1mm}
-\par\noindent
-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
-Failed lock: Resource temporarily unavailable
-\end{verbatim}%$
-\end{minipage}\vspace{1mm}
-\par\noindent
-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
-Lock acquired
-\end{verbatim}%$
-\end{minipage}\vspace{1mm}
-\par\noindent
-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
-Failed lock: Resource temporarily unavailable
-\end{verbatim}%$
-\end{minipage}\vspace{1mm}
-\par\noindent
-come ci aspettiamo questo non sarà consentito.
-
-Il programma di norma esegue il tentativo di acquisire il lock in modalità non
-bloccante, se però usiamo l'opzione \cmd{-b} possiamo impostare la modalità
-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
-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
-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
-Lock acquired
-\end{verbatim}%$
-\end{minipage}\vspace{3mm}
-\par\noindent
-
-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 è
-ottenuto un blocco in lettura sull'intero file, possiamo verificare cosa
-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
-Lock acquired
-\end{verbatim}
-\end{minipage}\vspace{1mm}
-\par\noindent
-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}
-
-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}
-
-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{table}[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}
-
-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}).
-
-
-
-\subsection{Il \textit{mandatory locking}}
-\label{sec:file_mand_locking}
-
-\itindbeg{mandatory~locking|(}
-
-Il \textit{mandatory locking} è una opzione introdotta inizialmente in SVr4,
-per introdurre un \textit{file locking} che, come dice il nome, fosse
-effettivo indipendentemente dai controlli eseguiti da un processo. Con il
-\textit{mandatory locking} infatti è possibile far eseguire il blocco del file
-direttamente al sistema, così che, anche qualora non si predisponessero le
-opportune verifiche nei processi, questo verrebbe comunque rispettato.
-
-Per poter utilizzare il \textit{mandatory locking} è stato introdotto un
-utilizzo particolare del bit \itindex{sgid~bit} \acr{sgid}. Se si ricorda
-quanto esposto in sez.~\ref{sec:file_special_perm}), esso viene di norma
-utilizzato per cambiare il group-ID effettivo con cui viene eseguito un
-programma, ed è pertanto sempre associato alla presenza del permesso di
-esecuzione per il gruppo. 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}.\footnote{un lettore attento potrebbe ricordare quanto detto in
- sez.~\ref{sec:file_perm_management} e cioè che il bit \acr{sgid} viene
- cancellato (come misura di sicurezza) quando di scrive su un file, questo
- non vale quando esso viene utilizzato per attivare il \textit{mandatory
- locking}.}
-
-L'uso del \textit{mandatory locking} presenta vari aspetti delicati, dato che
-neanche l'amministratore può passare sopra ad un \textit{file lock}; pertanto
-un processo che blocchi un file cruciale può renderlo completamente
-inaccessibile, rendendo completamente inutilizzabile il sistema\footnote{il
- problema si potrebbe risolvere rimuovendo il bit \itindex{sgid~bit}
- \acr{sgid}, ma non è detto che sia così facile fare questa operazione con un
- sistema bloccato.} inoltre con il \textit{mandatory locking} si può
-bloccare completamente un server NFS richiedendo una lettura su un file su cui
-è attivo un blocco. Per questo motivo l'abilitazione del \textit{mandatory
- locking} è di norma disabilitata, e deve essere attivata filesystem per
-filesystem in fase di montaggio (specificando l'apposita opzione di
-\func{mount} riportata in tab.~\ref{tab:sys_mount_flags}, o con l'opzione
-\code{-o mand} per il comando omonimo).
-
-Si tenga presente inoltre che il \textit{mandatory locking} funziona solo
-sull'interfaccia POSIX di \func{fcntl}. Questo ha due conseguenze: che non si
-ha nessun effetto sui \textit{file lock} richiesti con l'interfaccia di
-\func{flock}, e che la granularità del blocco è quella del singolo byte, come
-per \func{fcntl}.
-
-La sintassi di acquisizione dei blocchi è esattamente la stessa vista in
-precedenza per \func{fcntl} e \func{lockf}, la differenza è che in caso di
-\textit{mandatory lock} attivato non è più necessario controllare la
-disponibilità di accesso al file, ma si potranno usare direttamente le
-ordinarie funzioni di lettura e scrittura e sarà compito del kernel gestire
-direttamente il \textit{file locking}.
-
-Questo significa che in caso di \textit{read lock} la lettura dal file potrà
-avvenire normalmente con \func{read}, mentre una \func{write} si bloccherà
-fino al rilascio del blocco, a meno di non aver aperto il file con
-\const{O\_NONBLOCK}, nel qual caso essa ritornerà immediatamente con un errore
-di \errcode{EAGAIN}.
-
-Se invece si è acquisito un \textit{write lock} tutti i tentativi di leggere o
-scrivere sulla regione del file bloccata fermeranno il processo fino al
-rilascio del blocco, a meno che il file non sia stato aperto con
-\const{O\_NONBLOCK}, nel qual caso di nuovo si otterrà un ritorno immediato
-con l'errore di \errcode{EAGAIN}.
-
-Infine occorre ricordare che le funzioni di lettura e scrittura non sono le
-sole ad operare sui contenuti di un file, e che sia \func{creat} che
-\func{open} (quando chiamata con \const{O\_TRUNC}) effettuano dei cambiamenti,
-così come \func{truncate}, riducendone le dimensioni (a zero nei primi due
-casi, a quanto specificato nel secondo). Queste operazioni sono assimilate a
-degli accessi in scrittura e pertanto non potranno essere eseguite (fallendo
-con un errore di \errcode{EAGAIN}) su un file su cui sia presente un qualunque
-blocco (le prime due sempre, la terza solo nel caso che la riduzione delle
-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
-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
- diretto al file è consentito.} in Linux è stata però fatta la scelta
-implementativa\footnote{per i dettagli si possono leggere le note relative
- all'implementazione, mantenute insieme ai sorgenti del kernel nel file
- \file{Documentation/mandatory.txt}.} di seguire questo comportamento
-soltanto quando si chiama \func{mmap} con l'opzione \const{MAP\_SHARED} (nel
-qual caso la funzione fallisce con il solito \errcode{EAGAIN}) che comporta la
-possibilità di modificare il file.
-
-\index{file!locking|)}
-
-\itindend{mandatory~locking|(}
-
-
-\section{L'\textit{I/O multiplexing}}
-\label{sec:file_multiplexing}
-
-
-Uno dei problemi che si presentano quando si deve operare contemporaneamente
-su molti file usando le funzioni illustrate in
-cap.~\ref{cha:file_unix_interface} e cap.~\ref{cha:files_std_interface} è che
-si può essere bloccati nelle operazioni su un file mentre un altro potrebbe
-essere disponibile. L'\textit{I/O multiplexing} nasce risposta a questo
-problema. In questa sezione forniremo una introduzione a questa problematica
-ed analizzeremo le varie funzioni usate per implementare questa modalità di
-I/O.
-
-
-\subsection{La problematica dell'\textit{I/O multiplexing}}
-\label{sec:file_noblocking}
-
-Abbiamo visto in sez.~\ref{sec:sig_gen_beha}, affrontando la suddivisione fra
-\textit{fast} e \textit{slow} system call,\index{system~call~lente} che in
-certi casi le funzioni di I/O possono bloccarsi indefinitamente.\footnote{si
- ricordi però che questo può accadere solo per le pipe, i socket ed alcuni
- file di dispositivo\index{file!di~dispositivo}; sui file normali le funzioni
- di lettura e scrittura ritornano sempre subito.} Ad esempio le operazioni
-di lettura possono bloccarsi quando non ci sono dati disponibili sul
-descrittore su cui si sta operando.
-
-Questo comportamento causa uno dei problemi più comuni che ci si trova ad
-affrontare nelle operazioni di I/O, che si verifica quando si deve operare con
-più file descriptor eseguendo funzioni che possono bloccarsi senza che sia
-possibile prevedere quando questo può avvenire (il caso più classico è quello
-di un server in attesa di dati in ingresso da vari client). Quello che può
-accadere è di restare bloccati nell'eseguire una operazione su un file
-descriptor che non è ``\textsl{pronto}'', quando ce ne potrebbe essere un
-altro disponibile. Questo comporta nel migliore dei casi una operazione
-ritardata inutilmente nell'attesa del completamento di quella bloccata, mentre
-nel peggiore dei casi (quando la conclusione della operazione bloccata dipende
-da quanto si otterrebbe dal file descriptor ``\textsl{disponibile}'') si
-potrebbe addirittura arrivare ad un \itindex{deadlock} \textit{deadlock}.
-
-Abbiamo già accennato in sez.~\ref{sec:file_open} che è possibile prevenire
-questo tipo di comportamento delle funzioni di I/O aprendo un file in
-\textsl{modalità non-bloccante}, attraverso l'uso del flag \const{O\_NONBLOCK}
-nella chiamata di \func{open}. In questo caso le funzioni di input/output
-eseguite sul file che si sarebbero bloccate, ritornano immediatamente,
-restituendo l'errore \errcode{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 \itindex{polling} \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 superare questo problema è stato introdotto il concetto di \textit{I/O
- multiplexing}, una nuova modalità di operazioni che consente di tenere sotto
-controllo più file descriptor in contemporanea, permettendo di bloccare un
-processo quando le operazioni volute non sono possibili, e di riprenderne
-l'esecuzione una volta che almeno una di quelle richieste sia effettuabile, in
-modo da poterla eseguire con la sicurezza di non restare bloccati.
-
-Dato che, come abbiamo già accennato, per i normali file su disco non si ha
-mai un accesso bloccante, l'uso più comune delle funzioni che esamineremo nei
-prossimi paragrafi è per i server di rete, in cui esse vengono utilizzate per
-tenere sotto controllo dei socket; pertanto ritorneremo su di esse con
-ulteriori dettagli e qualche esempio di utilizzo concreto in
-sez.~\ref{sec:TCP_sock_multiplexing}.
-
-
-\subsection{Le funzioni \func{select} e \func{pselect}}
-\label{sec:file_select}
-
-Il primo kernel unix-like ad introdurre una interfaccia per l'\textit{I/O
- multiplexing} è stato BSD,\footnote{la funzione \func{select} è apparsa in
- BSD4.2 e standardizzata in BSD4.4, ma è stata portata su tutti i sistemi che
- supportano i socket, compreso le varianti di System V.} con la funzione
-\funcd{select}, il cui prototipo è:
-\begin{functions}
- \headdecl{sys/time.h}
- \headdecl{sys/types.h}
- \headdecl{unistd.h}
- \funcdecl{int select(int ndfs, fd\_set *readfds, fd\_set *writefds, fd\_set
- *exceptfds, struct timeval *timeout)}
-
- Attende che uno dei file descriptor degli insiemi specificati diventi
- attivo.
-
- \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} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EBADF}] si è specificato un file descriptor sbagliato in uno
- degli insiemi.
- \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale.
- \item[\errcode{EINVAL}] si è specificato per \param{ndfs} un valore negativo
- o un valore non valido per \param{timeout}.
- \end{errlist}
- ed inoltre \errval{ENOMEM}.
-}
-\end{functions}
-
-La funzione mette il processo in stato di \textit{sleep} (vedi
-tab.~\ref{tab:proc_proc_states}) fintanto che almeno uno dei file descriptor
-degli insiemi specificati (\param{readfds}, \param{writefds} e
-\param{exceptfds}), non diventa attivo, per un tempo massimo specificato da
-\param{timeout}.
-
-\itindbeg{file~descriptor~set}
-
-Per specificare quali file descriptor si intende selezionare la funzione usa
-un particolare oggetto, il \textit{file descriptor set}, identificato dal tipo
-\type{fd\_set}, che serve ad identificare un insieme di file descriptor, in
-maniera analoga a come un \itindex{signal~set} \textit{signal set} (vedi
-sez.~\ref{sec:sig_sigset}) identifica un insieme di segnali. Per la
-manipolazione di questi \textit{file descriptor set} si possono usare delle
-opportune macro di preprocessore:
-\begin{functions}
- \headdecl{sys/time.h}
- \headdecl{sys/types.h}
- \headdecl{unistd.h}
- \funcdecl{void \macro{FD\_ZERO}(fd\_set *set)}
- Inizializza l'insieme (vuoto).
-
- \funcdecl{void \macro{FD\_SET}(int fd, fd\_set *set)}
- Inserisce il file descriptor \param{fd} nell'insieme.
-
- \funcdecl{void \macro{FD\_CLR}(int fd, fd\_set *set)}
- Rimuove il file descriptor \param{fd} dall'insieme.
-
- \funcdecl{int \macro{FD\_ISSET}(int fd, fd\_set *set)}
- Controlla se il file descriptor \param{fd} è nell'insieme.
-\end{functions}
-
-In genere un \textit{file descriptor set} può contenere fino ad un massimo di
-\const{FD\_SETSIZE} file descriptor. Questo valore in origine corrispondeva
-al limite per il numero massimo di file aperti\footnote{ad esempio in Linux,
- fino alla serie 2.0.x, c'era un limite di 256 file per processo.}, ma da
-quando, come nelle versioni più recenti del kernel, questo limite è stato
-rimosso, esso indica le dimensioni massime dei numeri usati nei \textit{file
- descriptor set}.\footnote{il suo valore, secondo lo standard POSIX
- 1003.1-2001, è definito in \file{sys/select.h}, ed è pari a 1024.}
-
-Si tenga presente che i \textit{file descriptor set} devono sempre essere
-inizializzati con \macro{FD\_ZERO}; passare a \func{select} un valore non
-inizializzato può dar luogo a comportamenti non prevedibili; allo stesso modo
-usare \macro{FD\_SET} o \macro{FD\_CLR} con un file descriptor il cui valore
-eccede \const{FD\_SETSIZE} può dare luogo ad un comportamento indefinito.
-
-La funzione richiede di specificare tre insiemi distinti di file descriptor;
-il primo, \param{readfds}, verrà osservato per rilevare la disponibilità di
-effettuare una lettura,\footnote{per essere precisi la funzione ritornerà in
- tutti i casi in cui la successiva esecuzione di \func{read} risulti non
- bloccante, quindi anche in caso di \textit{end-of-file}; inoltre con Linux
- possono verificarsi casi particolari, ad esempio quando arrivano dati su un
- socket dalla rete che poi risultano corrotti e vengono scartati, può
- accadere che \func{select} riporti il relativo file descriptor come
- leggibile, ma una successiva \func{read} si blocchi.} il secondo,
-\param{writefds}, per verificare la possibilità di effettuare una scrittura ed
-il terzo, \param{exceptfds}, per verificare l'esistenza di eccezioni (come i
-dati urgenti \itindex{out-of-band} su un socket, vedi
-sez.~\ref{sec:TCP_urgent_data}).
-
-Dato che in genere non si tengono mai sotto controllo fino a
-\const{FD\_SETSIZE} file contemporaneamente la funzione richiede di
-specificare qual è il valore più alto fra i file descriptor indicati nei tre
-insiemi precedenti. Questo viene fatto per efficienza, per evitare di passare
-e far controllare al kernel una quantità di memoria superiore a quella
-necessaria. Questo limite viene indicato tramite l'argomento \param{ndfs}, che
-deve corrispondere al valore massimo aumentato di uno.\footnote{si ricordi che
- i file descriptor sono numerati progressivamente a partire da zero, ed il
- valore indica il numero più alto fra quelli da tenere sotto controllo;
- dimenticarsi di aumentare di uno il valore di \param{ndfs} è un errore
- comune.}
-
-Infine l'argomento \param{timeout}, espresso con una struttura di tipo
-\struct{timeval} (vedi fig.~\ref{fig:sys_timeval_struct}) specifica un tempo
-massimo di attesa prima che la funzione ritorni; se impostato a \val{NULL} la
-funzione attende indefinitamente. Si può specificare anche un tempo nullo
-(cioè una struttura \struct{timeval} con i campi impostati a zero), qualora si
-voglia semplicemente controllare lo stato corrente dei file descriptor.
-
-La funzione restituisce il numero di file descriptor pronti,\footnote{questo è
- il comportamento previsto dallo standard, ma la standardizzazione della
- funzione è recente, ed esistono ancora alcune versioni di Unix che non si
- comportano in questo modo.} e ciascun insieme viene sovrascritto per
-indicare quali sono i file descriptor pronti per le operazioni ad esso
-relative, in modo da poterli controllare con \macro{FD\_ISSET}. Se invece si
-ha un timeout viene restituito un valore nullo e gli insiemi non vengono
-modificati. In caso di errore la funzione restituisce -1, ed i valori dei tre
-insiemi sono indefiniti e non si può fare nessun affidamento sul loro
-contenuto.
-
-\itindend{file~descriptor~set}
-
-Una volta ritornata la funzione si potrà controllare quali sono i file
-descriptor pronti ed operare su di essi, si tenga presente però che si tratta
-solo di un suggerimento, esistono infatti condizioni\footnote{ad esempio
- quando su un socket arrivano dei dati che poi vengono scartati perché
- corrotti.} in cui \func{select} può riportare in maniera spuria che un file
-descriptor è pronto in lettura, quando una successiva lettura si bloccherebbe.
-Per questo quando si usa \textit{I/O multiplexing} è sempre raccomandato l'uso
-delle funzioni di lettura e scrittura in modalità non bloccante.
-
-In Linux \func{select} modifica anche il valore di \param{timeout},
-impostandolo al tempo restante, quando la funzione viene interrotta da un
-segnale. In tal caso infatti si ha un errore di \errcode{EINTR}, ed occorre
-rilanciare la funzione; in questo modo non è necessario ricalcolare tutte le
-volte il tempo rimanente. Questo può causare problemi di portabilità sia
-quando si usa codice scritto su Linux che legge questo valore, sia quando si
-usano programmi scritti per altri sistemi che non dispongono di questa
-caratteristica e ricalcolano \param{timeout} tutte le volte.\footnote{in
- genere questa caratteristica è disponibile nei sistemi che derivano da
- System V e non è disponibile per quelli che derivano da BSD; lo standard
- POSIX.1-2001 non permette questo comportamento.}
-
-Uno dei problemi che si presentano con l'uso di \func{select} è che il suo
-comportamento dipende dal valore del file descriptor che si vuole tenere sotto
-controllo. Infatti il kernel riceve con \param{ndfs} un limite massimo per
-tale valore, e per capire quali sono i file descriptor da tenere sotto
-controllo dovrà effettuare una scansione su tutto l'intervallo, che può anche
-essere molto ampio anche se i file descriptor sono solo poche unità; tutto ciò
-ha ovviamente delle conseguenze ampiamente negative per le prestazioni.
-
-Inoltre c'è anche il problema che il numero massimo dei file che si possono
-tenere sotto controllo, la funzione è nata quando il kernel consentiva un
-numero massimo di 1024 file descriptor per processo, adesso che il numero può
-essere arbitrario si viene a creare una dipendenza del tutto artificiale dalle
-dimensioni della struttura \type{fd\_set}, che può necessitare di essere
-estesa, con ulteriori perdite di prestazioni.
-
-Lo standard POSIX è rimasto a lungo senza primitive per l'\textit{I/O
- multiplexing}, introdotto solo con le ultime revisioni dello standard (POSIX
-1003.1g-2000 e POSIX 1003.1-2001). La scelta è stata quella di seguire
-l'interfaccia creata da BSD, ma prevede che tutte le funzioni ad esso relative
-vengano dichiarate nell'header \file{sys/select.h}, che sostituisce i
-precedenti, ed inoltre aggiunge a \func{select} una nuova funzione
-\funcd{pselect},\footnote{il supporto per lo standard POSIX 1003.1-2001, ed
- l'header \file{sys/select.h}, compaiono in Linux a partire dalle \acr{glibc}
- 2.1. Le \acr{libc4} e \acr{libc5} non contengono questo header, le
- \acr{glibc} 2.0 contengono una definizione sbagliata di \func{psignal},
- senza l'argomento \param{sigmask}, la definizione corretta è presente dalle
- \acr{glibc} 2.1-2.2.1 se si è definito \macro{\_GNU\_SOURCE} e nelle
- \acr{glibc} 2.2.2-2.2.4 se si è definito \macro{\_XOPEN\_SOURCE} con valore
- maggiore di 600.} il cui prototipo è:
-\begin{prototype}{sys/select.h}
- {int pselect(int n, fd\_set *readfds, fd\_set *writefds, fd\_set *exceptfds,
- struct timespec *timeout, sigset\_t *sigmask)}
-
- Attende che uno dei file descriptor degli insiemi specificati diventi
- attivo.
-
- \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} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EBADF}] si è specificato un file descriptor sbagliato in uno
- degli insiemi.
- \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale.
- \item[\errcode{EINVAL}] si è specificato per \param{ndfs} un valore negativo
- o un valore non valido per \param{timeout}.
- \end{errlist}
- ed inoltre \errval{ENOMEM}.}
-\end{prototype}
-
-La funzione è sostanzialmente identica a \func{select}, solo che usa una
-struttura \struct{timespec} (vedi fig.~\ref{fig:sys_timespec_struct}) per
-indicare con maggiore precisione il timeout e non ne aggiorna il valore in
-caso di interruzione.\footnote{in realtà la system call di Linux aggiorna il
- valore al tempo rimanente, ma la funzione fornita dalle \acr{glibc} modifica
- questo comportamento passando alla system call una variabile locale, in modo
- da mantenere l'aderenza allo standard POSIX che richiede che il valore di
- \param{timeout} non sia modificato.} Inoltre prende un argomento aggiuntivo
-\param{sigmask} che è il puntatore ad una maschera di segnali (si veda
-sez.~\ref{sec:sig_sigmask}). La maschera corrente viene sostituita da questa
-immediatamente prima di eseguire l'attesa, e ripristinata al ritorno della
-funzione.
-
-L'uso di \param{sigmask} è stato introdotto allo scopo di prevenire possibili
-\textit{race condition} \itindex{race~condition} quando ci si deve porre in
-attesa sia di un segnale che di dati. La tecnica classica è quella di
-utilizzare il gestore per impostare una variabile globale e controllare questa
-nel corpo principale del programma; abbiamo visto in
-sez.~\ref{sec:sig_example} come questo lasci spazio a possibili race
-condition, per cui diventa essenziale utilizzare \func{sigprocmask} per
-disabilitare la ricezione del segnale prima di eseguire il controllo e
-riabilitarlo dopo l'esecuzione delle relative operazioni, onde evitare
-l'arrivo di un segnale immediatamente dopo il controllo, che andrebbe perso.
-
-Nel nostro caso il problema si pone quando oltre al segnale si devono tenere
-sotto controllo anche dei file descriptor con \func{select}, in questo caso si
-può fare conto sul fatto che all'arrivo di un segnale essa verrebbe interrotta
-e si potrebbero eseguire di conseguenza le operazioni relative al segnale e
-alla gestione dati con un ciclo del tipo:
-\includecodesnip{listati/select_race.c}
-qui però emerge una \itindex{race~condition} \textit{race condition}, perché
-se il segnale arriva prima della chiamata a \func{select}, questa non verrà
-interrotta, e la ricezione del segnale non sarà rilevata.
-
-Per questo è stata introdotta \func{pselect} che attraverso l'argomento
-\param{sigmask} permette di riabilitare la ricezione il segnale
-contestualmente all'esecuzione della funzione,\footnote{in Linux però, fino al
- kernel 2.6.16, non era presente la relativa system call, e la funzione era
- implementata nelle \acr{glibc} attraverso \func{select} (vedi \texttt{man
- select\_tut}) per cui la possibilità di \itindex{race~condition}
- \textit{race condition} permaneva; in tale situazione si può ricorrere ad una
- soluzione alternativa, chiamata \itindex{self-pipe trick} \textit{self-pipe
- trick}, che consiste nell'aprire una pipe (vedi sez.~\ref{sec:ipc_pipes})
- ed usare \func{select} sul capo in lettura della stessa; si può indicare
- l'arrivo di un segnale scrivendo sul capo in scrittura all'interno del
- gestore dello stesso; in questo modo anche se il segnale va perso prima
- della chiamata di \func{select} questa lo riconoscerà comunque dalla
- presenza di dati sulla pipe.} ribloccandolo non appena essa ritorna, così
-che il precedente codice potrebbe essere riscritto nel seguente modo:
-\includecodesnip{listati/pselect_norace.c}
-in questo caso utilizzando \var{oldmask} durante l'esecuzione di
-\func{pselect} la ricezione del segnale sarà abilitata, ed in caso di
-interruzione si potranno eseguire le relative operazioni.
-
-
-\subsection{Le funzioni \func{poll} e \func{ppoll}}
-\label{sec:file_poll}
-
-Nello sviluppo di System V, invece di utilizzare l'interfaccia di
-\func{select}, che è una estensione tipica di BSD, è stata introdotta un'altra
-interfaccia, basata sulla funzione \funcd{poll},\footnote{la funzione è
- prevista dallo standard XPG4, ed è stata introdotta in Linux come system
- call a partire dal kernel 2.1.23 ed inserita nelle \acr{libc} 5.4.28.} il
-cui prototipo è:
-\begin{prototype}{sys/poll.h}
- {int poll(struct pollfd *ufds, unsigned int nfds, int timeout)}
-
- La funzione attende un cambiamento di stato su un insieme di file
- descriptor.
-
- \bodydesc{La funzione restituisce il numero di file descriptor con attività
- in caso di successo, o 0 se c'è stato un timeout e -1 in caso di errore,
- ed in quest'ultimo caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EBADF}] si è specificato un file descriptor sbagliato in uno
- degli insiemi.
- \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale.
- \item[\errcode{EINVAL}] il valore di \param{nfds} eccede il limite
- \macro{RLIMIT\_NOFILE}.
- \end{errlist}
- ed inoltre \errval{EFAULT} e \errval{ENOMEM}.}
-\end{prototype}
-
-La funzione permette di tenere sotto controllo contemporaneamente \param{ndfs}
-file descriptor, specificati attraverso il puntatore \param{ufds} ad un
-vettore di strutture \struct{pollfd}. Come con \func{select} si può
-interrompere l'attesa dopo un certo tempo, questo deve essere specificato con
-l'argomento \param{timeout} in numero di millisecondi: un valore negativo
-indica un'attesa indefinita, mentre un valore nullo comporta il ritorno
-immediato (e può essere utilizzato per impiegare \func{poll} in modalità
-\textsl{non-bloccante}).
-
-Per ciascun file da controllare deve essere inizializzata una struttura
-\struct{pollfd} nel vettore indicato dall'argomento \param{ufds}. La
-struttura, la cui definizione è riportata in fig.~\ref{fig:file_pollfd},
-prevede tre campi: in \var{fd} deve essere indicato il numero del file
-descriptor da controllare, in \var{events} deve essere specificata una
-maschera binaria di flag che indichino il tipo di evento che si vuole
-controllare, mentre in \var{revents} il kernel restituirà il relativo
-risultato. Usando un valore negativo per \param{fd} la corrispondente
-struttura sarà ignorata da \func{poll}. Dato che i dati in ingresso sono del
-tutto indipendenti da quelli in uscita (che vengono restituiti in
-\var{revents}) non è necessario reinizializzare tutte le volte il valore delle
-strutture \struct{pollfd} a meno di non voler cambiare qualche condizione.
-
-\begin{figure}[!htb]
- \footnotesize \centering
- \begin{minipage}[c]{15cm}
- \includestruct{listati/pollfd.h}
- \end{minipage}
- \normalsize
- \caption{La struttura \structd{pollfd}, utilizzata per specificare le
- modalità di controllo di un file descriptor alla funzione \func{poll}.}
- \label{fig:file_pollfd}
-\end{figure}
-
-Le costanti che definiscono i valori relativi ai bit usati nelle maschere
-binarie dei campi \var{events} e \var{revents} sono riportati in
-tab.~\ref{tab:file_pollfd_flags}, insieme al loro significato. Le si sono
-suddivise in tre gruppi, nel primo gruppo si sono indicati i bit utilizzati
-per controllare l'attività in ingresso, nel secondo quelli per l'attività in
-uscita, mentre il terzo gruppo contiene dei valori che vengono utilizzati solo
-nel campo \var{revents} per notificare delle condizioni di errore.
-
-\begin{table}[htb]
- \centering
- \footnotesize
- \begin{tabular}[c]{|l|l|}
- \hline
- \textbf{Flag} & \textbf{Significato} \\
- \hline
- \hline
- \const{POLLIN} & È possibile la lettura.\\
- \const{POLLRDNORM}& Sono disponibili in lettura dati normali.\\
- \const{POLLRDBAND}& Sono disponibili in lettura dati prioritari.\\
- \const{POLLPRI} & È possibile la lettura di \itindex{out-of-band} dati
- urgenti.\\
- \hline
- \const{POLLOUT} & È possibile la scrittura immediata.\\
- \const{POLLWRNORM}& È possibile la scrittura di dati normali.\\
- \const{POLLWRBAND}& È possibile la scrittura di dati prioritari.\\
- \hline
- \const{POLLERR} & C'è una condizione di errore.\\
- \const{POLLHUP} & Si è verificato un hung-up.\\
- \const{POLLRDHUP} & Si è avuta una \textsl{half-close} su un
- socket.\footnotemark\\
- \const{POLLNVAL} & Il file descriptor non è aperto.\\
- \hline
- \const{POLLMSG} & Definito per compatibilità con SysV.\\
- \hline
- \end{tabular}
- \caption{Costanti per l'identificazione dei vari bit dei campi
- \var{events} e \var{revents} di \struct{pollfd}.}
- \label{tab:file_pollfd_flags}
-\end{table}
-
-\footnotetext{si tratta di una estensione specifica di Linux, disponibile a
- partire dal kernel 2.6.17 definendo la marco \macro{\_GNU\_SOURCE}, che
- consente di riconoscere la chiusura in scrittura dell'altro capo di un
- socket, situazione che si viene chiamata appunto \itindex{half-close}
- \textit{half-close} (\textsl{mezza chiusura}) su cui torneremo con maggiori
- dettagli in sez.~\ref{sec:TCP_shutdown}.}
-
-Il valore \const{POLLMSG} non viene utilizzato ed è definito solo per
-compatibilità con l'implementazione di SysV che usa gli
-\textit{stream};\footnote{essi sono una interfaccia specifica di SysV non
- presente in Linux, e non hanno nulla a che fare con i file \textit{stream}
- delle librerie standard del C.} è da questi che derivano i nomi di alcune
-costanti, in quanto per essi sono definite tre classi di dati:
-\textsl{normali}, \textit{prioritari} ed \textit{urgenti}. In Linux la
-distinzione ha senso solo per i dati urgenti \itindex{out-of-band} dei socket
-(vedi sez.~\ref{sec:TCP_urgent_data}), ma su questo e su come \func{poll}
-reagisce alle varie condizioni dei socket torneremo in
-sez.~\ref{sec:TCP_serv_poll}, dove vedremo anche un esempio del suo utilizzo.
-
-Si tenga conto comunque che le costanti relative ai diversi tipi di dati
-normali e prioritari, vale a dire \const{POLLRDNORM}, \const{POLLWRNORM},
-\const{POLLRDBAND} e \const{POLLWRBAND} fanno riferimento alle implementazioni
-in stile SysV (in particolare le ultime due non vengono usate su Linux), e
-sono utilizzabili soltanto qualora si sia definita la macro
-\macro{\_XOPEN\_SOURCE}.\footnote{e ci si ricordi di farlo sempre in testa al
- file, definirla soltanto prima di includere \file{sys/poll.h} non è
- sufficiente.}
-
-In caso di successo funzione ritorna restituendo il numero di file (un valore
-positivo) per i quali si è verificata una delle condizioni di attesa richieste
-o per i quali si è verificato un errore, nel qual caso vengono utilizzati i
-valori di tab.~\ref{tab:file_pollfd_flags} esclusivi di \var{revents}. Un
-valore nullo indica che si è raggiunto il timeout, mentre un valore negativo
-indica un errore nella chiamata, il cui codice viene riportato al solito
-tramite \var{errno}.
-
-L'uso di \func{poll} consente di superare alcuni dei problemi illustrati in
-precedenza per \func{select}; anzitutto, dato che in questo caso si usa un
-vettore di strutture \struct{pollfd} di dimensione arbitraria, non esiste il
-limite introdotto dalle dimensioni massime di un \itindex{file~descriptor~set}
-\textit{file descriptor set} e la dimensione dei dati passati al kernel
-dipende solo dal numero dei file descriptor che si vogliono controllare, non
-dal loro valore.\footnote{anche se usando dei bit un \textit{file descriptor
- set} può essere più efficiente di un vettore di strutture \struct{pollfd},
- qualora si debba osservare un solo file descriptor con un valore molto alto
- ci si troverà ad utilizzare inutilmente un maggiore quantitativo di
- memoria.}
-
-Inoltre con \func{select} lo stesso \itindex{file~descriptor~set} \textit{file
- descriptor set} è usato sia in ingresso che in uscita, e questo significa
-che tutte le volte che si vuole ripetere l'operazione occorre reinizializzarlo
-da capo. Questa operazione, che può essere molto onerosa se i file descriptor
-da tenere sotto osservazione sono molti, non è invece necessaria con
-\func{poll}.
-
-Abbiamo visto in sez.~\ref{sec:file_select} come lo standard POSIX preveda una
-variante di \func{select} che consente di gestire correttamente la ricezione
-dei segnali nell'attesa su un file descriptor. Con l'introduzione di una
-implementazione reale di \func{pselect} nel kernel 2.6.16, è stata aggiunta
-anche una analoga funzione che svolga lo stesso ruolo per \func{poll}.
-
-In questo caso si tratta di una estensione che è specifica di Linux e non è
-prevista da nessuno standard; essa può essere utilizzata esclusivamente se si
-definisce la macro \macro{\_GNU\_SOURCE} ed ovviamente non deve essere usata
-se si ha a cuore la portabilità. La funzione è \funcd{ppoll}, ed il suo
-prototipo è:
-\begin{prototype}{sys/poll.h}
- {int ppoll(struct pollfd *fds, nfds\_t nfds, const struct timespec *timeout,
- const sigset\_t *sigmask)}
-
- La funzione attende un cambiamento di stato su un insieme di file
- descriptor.
-
- \bodydesc{La funzione restituisce il numero di file descriptor con attività
- in caso di successo, o 0 se c'è stato un timeout e -1 in caso di errore,
- ed in quest'ultimo caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EBADF}] si è specificato un file descriptor sbagliato in uno
- degli insiemi.
- \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale.
- \item[\errcode{EINVAL}] il valore di \param{nfds} eccede il limite
- \macro{RLIMIT\_NOFILE}.
- \end{errlist}
- ed inoltre \errval{EFAULT} e \errval{ENOMEM}.}
-\end{prototype}
-
-La funzione ha lo stesso comportamento di \func{poll}, solo che si può
-specificare, con l'argomento \param{sigmask}, il puntatore ad una maschera di
-segnali; questa sarà la maschera utilizzata per tutto il tempo che la funzione
-resterà in attesa, all'uscita viene ripristinata la maschera originale. L'uso
-di questa funzione è cioè equivalente, come illustrato nella pagina di
-manuale, all'esecuzione atomica del seguente codice:
-\includecodesnip{listati/ppoll_means.c}
-
-Eccetto per \param{timeout}, che come per \func{pselect} deve essere un
-puntatore ad una struttura \struct{timespec}, gli altri argomenti comuni con
-\func{poll} hanno lo stesso significato, e la funzione restituisce gli stessi
-risultati illustrati in precedenza. Come nel caso di \func{pselect} la system
-call che implementa \func{ppoll} restituisce, se la funzione viene interrotta
-da un segnale, il tempo mancante in \param{timeout}, e come per \func{pselect}
-la funzione di libreria fornita dalle \acr{glibc} maschera questo
-comportamento non modificando mai il valore di \param{timeout}.\footnote{anche
- se in questo caso non esiste nessuno standard che richiede questo
- comportamento.}
-
-
-\subsection{L'interfaccia di \textit{epoll}}
-\label{sec:file_epoll}
-
-\itindbeg{epoll}
-
-Nonostante \func{poll} presenti alcuni vantaggi rispetto a \func{select},
-anche questa funzione non è molto efficiente quando deve essere utilizzata con
-un gran numero di file descriptor,\footnote{in casi del genere \func{select}
- viene scartata a priori, perché può avvenire che il numero di file
- descriptor ecceda le dimensioni massime di un \itindex{file~descriptor~set}
- \textit{file descriptor set}.} in particolare nel caso in cui solo pochi di
-questi diventano attivi. Il problema in questo caso è che il tempo impiegato
-da \func{poll} a trasferire i dati da e verso il kernel è proporzionale al
-numero di file descriptor osservati, non a quelli che presentano attività.
-
-Quando ci sono decine di migliaia di file descriptor osservati e migliaia di
-eventi al secondo,\footnote{il caso classico è quello di un server web di un
- sito con molti accessi.} l'uso di \func{poll} comporta la necessità di
-trasferire avanti ed indietro da user space a kernel space la lunga lista
-delle strutture \struct{pollfd} migliaia di volte al secondo. A questo poi si
-aggiunge il fatto che la maggior parte del tempo di esecuzione sarà impegnato
-ad eseguire una scansione su tutti i file descriptor tenuti sotto controllo
-per determinare quali di essi (in genere una piccola percentuale) sono
-diventati attivi. In una situazione come questa l'uso delle funzioni classiche
-dell'interfaccia dell'\textit{I/O multiplexing} viene a costituire un collo di
-bottiglia che degrada irrimediabilmente le prestazioni.
-
-Per risolvere questo tipo di situazioni sono state ideate delle interfacce
-specialistiche\footnote{come \texttt{/dev/poll} in Solaris, o \texttt{kqueue}
- in BSD.} il cui scopo fondamentale è quello di restituire solamente le
-informazioni relative ai file descriptor osservati che presentano una
-attività, evitando così le problematiche appena illustrate. In genere queste
-prevedono che si registrino una sola volta i file descriptor da tenere sotto
-osservazione, e forniscono un meccanismo che notifica quali di questi
-presentano attività.
-
-Le modalità con cui avviene la notifica sono due, la prima è quella classica
-(quella usata da \func{poll} e \func{select}) che viene chiamata \textit{level
- triggered}.\footnote{la nomenclatura è stata introdotta da Jonathan Lemon in
- un articolo su \texttt{kqueue} al BSDCON 2000, e deriva da quella usata
- nell'elettronica digitale.} In questa modalità vengono notificati i file
-descriptor che sono \textsl{pronti} per l'operazione richiesta, e questo
-avviene indipendentemente dalle operazioni che possono essere state fatte su
-di essi a partire dalla precedente notifica. Per chiarire meglio il concetto
-ricorriamo ad un esempio: se su un file descriptor sono diventati disponibili
-in lettura 2000 byte ma dopo la notifica ne sono letti solo 1000 (ed è quindi
-possibile eseguire una ulteriore lettura dei restanti 1000), in modalità
-\textit{level triggered} questo sarà nuovamente notificato come
-\textsl{pronto}.
-
-La seconda modalità, è detta \textit{edge triggered}, e prevede che invece
-vengano notificati solo i file descriptor che hanno subito una transizione da
-\textsl{non pronti} a \textsl{pronti}. Questo significa che in modalità
-\textit{edge triggered} nel caso del precedente esempio il file descriptor
-diventato pronto da cui si sono letti solo 1000 byte non verrà nuovamente
-notificato come pronto, nonostante siano ancora disponibili in lettura 1000
-byte. Solo una volta che si saranno esauriti tutti i byte disponibili, e che
-il file descriptor sia tornato non essere pronto, si potrà ricevere una
-ulteriore notifica qualora ritornasse pronto.
-
-Nel caso di Linux al momento la sola interfaccia che fornisce questo tipo di
-servizio è \textit{epoll},\footnote{l'interfaccia è stata creata da Davide
- Libenzi, ed è stata introdotta per la prima volta nel kernel 2.5.44, ma la
- sua forma definitiva è stata raggiunta nel kernel 2.5.66.} anche se sono in
-discussione altre interfacce con le quali si potranno effettuare lo stesso
-tipo di operazioni;\footnote{al momento della stesura di queste note (Giugno
- 2007) un'altra interfaccia proposta è quella di \textit{kevent}, che
- fornisce un sistema di notifica di eventi generico in grado di fornire le
- stesse funzionalità di \textit{epoll}, esiste però una forte discussione
- intorno a tutto ciò e niente di definito.} \textit{epoll} è in grado di
-operare sia in modalità \textit{level triggered} che \textit{edge triggered}.
-
-La prima versione \textit{epoll} prevedeva l'apertura di uno speciale file di
-dispositivo, \texttt{/dev/epoll}, per ottenere un file descriptor da
-utilizzare con le funzioni dell'interfaccia,\footnote{il backporting
- dell'interfaccia per il kernel 2.4, non ufficiale, utilizza sempre questo
- file.} ma poi si è passati all'uso una apposita \textit{system call}. Il
-primo passo per usare l'interfaccia di \textit{epoll} è pertanto quello di
-chiamare la funzione \funcd{epoll\_create}, il cui prototipo è:
-\begin{prototype}{sys/epoll.h}
- {int epoll\_create(int size)}
-
- Apre un file descriptor per \textit{epoll}.
-
- \bodydesc{La funzione restituisce un file descriptor in caso di successo, o
- $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EINVAL}] si è specificato un valore di \param{size} non
- positivo.
- \item[\errcode{ENFILE}] si è raggiunto il massimo di file descriptor aperti
- nel sistema.
- \item[\errcode{ENOMEM}] non c'è sufficiente memoria nel kernel per creare
- l'istanza.
- \end{errlist}
-}
-\end{prototype}
-
-La funzione restituisce un file descriptor speciale,\footnote{esso non è
- associato a nessun file su disco, inoltre a differenza dei normali file
- descriptor non può essere inviato ad un altro processo attraverso un socket
- locale (vedi sez.~\ref{sec:sock_fd_passing}).} detto anche \textit{epoll
- descriptor}, che viene associato alla infrastruttura utilizzata dal kernel
-per gestire la notifica degli eventi; l'argomento \param{size} serve a dare
-l'indicazione del numero di file descriptor che si vorranno tenere sotto
-controllo, ma costituisce solo un suggerimento per semplificare l'allocazione
-di risorse sufficienti, non un valore massimo.
-
-Una volta ottenuto un file descriptor per \textit{epoll} il passo successivo è
-indicare quali file descriptor mettere sotto osservazione e quali operazioni
-controllare, per questo si deve usare la seconda funzione dell'interfaccia,
-\funcd{epoll\_ctl}, il cui prototipo è:
-\begin{prototype}{sys/epoll.h}
- {int epoll\_ctl(int epfd, int op, int fd, struct epoll\_event *event)}
-
- Esegue le operazioni di controllo di \textit{epoll}.
-
- \bodydesc{La funzione restituisce $0$ in caso di successo o $-1$ in caso di
- errore, nel qual caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EBADF}] il file descriptor \param{epfd} o \param{fd} non sono
- validi.
- \item[\errcode{EEXIST}] l'operazione richiesta è \const{EPOLL\_CTL\_ADD} ma
- \param{fd} è già stato inserito in \param{epfd}.
- \item[\errcode{EINVAL}] il file descriptor \param{epfd} non è stato ottenuto
- con \func{epoll\_create}, o \param{fd} è lo stesso \param{epfd} o
- l'operazione richiesta con \param{op} non è supportata.
- \item[\errcode{ENOENT}] l'operazione richiesta è \const{EPOLL\_CTL\_MOD} o
- \const{EPOLL\_CTL\_DEL} ma \param{fd} non è inserito in \param{epfd}.
- \item[\errcode{ENOMEM}] non c'è sufficiente memoria nel kernel gestire
- l'operazione richiesta.
- \item[\errcode{EPERM}] il file \param{fd} non supporta \textit{epoll}.
- \end{errlist}
-}
-\end{prototype}
-
-Il comportamento della funzione viene controllato dal valore dall'argomento
-\param{op} che consente di specificare quale operazione deve essere eseguita.
-Le costanti che definiscono i valori utilizzabili per \param{op}
-sono riportate in tab.~\ref{tab:epoll_ctl_operation}, assieme al significato
-delle operazioni cui fanno riferimento.
-
-\begin{table}[htb]
- \centering
- \footnotesize
- \begin{tabular}[c]{|l|p{8cm}|}
- \hline
- \textbf{Valore} & \textbf{Significato} \\
- \hline
- \hline
- \const{EPOLL\_CTL\_ADD}& Aggiunge un nuovo file descriptor da osservare
- \param{fd} alla lista dei file descriptor
- controllati tramite \param{epfd}, in
- \param{event} devono essere specificate le
- modalità di osservazione.\\
- \const{EPOLL\_CTL\_MOD}& Modifica le modalità di osservazione del file
- descriptor \param{fd} secondo il contenuto di
- \param{event}.\\
- \const{EPOLL\_CTL\_DEL}& Rimuove il file descriptor \param{fd} dalla lista
- dei file controllati tramite \param{epfd}.\\
- \hline
- \end{tabular}
- \caption{Valori dell'argomento \param{op} che consentono di scegliere quale
- operazione di controllo effettuare con la funzione \func{epoll\_ctl}.}
- \label{tab:epoll_ctl_operation}
-\end{table}
-
-La funzione prende sempre come primo argomento un file descriptor di
-\textit{epoll}, \param{epfd}, che deve essere stato ottenuto in precedenza con
-una chiamata a \func{epoll\_create}. L'argomento \param{fd} indica invece il
-file descriptor che si vuole tenere sotto controllo, quest'ultimo può essere
-un qualunque file descriptor utilizzabile con \func{poll}, ed anche un altro
-file descriptor di \textit{epoll}, ma non lo stesso \param{epfd}.
-
-L'ultimo argomento, \param{event}, deve essere un puntatore ad una struttura
-di tipo \struct{epoll\_event}, ed ha significato solo con le operazioni
-\const{EPOLL\_CTL\_MOD} e \const{EPOLL\_CTL\_ADD}, per le quali serve ad
-indicare quale tipo di evento relativo ad \param{fd} si vuole che sia tenuto
-sotto controllo. L'argomento viene ignorato con l'operazione
-\const{EPOLL\_CTL\_DEL}.\footnote{fino al kernel 2.6.9 era comunque richiesto
- che questo fosse un puntatore valido, anche se poi veniva ignorato, a
- partire dal 2.6.9 si può specificare anche un valore \texttt{NULL}.}
-
-\begin{figure}[!htb]
- \footnotesize \centering
- \begin{minipage}[c]{15cm}
- \includestruct{listati/epoll_event.h}
- \end{minipage}
- \normalsize
- \caption{La struttura \structd{epoll\_event}, che consente di specificare
- gli eventi associati ad un file descriptor controllato con
- \textit{epoll}.}
- \label{fig:epoll_event}
-\end{figure}
-
-La struttura \struct{epoll\_event} è l'analoga di \struct{pollfd} e come
-quest'ultima serve sia in ingresso (quando usata con \func{epoll\_ctl}) ad
-impostare quali eventi osservare, che in uscita (nei risultati ottenuti con
-\func{epoll\_wait}) per ricevere le notifiche degli eventi avvenuti. La sua
-definizione è riportata in fig.~\ref{fig:epoll_event}.
-
-Il primo campo, \var{events}, è una maschera binaria in cui ciascun bit
-corrisponde o ad un tipo di evento, o una modalità di notifica; detto campo
-deve essere specificato come OR aritmetico delle costanti riportate in
-tab.~\ref{tab:epoll_events}. Il secondo campo, \var{data}, serve ad indicare a
-quale file descriptor si intende fare riferimento, ed in astratto può
-contenere un valore qualsiasi che permetta di identificarlo, di norma comunque
-si usa come valore lo stesso \param{fd}.
-
-\begin{table}[htb]
- \centering
- \footnotesize
- \begin{tabular}[c]{|l|p{8cm}|}
- \hline
- \textbf{Valore} & \textbf{Significato} \\
- \hline
- \hline
- \const{EPOLLIN} & Il file è pronto per le operazioni di lettura
- (analogo di \const{POLLIN}).\\
- \const{EPOLLOUT} & Il file è pronto per le operazioni di scrittura
- (analogo di \const{POLLOUT}).\\
- \const{EPOLLRDHUP} & L'altro capo di un socket di tipo
- \const{SOCK\_STREAM} (vedi sez.~\ref{sec:sock_type})
- ha chiuso la connessione o il capo in scrittura
- della stessa (vedi sez.~\ref{sec:TCP_shutdown}).\\
- \const{EPOLLPRI} & Ci sono \itindex{out-of-band} dati urgenti
- disponibili in lettura (analogo di
- \const{POLLPRI}); questa condizione viene comunque
- riportata in uscita, e non è necessaria impostarla
- in ingresso.\\
- \const{EPOLLERR} & Si è verificata una condizione di errore
- (analogo di \const{POLLERR}); questa condizione
- viene comunque riportata in uscita, e non è
- necessaria impostarla in ingresso.\\
- \const{EPOLLHUP} & Si è verificata una condizione di hung-up.\\
- \const{EPOLLET} & Imposta la notifica in modalità \textit{edge
- triggered} per il file descriptor associato.\\
- \const{EPOLLONESHOT}& Imposta la modalità \textit{one-shot} per il file
- descriptor associato.\footnotemark\\
- \hline
- \end{tabular}
- \caption{Costanti che identificano i bit del campo \param{events} di
- \struct{epoll\_event}.}
- \label{tab:epoll_events}
-\end{table}
-
-\footnotetext{questa modalità è disponibile solo a partire dal kernel 2.6.2.}
-
-Le modalità di utilizzo di \textit{epoll} prevedono che si definisca qual'è
-l'insieme dei file descriptor da tenere sotto controllo tramite un certo
-\textit{epoll descriptor} \param{epfd} attraverso una serie di chiamate a
-\const{EPOLL\_CTL\_ADD}.\footnote{un difetto dell'interfaccia è che queste
- chiamate devono essere ripetute per ciascun file descriptor, incorrendo in
- una perdita di prestazioni qualora il numero di file descriptor sia molto
- grande; per questo è stato proposto di introdurre come estensione una
- funzione \func{epoll\_ctlv} che consenta di effettuare con una sola chiamata
- le impostazioni per un blocco di file descriptor.} L'uso di
-\const{EPOLL\_CTL\_MOD} consente in seguito di modificare le modalità di
-osservazione di un file descriptor che sia già stato aggiunto alla lista di
-osservazione.
-
-Le impostazioni di default prevedono che la notifica degli eventi richiesti
-sia effettuata in modalità \textit{level triggered}, a meno che sul file
-descriptor non si sia impostata la modalità \textit{edge triggered},
-registrandolo con \const{EPOLLET} attivo nel campo \var{events}. Si tenga
-presente che è possibile tenere sotto osservazione uno stesso file descriptor
-su due \textit{epoll descriptor} diversi, ed entrambi riceveranno le
-notifiche, anche se questa pratica è sconsigliata.
-
-Qualora non si abbia più interesse nell'osservazione di un file descriptor lo
-si può rimuovere dalla lista associata a \param{epfd} con
-\const{EPOLL\_CTL\_DEL}; si tenga conto inoltre che i file descriptor sotto
-osservazione che vengono chiusi sono eliminati dalla lista automaticamente e
-non è necessario usare \const{EPOLL\_CTL\_DEL}.
-
-Infine una particolare modalità di notifica è quella impostata con
-\const{EPOLLONESHOT}: a causa dell'implementazione di \textit{epoll} infatti
-quando si è in modalità \textit{edge triggered} l'arrivo in rapida successione
-di dati in blocchi separati\footnote{questo è tipico con i socket di rete, in
- quanto i dati arrivano a pacchetti.} può causare una generazione di eventi
-(ad esempio segnalazioni di dati in lettura disponibili) anche se la
-condizione è già stata rilevata.\footnote{si avrebbe cioè una rottura della
- logica \textit{edge triggered}.}
-
-Anche se la situazione è facile da gestire, la si può evitare utilizzando
-\const{EPOLLONESHOT} per impostare la modalità \textit{one-shot}, in cui la
-notifica di un evento viene effettuata una sola volta, dopo di che il file
-descriptor osservato, pur restando nella lista di osservazione, viene
-automaticamente disattivato,\footnote{la cosa avviene contestualmente al
- ritorno di \func{epoll\_wait} a causa dell'evento in questione.} e per
-essere riutilizzato dovrà essere riabilitato esplicitamente con una successiva
-chiamata con \const{EPOLL\_CTL\_MOD}.
-
-Una volta impostato l'insieme di file descriptor che si vogliono osservare con
-i relativi eventi, la funzione che consente di attendere l'occorrenza di uno
-di tali eventi è \funcd{epoll\_wait}, il cui prototipo è:
-\begin{prototype}{sys/epoll.h}
- {int epoll\_wait(int epfd, struct epoll\_event * events, int maxevents, int
- timeout)}
-
- Attende che uno dei file descriptor osservati sia pronto.
-
- \bodydesc{La funzione restituisce il numero di file descriptor pronti in
- caso di successo o $-1$ in caso di errore, nel qual caso \var{errno}
- assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EBADF}] il file descriptor \param{epfd} non è valido.
- \item[\errcode{EFAULT}] il puntatore \param{events} non è valido.
- \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale prima
- della scadenza di \param{timeout}.
- \item[\errcode{EINVAL}] il file descriptor \param{epfd} non è stato ottenuto
- con \func{epoll\_create}, o \param{maxevents} non è maggiore di zero.
- \end{errlist}
-}
-\end{prototype}
-
-La funzione si blocca in attesa di un evento per i file descriptor registrati
-nella lista di osservazione di \param{epfd} fino ad un tempo massimo
-specificato in millisecondi tramite l'argomento \param{timeout}. Gli eventi
-registrati vengono riportati in un vettore di strutture \struct{epoll\_event}
-(che deve essere stato allocato in precedenza) all'indirizzo indicato
-dall'argomento \param{events}, fino ad un numero massimo di eventi impostato
-con l'argomento \param{maxevents}.
-
-La funzione ritorna il numero di eventi rilevati, o un valore nullo qualora
-sia scaduto il tempo massimo impostato con \param{timeout}. Per quest'ultimo,
-oltre ad un numero di millisecondi, si può utilizzare il valore nullo, che
-indica di non attendere e ritornare immediatamente,\footnote{anche in questo
- caso il valore di ritorno sarà nullo.} o il valore $-1$, che indica
-un'attesa indefinita. L'argomento \param{maxevents} dovrà invece essere sempre
-un intero positivo.
-
-Come accennato la funzione restituisce i suoi risultati nel vettore di
-strutture \struct{epoll\_event} puntato da \param{events}; in tal caso nel
-campo \param{events} di ciascuna di esse saranno attivi i flag relativi agli
-eventi accaduti, mentre nel campo \var{data} sarà restituito il valore che era
-stato impostato per il file descriptor per cui si è verificato l'evento quando
-questo era stato registrato con le operazioni \const{EPOLL\_CTL\_MOD} o
-\const{EPOLL\_CTL\_ADD}, in questo modo il campo \var{data} consente di
-identificare il file descriptor.\footnote{ed è per questo che, come accennato,
- è consuetudine usare per \var{data} il valore del file descriptor stesso.}
-
-Si ricordi che le occasioni per cui \func{epoll\_wait} ritorna dipendono da
-come si è impostata la modalità di osservazione (se \textit{level triggered} o
-\textit{edge triggered}) del singolo file descriptor. L'interfaccia assicura
-che se arrivano più eventi fra due chiamate successive ad \func{epoll\_wait}
-questi vengano combinati. Inoltre qualora su un file descriptor fossero
-presenti eventi non ancora notificati, e si effettuasse una modifica
-dell'osservazione con \const{EPOLL\_CTL\_MOD}, questi verrebbero riletti alla
-luce delle modifiche.
-
-Si tenga presente infine che con l'uso della modalità \textit{edge triggered}
-il ritorno di \func{epoll\_wait} indica che un file descriptor è pronto e
-resterà tale fintanto che non si sono completamente esaurite le operazioni su
-di esso. Questa condizione viene generalmente rilevata dall'occorrere di un
-errore di \errcode{EAGAIN} al ritorno di una \func{read} o una
-\func{write},\footnote{è opportuno ricordare ancora una volta che l'uso
- dell'\textit{I/O multiplexing} richiede di operare sui file in modalità non
- bloccante.} ma questa non è la sola modalità possibile, ad esempio la
-condizione può essere riconosciuta anche per il fatto che sono stati
-restituiti meno dati di quelli richiesti.
-
-Come già per \func{select} e \func{poll} anche per l'interfaccia di
-\textit{epoll} si pone il problema di gestire l'attesa di segnali e di dati
-contemporaneamente, per far questo di nuovo è necessaria una variante della
-funzione di attesa che consenta di reimpostare all'uscita una maschera di
-segnali, analoga alle precedenti estensioni \func{pselect} e \func{ppoll} di
-\func{select} e \func{poll}; in questo caso la funzione si chiama
-\funcd{epoll\_pwait}\footnote{la funziona è stata introdotta a partire dal
- kernel 2.6.19, ed è come tutta l'interfaccia di \textit{epoll}, specifica di
- Linux.} ed il suo prototipo è:
-\begin{prototype}{sys/epoll.h}
- {int epoll\_pwait(int epfd, struct epoll\_event * events, int maxevents,
- int timeout, const sigset\_t *sigmask)}
-
- Attende che uno dei file descriptor osservati sia pronto, mascherando i
- segnali.
-
- \bodydesc{La funzione restituisce il numero di file descriptor pronti in
- caso di successo o $-1$ in caso di errore, nel qual caso \var{errno}
- assumerà uno dei valori già visti con \funcd{epoll\_wait}.
-}
-\end{prototype}
-
-La funzione è del tutto analoga \funcd{epoll\_wait}, soltanto che alla sua
-uscita viene ripristinata la maschera di segnali originale, sostituita durante
-l'esecuzione da quella impostata con l'argomento \param{sigmask}; in sostanza
-la chiamata a questa funzione è equivalente al seguente codice, eseguito però
-in maniera atomica:
-\includecodesnip{listati/epoll_pwait_means.c}
-
-Si tenga presente che come le precedenti funzioni di \textit{I/O multiplexing}
-anche le funzioni dell'interfaccia di \textit{epoll} vengono utilizzate
-prevalentemente con i server di rete, quando si devono tenere sotto
-osservazione un gran numero di socket; per questo motivo rimandiamo anche in
-questo caso la trattazione di un esempio concreto a quando avremo esaminato in
-dettaglio le caratteristiche dei socket; in particolare si potrà trovare un
-programma che utilizza questa interfaccia in sez.~\ref{sec:TCP_serv_epoll}.
-
-\itindend{epoll}
-
-
-\subsection{La notifica di eventi tramite file descriptor}
-\label{sec:sig_signalfd_eventfd}
-
-Abbiamo visto in sez.~\ref{sec:file_select} come il meccanismo classico delle
-notifiche di eventi tramite i segnali, presente da sempre nei sistemi
-unix-like, porti a notevoli problemi nell'interazione con le funzioni per
-l'I/O multiplexing, tanto che per evitare possibili \itindex{race~condition}
-\textit{race condition} sono state introdotte estensioni dello standard POSIX e
-funzioni apposite come \func{pselect}, \func{ppoll} e \funcd{epoll\_pwait}.
-
-Benché i segnali siano il meccanismo più usato per effettuare notifiche ai
-processi, la loro interfaccia di programmazione, che comporta l'esecuzione di
-una funzione di gestione in maniera asincrona e totalmente scorrelata
-dall'ordinario flusso di esecuzione del processo, si è però dimostrata quasi
-subito assai problematica. Oltre ai limiti relativi ai limiti al cosa si può
-fare all'interno della funzione del gestore di segnali (quelli illustrati in
-sez.~\ref{sec:sig_signal_handler}), c'è il problema più generale consistente
-nel fatto che questa modalità di funzionamento cozza con altre interfacce di
-programmazione previste dal sistema in cui si opera in maniera
-\textsl{sincrona}, come quelle dell'I/O multiplexing appena illustrate.
-
-In questo tipo di interfacce infatti ci si aspetta che il processo gestisca
-gli eventi a cui vuole rispondere in maniera sincrona generando le opportune
-risposte, mentre con l'arrivo di un segnale si possono avere interruzioni
-asincrone in qualunque momento. Questo comporta la necessità di dover
-gestire, quando si deve tener conto di entrambi i tipi di eventi, le
-interruzioni delle funzioni di attesa sincrone, ed evitare possibili
-\itindex{race~condition} \textit{race conditions}.\footnote{in sostanza se non
- fossero per i segnali non ci sarebbe da doversi preoccupare, fintanto che si
- effettuano operazioni all'interno di un processo, della non atomicità delle
- \index{system~call~lente} system call lente che vengono interrotte e devono
- essere riavviate.}
-
-Abbiamo visto però in sez.~\ref{sec:sig_real_time} che insieme ai segnali
-\textit{real-time} sono state introdotte anche delle interfacce di gestione
-sincrona dei segnali con la funzione \func{sigwait} e le sue affini. Queste
-funzioni consentono di gestire i segnali bloccando un processo fino alla
-avvenuta ricezione e disabilitando l'esecuzione asincrona rispetto al resto
-del programma del gestore del segnale. Questo consente di risolvere i
-problemi di atomicità nella gestione degli eventi associati ai segnali, avendo
-tutto il controllo nel flusso principale del programma, ottenendo così una
-gestione simile a quella dell'I/O multiplexing, ma non risolve i problemi
-delle interazioni con quest'ultimo, perché o si aspetta la ricezione di un
-segnale o si aspetta che un file descriptor sia accessibile e nessuna delle
-rispettive funzioni consente di fare contemporaneamente entrambe le cose.
-
-Per risolvere questo problema nello sviluppo del kernel si è pensato di
-introdurre un meccanismo alternativo alla notifica dei segnali (esteso anche
-ad altri eventi generici) che, ispirandosi di nuovo alla filosofia di Unix per
-cui tutto è un file, consentisse di eseguire la notifica con l'uso di
-opportuni file descriptor.\footnote{ovviamente si tratta di una funzionalità
- specifica di Linux, non presente in altri sistemi unix-like, e non prevista
- da nessuno standard.}
-
-In sostanza, come per \func{sigwait}, si può disabilitare l'esecuzione di un
-gestore in occasione dell'arrivo di un segnale, e rilevarne l'avvenuta
-ricezione leggendone la notifica tramite l'uso di uno speciale file
-descriptor. Trattandosi di un file descriptor questo potrà essere tenuto sotto
-osservazione con le ordinarie funzioni dell'I/O multiplexing (vale a dire con
-le solite \func{select}, \func{poll} e \funcd{epoll\_wait}) allo stesso modo
-di quelli associati a file o socket, per cui alla fine si potrà attendere in
-contemporanea sia l'arrivo del segnale che la disponibilità di accesso ai dati
-relativi a questi ultimi.
-
-La funzione che permette di abilitare la ricezione dei segnali tramite file
-descriptor è \funcd{signalfd},\footnote{in realtà questa è il nome della
- funzione fornita dalle \acr{glibc}, esistono in realtà due
- versioni, anche se } il cui prototipo è:
-\begin{prototype}{sys/signalfd.h}
- {int signalfd(int fd, const sigset\_t *mask, int flags)}
-
- Crea o modifica un file descriptor pet la ricezione dei segnali.
-
- \bodydesc{La funzione restituisce un numero di file descriptor in caso di
- successo o $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno
- dei valori:
- \begin{errlist}
- \item[\errcode{EBADF}] il valore \param{fd} non indica un file descriptor.
- \item[\errcode{EINVAL}] il file descriptor \param{fd} non è stato ottenuto
- con \func{signalfd} o il valore di \param{flags} non è valido.
- \item[\errcode{ENOMEN}] 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.
- \end{errlist}
- ed inoltre \errval{EMFILE} e \errval{ENFILE}.
-}
-\end{prototype}
-
-La funzione consente di creare o modificare le caratteristiche di un file
-descriptor speciale su cui ricevere le notifiche della ricezione di
-segnali. Per creare un nuovo file descriptor è necessario passare $-1$ come
-valore per l'argomento \param{fd}, ogni altro valore positivo verrà invece
-interpretato come il numero del file descriptor (che deve esser stato
-precedentemente creato sempre con \func{signalfd}) di cui si vogliono
-modificare le caratteristiche. Nel primo caso la funzione ritornerà il nuovo
-file descriptor e nel secondo caso \param{fd}, in caso di errore verrà invece
-restituito $-1$.
-
-L'elenco dei segnali che si vogliono gestire con \func{signalfd} deve essere
-specificato tramite l'argomento \param{mask}. Questo deve essere passato come
-puntatore ad una maschera di segnali creata con l'uso delle apposite macro
-illustrate in sez.~\ref{sec:sig_sigset} che indichi su quali segnali si
-intende operare con \func{signalfd}, l'elenco può essere modificato da una
-chiamata successiva. Dato che \const{SIGKILL} e \const{SIGSTOP} non possono
-essere intercettati (e non prevedono neanche la possibilità di un gestore) un
-loro inserimento nella maschera verrà semplicemente ignorato, senza generare
-errori.
-
-Infine l'argomento \param{flags} consente di impostare direttamente in fase di
-creazione due flag per il file descriptor analoghe a quelle che si possono
-impostare con \func{open}, evitando una impostazione successiva con
-\func{fcntl}.\footnote{questo è un argomento aggiuntivo introdotto con il
- kernel 2.6.27, in precedenza il valore era nullo }
-
-\begin{table}[htb]
- \centering
- \footnotesize
- \begin{tabular}[c]{|l|p{8cm}|}
- \hline
- \textbf{Valore} & \textbf{Significato} \\
- \hline
- \hline
- \const{SFD\_NONBLOCK}& imposta sul file descriptor il flag di
- \const{O\_NONBLOCK} per renderlo non bloccante.\\
- \const{SFD\_CLOEXEC}& imposta il flag di \const{O\_CLOEXEC} per la
- chiusura automatica del file descriptor nella
- esecuzione di \func{exec}.\\
- \hline
- \end{tabular}
- \caption{Valori dell'argomento \param{flags} per la funzione \func{signalfd}
- che consentono di impostare fl.}
- \label{tab:signalfd_flags}
-\end{table}
-
-% TODO trattare qui eventfd signalfd e timerfd introdotte con il 2.6.22
-% timerfd è stata tolta nel 2.6.23 e rifatta per bene nel 2.6.25
-% vedi: http://lwn.net/Articles/233462/
-% http://lwn.net/Articles/245533/
-% http://lwn.net/Articles/267331/
-
-
-
-\section{L'accesso \textsl{asincrono} ai file}
-\label{sec:file_asyncronous_access}
-
-Benché l'\textit{I/O multiplexing} sia stata la prima, e sia tutt'ora una fra
-le più diffuse modalità di gestire l'I/O in situazioni complesse in cui si
-debba operare su più file contemporaneamente, esistono altre modalità di
-gestione delle stesse problematiche. In particolare sono importanti in questo
-contesto le modalità di accesso ai file eseguibili in maniera
-\textsl{asincrona}, quelle cioè in cui un processo non deve bloccarsi in
-attesa della disponibilità dell'accesso al file, ma può proseguire
-nell'esecuzione utilizzando invece un meccanismo di notifica asincrono (di
-norma un segnale, ma esistono anche altre interfacce, come \itindex{inotify}
-\textit{inotify}), per essere avvisato della possibilità di eseguire le
-operazioni di I/O volute.
-
-
-\subsection{Il \textit{Signal driven I/O}}
-\label{sec:file_asyncronous_operation}
-
-\itindbeg{signal~driven~I/O}
-
-Abbiamo accennato in sez.~\ref{sec:file_open} che è possibile, attraverso
-l'uso del flag \const{O\_ASYNC},\footnote{l'uso del flag di \const{O\_ASYNC} e
- dei comandi \const{F\_SETOWN} e \const{F\_GETOWN} per \func{fcntl} è
- specifico di Linux e BSD.} aprire un file in modalità asincrona, così come è
-possibile attivare in un secondo tempo questa modalità impostando questo flag
-attraverso l'uso di \func{fcntl} con il comando \const{F\_SETFL} (vedi
-sez.~\ref{sec:file_fcntl}). In realtà parlare di apertura in modalità
-asincrona non significa che le operazioni di lettura o scrittura del file
-vengono eseguite in modo asincrono (tratteremo questo, che è ciò che più
-propriamente viene chiamato \textsl{I/O asincrono}, in
-sez.~\ref{sec:file_asyncronous_io}), quanto dell'attivazione un meccanismo di
-notifica asincrona delle variazione dello stato del file descriptor aperto in
-questo modo.
-
-Quello che succede è che per tutti i file posti in questa modalità\footnote{si
- tenga presente però che essa non è utilizzabile con i file ordinari ma solo
- con socket, file di terminale o pseudo terminale, ed anche, a partire dal
- kernel 2.6, anche per fifo e pipe.} il sistema genera un apposito segnale,
-\const{SIGIO}, tutte le volte che diventa possibile leggere o scrivere dal
-file descriptor che si è posto in questa modalità. Inoltre è possibile, come
-illustrato in sez.~\ref{sec:file_fcntl}, selezionare con il comando
-\const{F\_SETOWN} di \func{fcntl} quale processo o quale gruppo di processi
-dovrà ricevere il segnale. In questo modo diventa possibile effettuare le
-operazioni di I/O in risposta alla ricezione del segnale, e non ci sarà più la
-necessità di restare bloccati in attesa della disponibilità di accesso ai
-file.
-
-% TODO: per i thread l'uso di F_SETOWN ha un significato diverso
-
-Per questo motivo Stevens, ed anche le pagine di manuale di Linux, chiamano
-questa modalità ``\textit{Signal driven I/O}''. Si tratta di un'altra
-modalità di gestione dell'I/O, alternativa all'uso di \itindex{epoll}
-\textit{epoll},\footnote{anche se le prestazioni ottenute con questa tecnica
- sono inferiori, il vantaggio è che questa modalità è utilizzabile anche con
- kernel che non supportano \textit{epoll}, come quelli della serie 2.4,
- ottenendo comunque prestazioni superiori a quelle che si hanno con
- \func{poll} e \func{select}.} che consente di evitare l'uso delle funzioni
-\func{poll} o \func{select} che, come illustrato in sez.~\ref{sec:file_epoll},
-quando vengono usate con un numero molto grande di file descriptor, non hanno
-buone prestazioni.
-
-Tuttavia con l'implementazione classica dei segnali questa modalità di I/O
-presenta notevoli problemi, dato che non è possibile determinare, quando i
-file descriptor sono più di uno, qual è quello responsabile dell'emissione del
-segnale. Inoltre dato che i segnali normali non si accodano (si ricordi quanto
-illustrato in sez.~\ref{sec:sig_notification}), in presenza di più file
-descriptor attivi contemporaneamente, più segnali emessi nello stesso momento
-verrebbero notificati una volta sola.
-
-Linux però supporta le estensioni POSIX.1b dei segnali real-time, che vengono
-accodati e che permettono di riconoscere il file descriptor che li ha emessi.
-In questo caso infatti si può fare ricorso alle informazioni aggiuntive
-restituite attraverso la struttura \struct{siginfo\_t}, utilizzando la forma
-estesa \var{sa\_sigaction} del gestore installata con il flag
-\const{SA\_SIGINFO} (si riveda quanto illustrato in
-sez.~\ref{sec:sig_sigaction}).
-
-Per far questo però occorre utilizzare le funzionalità dei segnali real-time
-(vedi sez.~\ref{sec:sig_real_time}) impostando esplicitamente con il comando
-\const{F\_SETSIG} di \func{fcntl} un segnale real-time da inviare in caso di
-I/O asincrono (il segnale predefinito è \const{SIGIO}). In questo caso il
-gestore, tutte le volte che riceverà \const{SI\_SIGIO} come valore del campo
-\var{si\_code}\footnote{il valore resta \const{SI\_SIGIO} qualunque sia il
- segnale che si è associato all'I/O, ed indica appunto che il segnale è stato
- generato a causa di attività di I/O.} di \struct{siginfo\_t}, troverà nel
-campo \var{si\_fd} il valore del file descriptor che ha generato il segnale.
-
-Un secondo vantaggio dell'uso dei segnali real-time è che essendo questi
-ultimi dotati di una coda di consegna ogni segnale sarà associato ad uno solo
-file descriptor; inoltre sarà possibile stabilire delle priorità nella
-risposta a seconda del segnale usato, dato che i segnali real-time supportano
-anche questa funzionalità. In questo modo si può identificare immediatamente
-un file su cui l'accesso è diventato possibile evitando completamente l'uso di
-funzioni come \func{poll} e \func{select}, almeno fintanto che non si satura
-la coda.
-
-Se infatti si eccedono le dimensioni di quest'ultima, il kernel, non potendo
-più assicurare il comportamento corretto per un segnale real-time, invierà al
-suo posto un solo \const{SIGIO}, su cui si saranno accumulati tutti i segnali
-in eccesso, e si dovrà allora determinare con un ciclo quali sono i file
-diventati attivi. L'unico modo per essere sicuri che questo non avvenga è di
-impostare la lunghezza della coda dei segnali real-time ad una dimensione
-identica al valore massimo del numero di file descriptor
-utilizzabili.\footnote{vale a dire impostare il contenuto di
- \procfile{/proc/sys/kernel/rtsig-max} allo stesso valore del contenuto di
- \procfile{/proc/sys/fs/file-max}.}
-
-% TODO fare esempio che usa O_ASYNC
-
-\itindend{signal~driven~I/O}
-
-
-
-\subsection{I meccanismi di notifica asincrona.}
-\label{sec:file_asyncronous_lease}
-
-Una delle domande più frequenti nella programmazione in ambiente unix-like è
-quella di come fare a sapere quando un file viene modificato. La
-risposta\footnote{o meglio la non risposta, tanto che questa nelle Unix FAQ
- \cite{UnixFAQ} viene anche chiamata una \textit{Frequently Unanswered
- Question}.} è che nell'architettura classica di Unix questo non è
-possibile. Al contrario di altri sistemi operativi infatti un kernel unix-like
-classico non prevedeva alcun meccanismo per cui un processo possa essere
-\textsl{notificato} di eventuali modifiche avvenute su un file. Questo è il
-motivo per cui i demoni devono essere \textsl{avvisati} in qualche
-modo\footnote{in genere questo vien fatto inviandogli un segnale di
- \const{SIGHUP} che, per una convenzione adottata dalla gran parte di detti
- programmi, causa la rilettura della configurazione.} se il loro file di
-configurazione è stato modificato, perché possano rileggerlo e riconoscere le
-modifiche.
-
-Questa scelta è stata fatta perché provvedere un simile meccanismo a livello
-generico per qualunque file comporterebbe un notevole aumento di complessità
-dell'architettura della gestione dei file, il tutto per fornire una
-funzionalità che serve soltanto in alcuni casi particolari. Dato che
-all'origine di Unix i soli programmi che potevano avere una tale esigenza
-erano i demoni, attenendosi a uno dei criteri base della progettazione, che
-era di far fare al kernel solo le operazioni strettamente necessarie e
-lasciare tutto il resto a processi in user space, non era stata prevista
-nessuna funzionalità di notifica.
-
-Visto però il crescente interesse nei confronti di una funzionalità di questo
-tipo, che è molto richiesta specialmente nello sviluppo dei programmi ad
-interfaccia grafica, quando si deve presentare all'utente lo stato del
-filesystem, sono state successivamente introdotte delle estensioni che
-permettessero la creazione di meccanismi di notifica più efficienti dell'unica
-soluzione disponibile con l'interfaccia tradizionale, che è quella del
-\itindex{polling} \textit{polling}.
-
-Queste nuove funzionalità sono delle estensioni specifiche, non
-standardizzate, che sono disponibili soltanto su Linux (anche se altri kernel
-supportano meccanismi simili). Alcune di esse sono realizzate, e solo a
-partire dalla versione 2.4 del kernel, attraverso l'uso di alcuni
-\textsl{comandi} aggiuntivi per la funzione \func{fcntl} (vedi
-sez.~\ref{sec:file_fcntl}), che divengono disponibili soltanto se si è
-definita la macro \macro{\_GNU\_SOURCE} prima di includere \file{fcntl.h}.
-
-\index{file!lease|(}
-
-La prima di queste funzionalità è quella del cosiddetto \textit{file lease};
-questo è un meccanismo che consente ad un processo, detto \textit{lease
- holder}, di essere notificato quando un altro processo, chiamato a sua volta
-\textit{lease breaker}, cerca di eseguire una \func{open} o una
-\func{truncate} sul file del quale l'\textit{holder} detiene il
-\textit{lease}.
-La notifica avviene in maniera analoga a come illustrato in precedenza per
-l'uso di \const{O\_ASYNC}: di default viene inviato al \textit{lease holder}
-il segnale \const{SIGIO}, ma questo segnale può essere modificato usando il
-comando \const{F\_SETSIG} di \func{fcntl}.\footnote{anche in questo caso si
- può rispecificare lo stesso \const{SIGIO}.} Se si è fatto questo\footnote{è
- in genere è opportuno farlo, come in precedenza, per utilizzare segnali
- real-time.} e si è installato il gestore del segnale con \const{SA\_SIGINFO}
-si riceverà nel campo \var{si\_fd} della struttura \struct{siginfo\_t} il
-valore del file descriptor del file sul quale è stato compiuto l'accesso; in
-questo modo un processo può mantenere anche più di un \textit{file lease}.
-
-Esistono due tipi di \textit{file lease}: di lettura (\textit{read lease}) e
-di scrittura (\textit{write lease}). Nel primo caso la notifica avviene quando
-un altro processo esegue l'apertura del file in scrittura o usa
-\func{truncate} per troncarlo. Nel secondo caso la notifica avviene anche se
-il file viene aperto in lettura; in quest'ultimo caso però il \textit{lease}
-può essere ottenuto solo se nessun altro processo ha aperto lo stesso file.
-
-Come accennato in sez.~\ref{sec:file_fcntl} il comando di \func{fcntl} che
-consente di acquisire un \textit{file lease} è \const{F\_SETLEASE}, che viene
-utilizzato anche per rilasciarlo. In tal caso il file descriptor \param{fd}
-passato a \func{fcntl} servirà come riferimento per il file su cui si vuole
-operare, mentre per indicare il tipo di operazione (acquisizione o rilascio)
-occorrerà specificare come valore dell'argomento \param{arg} di \func{fcntl}
-uno dei tre valori di tab.~\ref{tab:file_lease_fctnl}.
-
-\begin{table}[htb]
- \centering
- \footnotesize
- \begin{tabular}[c]{|l|l|}
- \hline
- \textbf{Valore} & \textbf{Significato} \\
- \hline
- \hline
- \const{F\_RDLCK} & Richiede un \textit{read lease}.\\
- \const{F\_WRLCK} & Richiede un \textit{write lease}.\\
- \const{F\_UNLCK} & Rilascia un \textit{file lease}.\\
- \hline
- \end{tabular}
- \caption{Costanti per i tre possibili valori dell'argomento \param{arg} di
- \func{fcntl} quando usata con i comandi \const{F\_SETLEASE} e
- \const{F\_GETLEASE}.}
- \label{tab:file_lease_fctnl}
-\end{table}
-
-Se invece si vuole conoscere lo stato di eventuali \textit{file lease}
-occorrerà chiamare \func{fcntl} sul relativo file descriptor \param{fd} con il
-comando \const{F\_GETLEASE}, e si otterrà indietro nell'argomento \param{arg}
-uno dei valori di tab.~\ref{tab:file_lease_fctnl}, che indicheranno la
-presenza del rispettivo tipo di \textit{lease}, o, nel caso di
-\const{F\_UNLCK}, l'assenza di qualunque \textit{file lease}.
-
-Si tenga presente che un processo può mantenere solo un tipo di \textit{lease}
-su un file, e che un \textit{lease} può essere ottenuto solo su file di dati
-(pipe e dispositivi sono quindi esclusi). Inoltre un processo non privilegiato
-può ottenere un \textit{lease} soltanto per un file appartenente ad un
-\acr{uid} corrispondente a quello del processo. Soltanto un processo con
-privilegi di amministratore (cioè con la \itindex{capabilities} capability
-\const{CAP\_LEASE}, vedi sez.~\ref{sec:proc_capabilities}) può acquisire
-\textit{lease} su qualunque file.
-
-Se su un file è presente un \textit{lease} quando il \textit{lease breaker}