-%% fileadv.tex
-%%
-%% Copyright (C) 2000-2015 Simone Piccardi. Permission is granted to
-%% copy, distribute and/or modify this document under the terms of the GNU Free
-%% Documentation License, Version 1.1 or any later version published by the
-%% Free Software Foundation; with the Invariant Sections being "Un preambolo",
-%% with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the
-%% license is included in the section entitled "GNU Free Documentation
-%% License".
-%%
-
-\chapter{La gestione avanzata dei file}
-\label{cha:file_advanced}
-In questo capitolo affronteremo le tematiche relative alla gestione avanzata
-dei file. Inizieremo con la trattazione delle problematiche del \textit{file
- locking} e poi prenderemo in esame le varie funzionalità avanzate che
-permettono una gestione più sofisticata dell'I/O su file, a partire da quelle
-che consentono di gestire l'accesso contemporaneo a più file esaminando le
-varie modalità alternative di gestire l'I/O per concludere con la gestione dei
-file mappati in memoria e le altre funzioni avanzate che consentono un
-controllo più dettagliato delle modalità di I/O.
-
-
-\section{Il \textit{file locking}}
-\label{sec:file_locking}
-
-\itindbeg{file~locking}
-
-In sez.~\ref{sec:file_shared_access} abbiamo preso in esame le modalità in cui
-un sistema unix-like gestisce l'accesso concorrente ai file da parte di
-processi diversi. In quell'occasione si è visto come, con l'eccezione dei file
-aperti in \itindex{append~mode} \textit{append mode}, quando più processi
-scrivono contemporaneamente sullo stesso file non è possibile determinare la
-sequenza in cui essi opereranno.
-
-Questo causa la possibilità di una \itindex{race~condition} \textit{race
- condition}; in generale le situazioni più comuni sono due: l'interazione fra
-un processo che scrive e altri che leggono, in cui questi ultimi possono
-leggere informazioni scritte solo in maniera parziale o incompleta; o quella
-in cui diversi processi scrivono, mescolando in maniera imprevedibile il loro
-output sul file.
-
-In tutti questi casi il \textit{file locking} è la tecnica che permette di
-evitare le \itindex{race~condition} \textit{race condition}, attraverso una
-serie di funzioni che permettono di bloccare l'accesso al file da parte di
-altri processi, così da evitare le sovrapposizioni, e garantire la atomicità
-delle operazioni di lettura o scrittura.
-
-
-\subsection{L'\textit{advisory locking}}
-\label{sec:file_record_locking}
-
-La prima modalità di \textit{file locking} che è stata implementata nei
-sistemi unix-like è quella che viene usualmente chiamata \textit{advisory
- locking},\footnote{Stevens in \cite{APUE} fa riferimento a questo argomento
- come al \textit{record locking}, dizione utilizzata anche dal manuale delle
- \acr{glibc}; nelle pagine di manuale si parla di \textit{discrectionary file
- lock} per \func{fcntl} e di \textit{advisory locking} per \func{flock},
- mentre questo nome viene usato da Stevens per riferirsi al \textit{file
- locking} POSIX. Dato che la dizione \textit{record locking} è quantomeno
- ambigua, in quanto in un sistema Unix non esiste niente che possa fare
- riferimento al concetto di \textit{record}, alla fine si è scelto di
- mantenere il nome \textit{advisory locking}.} in quanto sono i singoli
-processi, e non il sistema, che si incaricano di asserire e verificare se
-esistono delle condizioni di blocco per l'accesso ai file.
-
-Questo significa che le funzioni \func{read} o \func{write} vengono eseguite
-comunque e non risentono affatto della presenza di un eventuale \textit{lock};
-pertanto è sempre compito dei vari processi che intendono usare il
-\textit{file locking}, controllare esplicitamente lo stato dei file condivisi
-prima di accedervi, utilizzando le relative funzioni.
-
-In generale si distinguono due tipologie di \textit{file lock};\footnote{di
- seguito ci riferiremo sempre ai blocchi di accesso ai file con la
- nomenclatura inglese di \textit{file lock}, o più brevemente con
- \textit{lock}, per evitare confusioni linguistiche con il blocco di un
- processo (cioè la condizione in cui il processo viene posto in stato di
- \textit{sleep}).} la prima è il cosiddetto \textit{shared lock}, detto anche
-\textit{read lock} in quanto serve a bloccare l'accesso in scrittura su un
-file affinché il suo contenuto non venga modificato mentre lo si legge. Si
-parla appunto di \textsl{blocco condiviso} in quanto più processi possono
-richiedere contemporaneamente uno \textit{shared lock} su un file per
-proteggere il loro accesso in lettura.
-
-La seconda tipologia è il cosiddetto \textit{exclusive lock}, detto anche
-\textit{write lock} in quanto serve a bloccare l'accesso su un file (sia in
-lettura che in scrittura) da parte di altri processi mentre lo si sta
-scrivendo. Si parla di \textsl{blocco esclusivo} appunto perché un solo
-processo alla volta può richiedere un \textit{exclusive lock} su un file per
-proteggere il suo accesso in scrittura.
-
-In Linux sono disponibili due interfacce per utilizzare l'\textit{advisory
- locking}, la prima è quella derivata da BSD, che è basata sulla funzione
-\func{flock}, la seconda è quella recepita dallo standard POSIX.1 (che è
-derivata dall'interfaccia usata in System V), che è basata sulla funzione
-\func{fcntl}. I \textit{file lock} sono implementati in maniera completamente
-indipendente nelle due interfacce (in realtà con Linux questo avviene solo
-dalla serie 2.0 dei kernel) che pertanto possono coesistere senza
-interferenze.
-
-Entrambe le interfacce prevedono la stessa procedura di funzionamento: si
-inizia sempre con il richiedere l'opportuno \textit{file lock} (un
-\textit{exclusive lock} per una scrittura, uno \textit{shared lock} per una
-lettura) prima di eseguire l'accesso ad un file. Se il blocco viene acquisito
-il processo prosegue l'esecuzione, altrimenti (a meno di non aver richiesto un
-comportamento non bloccante) viene posto in stato di \textit{sleep}. Una volta
-finite le operazioni sul file si deve provvedere a rimuovere il blocco.
-
-La situazione delle varie possibilità che si possono verificare è riassunta in
-tab.~\ref{tab:file_file_lock}, dove si sono riportati, a seconda delle varie
-tipologie di blocco già presenti su un file, il risultato che si avrebbe in
-corrispondenza di una ulteriore richiesta da parte di un processo di un blocco
-nelle due tipologie di \textit{file lock} menzionate, con un successo o meno
-della richiesta.
-
-\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} & esecuzione & esecuzione & blocco \\
- \textit{Write lock}& esecuzione & blocco & blocco \\
- \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 di sistema
-usata per richiedere e rimuovere un \textit{file lock} è \funcd{flock}, ed il
-suo prototipo è:
-
-\begin{funcproto}{
-\fhead{sys/file.h}
-\fdecl{int flock(int fd, int operation)}
-\fdesc{Applica o rimuove un \textit{file lock}.}
-}
-
-{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
- caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale
- nell'attesa dell'acquisizione di un \textit{file lock}.
- \item[\errcode{EINVAL}] si è specificato un valore non valido
- per \param{operation}.
- \item[\errcode{ENOLCK}] il kernel non ha memoria sufficiente per gestire il
- \textit{file lock}.
- \item[\errcode{EWOULDBLOCK}] il file ha già un blocco attivo, e si è
- specificato \const{LOCK\_NB}.
- \end{errlist}
- ed inoltre \errval{EBADF} nel suo significato generico.
-}
-\end{funcproto}
-
-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} rispettivamente condiviso o esclusivo, ed
-ovviamente non possono essere usati insieme. Se con essi 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 direttamente \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 \itindex{inode}
-\textit{inode}, dato che questo è l'unico riferimento in comune che possono
-avere due processi diversi che aprono lo stesso file.
-
-In particolare, come accennato in fig.~\ref{fig:file_flock_struct}, i
-\textit{file lock} sono mantenuti in una \itindex{linked~list} \textit{linked
- list} di strutture \kstruct{file\_lock}. La lista è referenziata
-dall'indirizzo di partenza mantenuto dal campo \var{i\_flock} della struttura
-\kstruct{inode} (per le definizioni esatte si faccia riferimento al file
-\file{include/linux/fs.h} nei sorgenti del kernel). Un bit del campo
-\var{fl\_flags} di specifica se si tratta di un lock in semantica BSD
-(\const{FL\_FLOCK}) o POSIX (\const{FL\_POSIX}) o un \textit{file lease}
-(\const{FL\_LEASE}, vedi sez.~\ref{sec:file_asyncronous_lease}).
-
-\begin{figure}[!htb]
- \centering
- \includegraphics[width=12cm]{img/file_flock}
- \caption{Schema dell'architettura del \textit{file locking}, nel caso
- particolare del suo utilizzo da parte dalla funzione \func{flock}.}
- \label{fig:file_flock_struct}
-\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 (cioè l'aggiunta di una nuova struttura
-\kstruct{file\_lock}). Nel caso dei blocchi creati con \func{flock} la
-semantica della funzione prevede che sia \func{dup} che \func{fork} non creino
-ulteriori istanze di un \textit{file lock} quanto piuttosto degli ulteriori
-riferimenti allo stesso. Questo viene realizzato dal kernel secondo lo schema
-di fig.~\ref{fig:file_flock_struct}, associando ad ogni nuovo \textit{file
- lock} un puntatore alla voce nella \itindex{file~table} \textit{file table}
-da cui si è richiesto il blocco, che così ne identifica il titolare. Il
-puntatore è mantenuto nel campo \var{fl\_file} di \kstruct{file\_lock}, e
-viene utilizzato solo per i \textit{file lock} creati con la semantica BSD.
-
-Questa struttura prevede che, quando si richiede la rimozione di un
-\textit{file lock}, il kernel acconsenta solo se la richiesta proviene da un
-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_shared_access}, 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 di sistema
-\func{fcntl}. Abbiamo già trattato questa funzione nelle sue molteplici
-possibilità di utilizzo in sez.~\ref{sec:file_fcntl_ioctl}. Quando la si
-impiega per il \textit{file locking} essa viene usata solo secondo il seguente
-prototipo:
-
-\begin{funcproto}{
-\fhead{fcntl.h}
-\fdecl{int fcntl(int fd, int cmd, struct flock *lock)}
-\fdesc{Applica o rimuove un \textit{file lock}.}
-}
-
-{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
- caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EACCES}] l'operazione è proibita per la presenza di
- \textit{file lock} da parte di altri processi.
- \item[\errcode{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}.
- \item[\errcode{ENOLCK}] il sistema non ha le risorse per il blocco: ci
- sono troppi segmenti di \textit{lock} aperti, si è esaurita la tabella
- dei \textit{file lock}, o il protocollo per il blocco remoto è fallito.
- \end{errlist}
- ed inoltre \errval{EBADF}, \errval{EFAULT} nel loro significato generico.}
-\end{funcproto}
-
-Al contrario di quanto avviene con l'interfaccia basata su \func{flock} con
-\func{fcntl} è possibile bloccare anche delle singole sezioni di un file, fino
-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}[!htb]
- \footnotesize \centering
- \begin{minipage}[c]{0.90\textwidth}
- \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 tab.~\ref{tab:lseek_whence_values}).
-
-Si tenga presente che un \textit{file lock} può essere richiesto anche per una
-regione al di là della corrente fine del file, così che una eventuale
-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.
-
-Lo standard POSIX non richiede che \var{l\_len} sia positivo, ed a partire dal
-kernel 2.4.21 è possibile anche indicare valori di \var{l\_len} negativi, in
-tal caso l'intervallo coperto va da \var{l\_start}$+$\var{l\_len} a
-\var{l\_start}$-1$, mentre per un valore positivo l'intervallo va da
-\var{l\_start} a \var{l\_start}$+$\var{l\_len}$-1$. Si può però usare un
-valore negativo soltanto se l'inizio della regione indicata non cade prima
-dell'inizio del file, mentre come accennato con un valore positivo si
-può anche indicare una regione che eccede la dimensione corrente del file.
-
-Il tipo di \textit{file lock} richiesto viene specificato dal campo
-\var{l\_type}, esso può assumere i tre valori definiti dalle costanti
-riportate in tab.~\ref{tab:file_flock_type}, che permettono di richiedere
-rispettivamente uno \textit{shared lock}, un \textit{esclusive lock}, e la
-rimozione di un blocco precedentemente acquisito. Infine il campo \var{l\_pid}
-viene usato solo in caso di lettura, quando si chiama \func{fcntl} con
-\const{F\_GETLK}, e riporta il \ids{PID} del processo che detiene il
-\textit{file lock}.
-
-\begin{table}[htb]
- \centering
- \footnotesize
- \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}
-
-Oltre a quanto richiesto tramite i campi di \struct{flock}, l'operazione
-effettivamente svolta dalla funzione è stabilita dal valore dall'argomento
-\param{cmd} che, come già riportato in sez.~\ref{sec:file_fcntl_ioctl},
-specifica l'azione da compiere; i valori utilizzabili relativi al \textit{file
- locking} sono tre:
-\begin{basedescript}{\desclabelwidth{2.0cm}}
-\item[\const{F\_GETLK}] verifica se il \textit{file lock} specificato dalla
- struttura puntata da \param{lock} può essere acquisito: in caso negativo
- 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}. In questo caso nella figura si sono
-evidenziati solo i campi di \kstruct{file\_lock} significativi per la
-semantica POSIX, in particolare adesso ciascuna struttura contiene, oltre al
-\ids{PID} del processo in \var{fl\_pid}, la sezione di file che viene bloccata
-grazie ai campi \var{fl\_start} e \var{fl\_end}. La struttura è comunque la
-stessa, solo che in questo caso nel campo \var{fl\_flags} è impostato il bit
-\const{FL\_POSIX} ed il campo \var{fl\_file} non viene usato. Il blocco è
-sempre associato \itindex{inode} all'\textit{inode}, solo che in questo caso
-la titolarità non viene identificata con il riferimento ad una voce nella
-\itindex{file~table} \textit{file table}, ma con il valore del \ids{PID} del
-processo.
-
-\begin{figure}[!htb]
- \centering \includegraphics[width=12cm]{img/file_posix_lock}
- \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
- \kstruct{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
-\ids{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 \ids{PID} diverso, mentre passa indenne attraverso una
-\func{exec} in quanto il \ids{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 \ids{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
-\ids{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 \ids{PID} del processo che chiama la funzione, queste richieste
-avranno sempre successo. Nel caso della semantica BSD, essendo i lock
-relativi a tutto un file e non accumulandosi,\footnote{questa ultima
- caratteristica è vera in generale, se cioè si richiede più volte lo stesso
- \textit{file lock}, o più blocchi sulla stessa sezione di file, le richieste
- non si cumulano e basta una sola richiesta di rilascio per cancellare il
- blocco.} la cosa non ha alcun effetto; la funzione ritorna con successo,
-senza che il kernel debba modificare la lista dei \textit{file lock}.
-
-Con i \textit{file lock} POSIX invece si possono avere una serie di situazioni
-diverse: ad esempio è possibile rimuovere con una sola chiamata più
-\textit{file lock} distinti (indicando in una regione che si sovrapponga
-completamente a quelle di questi ultimi), o rimuovere solo una parte di un
-blocco preesistente (indicando una regione contenuta in quella di un altro
-blocco), creando un buco, o coprire con un nuovo blocco altri \textit{file
- lock} già ottenuti, e così via, a secondo di come si sovrappongono le
-regioni richieste e del tipo di operazione richiesta.
-
-Il comportamento seguito in questo caso è che la funzione ha successo ed
-esegue l'operazione richiesta sulla regione indicata; è compito del kernel
-preoccuparsi di accorpare o dividere le voci nella lista dei \textit{file
- lock} per far si che le regioni bloccate da essa risultanti siano coerenti
-con quanto necessario a soddisfare l'operazione richiesta.
-
-\begin{figure}[!htbp]
- \footnotesize \centering
- \begin{minipage}[c]{\codesamplewidth}
- \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, nel file \texttt{Flock.c}).
-
-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
-si 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:
-
-\begin{Console}
-[piccardi@gont sources]$ \textbf{./flock -r Flock.c}
-Lock acquired
-\end{Console}
-%$
-il programma segnalerà di aver acquisito un blocco e si bloccherà; in questo
-caso si è usato il \textit{file locking} POSIX e non avendo specificato niente
-riguardo alla sezione che si vuole bloccare sono stati usati i valori
-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:
-
-\begin{Console}
-[piccardi@gont sources]$ \textbf{./flock -w Flock.c}
-Failed lock: Resource temporarily unavailable
-\end{Console}
-%$
-come ci aspettiamo il programma terminerà segnalando l'indisponibilità del
-blocco, dato che il file è bloccato dal precedente \textit{read lock}. Si noti
-che il risultato è lo stesso anche se si richiede il blocco su una sola parte
-del file con il comando:
-
-\begin{Console}
-[piccardi@gont sources]$ \textbf{./flock -w -s0 -l10 Flock.c}
-Failed lock: Resource temporarily unavailable
-\end{Console}
-%$
-se invece blocchiamo una regione con:
-
-\begin{Console}
-[piccardi@gont sources]$ \textbf{./flock -r -s0 -l10 Flock.c}
-Lock acquired
-\end{Console}
-%$
-una volta che riproviamo ad acquisire il \textit{write lock} i risultati
-dipenderanno dalla regione richiesta; ad esempio nel caso in cui le due
-regioni si sovrappongono avremo che:
-
-\begin{Console}
-[piccardi@gont sources]$ \textbf{./flock -w -s5 -l15 Flock.c}
-Failed lock: Resource temporarily unavailable
-\end{Console}
-%$
-ed il blocco viene rifiutato, ma se invece si richiede una regione distinta
-avremo che:
-
-\begin{Console}
-[piccardi@gont sources]$ \textbf{./flock -w -s11 -l15 Flock.c}
-Lock acquired
-\end{Console}
-%$
-ed il blocco viene acquisito. Se a questo punto si prova ad eseguire un
-\textit{read lock} che comprende la nuova regione bloccata in scrittura:
-
-\begin{Console}
-[piccardi@gont sources]$ \textbf{./flock -r -s10 -l20 Flock.c}
-Failed lock: Resource temporarily unavailable
-\end{Console}
-%$
-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:
-
-\begin{Console}
-[piccardi@gont sources]$ \textbf{./flock -r -b -s0 -l10 Flock.c} Lock acquired
-\end{Console}
-%$
-il primo comando acquisisce subito un \textit{read lock}, e quindi non cambia
-nulla, ma se proviamo adesso a richiedere un \textit{write lock} che non potrà
-essere acquisito otterremo:
-
-\begin{Console}
-[piccardi@gont sources]$ \textbf{./flock -w -s0 -l10 Flock.c}
-\end{Console}
-%$
-il programma cioè si bloccherà nella chiamata a \func{fcntl}; se a questo
-punto rilasciamo il precedente blocco (terminando il primo comando un
-\texttt{C-c} sul terminale) potremo verificare che sull'altro terminale il
-blocco viene acquisito, con la comparsa di una nuova riga:
-
-\begin{Console}
-[piccardi@gont sources]$ \textbf{./flock -w -s0 -l10 Flock.c}
-Lock acquired
-\end{Console}
-%$
-
-Un'altra cosa che si può controllare con il nostro programma è l'interazione
-fra i due tipi di blocco; se ripartiamo dal primo comando con cui si è
-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:
-
-\begin{Console}
-[root@gont sources]# \textbf{./flock -f -w Flock.c}
-Lock acquired
-\end{Console}
-%$
-che ci mostra come i due tipi di blocco siano assolutamente indipendenti; per
-questo motivo occorre sempre tenere presente quale, fra le due semantiche
-disponibili, stanno usando i programmi con cui si interagisce, dato che i
-blocchi applicati con l'altra non avrebbero nessun effetto.
-
-% \subsection{La funzione \func{lockf}}
-% \label{sec:file_lockf}
-
-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 che utilizza la
-funzione \funcd{lockf},\footnote{la funzione è ripresa da System V e per
- poterla utilizzare è richiesta che siano definite le opportune macro, una
- fra \macro{\_BSD\_SOURCE} o \macro{\_SVID\_SOURCE}, oppure
- \macro{\_XOPEN\_SOURCE} ad un valore di almeno 500, oppure
- \macro{\_XOPEN\_SOURCE} e \macro{\_XOPEN\_SOURCE\_EXTENDED}.} il cui
-prototipo è:
-
-\begin{funcproto}{
-\fhead{unistd.h}
-\fdecl{int lockf(int fd, int cmd, off\_t len)}
-\fdesc{Applica, controlla o rimuove un \textit{file lock}.}
-}
-
-{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
- caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EAGAIN}] il file è bloccato, e si sono richiesti
- \const{F\_TLOCK} o \const{F\_TEST} (in alcuni casi può dare anche
- \errcode{EACCESS}.
- \item[\errcode{EBADF}] \param{fd} non è un file descriptor aperto o si sono
- richiesti \const{F\_LOCK} o \const{F\_TLOCK} ma il file non è scrivibile.
- \item[\errcode{EINVAL}] si è usato un valore non valido per \param{cmd}.
- \end{errlist}
- ed inoltre \errcode{EDEADLK} e \errcode{ENOLCK} con lo stesso significato
- che hanno con \funcd{fcntl}.
-}
-\end{funcproto}
-
-La funzione opera sul file indicato dal file descriptor \param{fd}, che deve
-essere aperto in scrittura, perché utilizza soltanto \textit{lock}
-esclusivi. La sezione di file bloccata viene controllata dal valore
-di \param{len}, che indica la lunghezza della stessa, usando come riferimento
-la posizione corrente sul file. La sezione effettiva varia a secondo del
-segno, secondo lo schema illustrato in fig.~\ref{fig:file_lockf_boundary}, se
-si specifica un valore nullo il file viene bloccato a partire dalla posizione
-corrente fino alla sua fine presente o futura (nello schema corrisponderebbe
-ad un valore infinito positivo).
-
-\begin{figure}[!htb]
- \centering
- \includegraphics[width=10cm]{img/lockf_boundary}
- \caption{Schema della sezione di file bloccata con \func{lockf}.}
- \label{fig:file_lockf_boundary}
-\end{figure}
-
-Il comportamento della funzione viene controllato dal valore
-dell'argomento \param{cmd}, che specifica quale azione eseguire, i soli valori
-consentiti sono i seguenti:
-
-\begin{basedescript}{\desclabelwidth{2.0cm}}
-\item[\const{F\_LOCK}] Richiede un \textit{lock} esclusivo sul file, e blocca
- il processo chiamante se, anche parzialmente, la sezione indicata si
- sovrappone ad una che è già stata bloccata da un altro processo; in caso di
- sovrapposizione con un altro blocco già ottenuto le sezioni vengono unite.
-\item[\const{F\_TLOCK}] Richiede un \textit{exclusive lock}, in maniera
- identica a \const{F\_LOCK}, ma in caso di indisponibilità non blocca il
- processo restituendo un errore di \errval{EAGAIN}.
-\item[\const{F\_ULOCK}] Rilascia il blocco sulla sezione indicata, questo può
- anche causare la suddivisione di una sezione bloccata in precedenza nelle
- due parti eccedenti nel caso si sia indicato un intervallo più limitato.
-\item[\const{F\_TEST}] Controlla la presenza di un blocco sulla sezione di
- file indicata, \func{lockf} ritorna $0$ se la sezione è libera o bloccata
- dal processo stesso, o $-1$ se è bloccata da un altro processo, nel qual
- caso \var{errno} assume il valore \errval{EAGAIN} (ma su alcuni sistemi può
- essere restituito anche \errval{EACCESS}).
-\end{basedescript}
-
-La funzione è semplicemente una diversa interfaccia al \textit{file locking}
-POSIX ed è realizzata utilizzando \func{fcntl}; pertanto la semantica delle
-operazioni è la stessa di quest'ultima e quindi la funzione presenta lo stesso
-comportamento riguardo gli effetti della chiusura dei file, ed il
-comportamento sui file duplicati e nel passaggio attraverso \func{fork} ed
-\func{exec}. Per questo stesso motivo la funzione non è equivalente a
-\func{flock} e può essere usata senza interferenze insieme a quest'ultima.
-
-% TODO trattare i POSIX file-private lock introdotti con il 3.15,
-% vedi http://lwn.net/Articles/586904/ correlato:
-% http://www.samba.org/samba/news/articles/low_point/tale_two_stds_os2.html
-
-\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} dei permessi dei
-file. Se si ricorda quanto esposto in sez.~\ref{sec:file_special_perm}), esso
-viene di norma utilizzato per cambiare il \ids{GID} 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 sez.~\ref{sec:filesystem_mounting}, 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 (vedi
-sez.~\ref{sec:file_memory_map}); anche in tal caso infatti, quando si esegue
-la mappatura con l'opzione \const{MAP\_SHARED}, si ha un accesso al contenuto
-del file. Lo standard SVID prevede che sia impossibile eseguire il
-\textit{memory mapping} di un file su cui sono presenti dei
-blocchi\footnote{alcuni sistemi, come HP-UX, sono ancora più restrittivi e lo
- impediscono anche in caso di \textit{advisory locking}, anche se questo
- comportamento non ha molto senso, dato che comunque qualunque accesso
- 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.
-
-Si tenga conto infine che su Linux l'implementazione corrente del
-\textit{mandatory locking} è difettosa e soffre di una \textit{race
- condition}, per cui una scrittura con \func{write} che si sovrapponga alla
-richiesta di un \textit{read lock} può modificare i dati anche dopo che questo
-è stato ottenuto, ed una lettura con \func{read} può restituire dati scritti
-dopo l'ottenimento di un \textit{write lock}. Lo stesso tipo di problema si
-può presentare anche con l'uso di file mappati in memoria; pertanto allo stato
-attuale delle cose è sconsigliabile fare affidamento sul \textit{mandatory
- locking}.
-
-\itindend{file~locking}
-
-\itindend{mandatory~locking}
-
-
-\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
-sez.~\ref{sec:file_unix_interface} e sez.~\ref{sec: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} \textit{system call},\index{system~call~lente}
-che in certi casi le funzioni di I/O eseguite su un file descritor possono
-bloccarsi indefinitamente. Questo non avviene mai per i file normali, per i
-quali le funzioni di lettura e scrittura ritornano sempre subito, ma può
-avvenire per alcuni \index{file!di~dispositivo} file di dispositivo, come ad
-esempio una seriale o un terminale, o con l'uso di file descriptor collegati a
-meccanismi di intercomunicazione come le \textit{pipe} (vedi
-sez.~\ref{sec:ipc_unix}) ed i socket (vedi sez.~\ref{sec:sock_socket_def}). In
-casi come questi ad esempio una operazione di lettura potrebbe bloccarsi se
-non ci sono dati disponibili sul descrittore su cui la si sta effettuando.
-
-Questo comportamento è alla radice di una delle problematiche più comuni che
-ci si trova ad affrontare nella gestione delle operazioni di I/O: la necessità
-di operare su più file descriptor eseguendo funzioni che possono bloccarsi
-indefinitamente senza che sia possibile prevedere quando questo può
-avvenire. Un caso classico è quello di un server di rete (tratteremo la
-problematica in dettaglio nella seconda parte della guida) in attesa di dati
-in ingresso prevenienti da vari client.
-
-In un caso di questo tipo, se si andasse ad operare sui vari file descriptor
-aperti uno dopo l'altro, potrebbe accadere di restare bloccati nell'eseguire
-una lettura su uno di quelli che non è ``\textsl{pronto}'', quando ce ne
-potrebbe essere un altro con dati disponibili. 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
-dell'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_close} 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 lettura o scrittura 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 \textit{system call}
-che nella gran parte dei casi falliranno.
-
-É appunto per superare questo problema è stato introdotto il concetto di
-\textit{I/O multiplexing}, una nuova modalità per la gestione dell'I/O che
-consente di tenere sotto controllo più file descriptor in contemporanea,
-permettendo di bloccare un processo quando le operazioni di lettura o
-scrittura non sono immediatamente effettuabili, e di riprenderne l'esecuzione
-una volta che almeno una di quelle che erano state richieste diventi
-possibile, 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, con la funzione \funcd{select} che è apparsa in
-BSD4.2 ed è stata standardizzata in BSD4.4, in seguito è stata portata su
-tutti i sistemi che supportano i socket, compreso le varianti di System V ed
-inserita in POSIX.1-2001; il suo prototipo è:\footnote{l'header
- \texttt{sys/select.h} è stato introdotto con POSIX.1-2001, è ed presente con
- le \acr{glibc} a partire dalla versione 2.0, in precedenza, con le
- \acr{libc4} e le \acr{libc5}, occorreva includere \texttt{sys/time.h},
- \texttt{sys/types.h} e \texttt{unistd.h}.}
-
-\begin{funcproto}{
-\fhead{sys/select.h}
-\fdecl{int select(int ndfs, fd\_set *readfds, fd\_set *writefds, fd\_set
- *exceptfds, \\
-\phantom{int select(}struct timeval *timeout)}
-\fdesc{Attende che uno fra i file descriptor degli insiemi specificati diventi
- attivo.}
-}
-{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
- caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EBADF}] si è specificato un file descriptor non valido
- (chiuso o con errori) 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} nel suo significato generico.}
-\end{funcproto}
-
-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:
-
-{\centering
-\vspace{3pt}
-\begin{funcbox}{
-\fhead{sys/select.h}
-\fdecl{void \macro{FD\_ZERO}(fd\_set *set)}
-\fdesc{Inizializza l'insieme (vuoto).}
-\fdecl{void \macro{FD\_SET}(int fd, fd\_set *set)}
-\fdesc{Inserisce il file descriptor \param{fd} nell'insieme.}
-\fdecl{void \macro{FD\_CLR}(int fd, fd\_set *set)}
-\fdesc{Rimuove il file descriptor \param{fd} dall'insieme.}
-\fdecl{int \macro{FD\_ISSET}(int fd, fd\_set *set)}
-\fdesc{Controlla se il file descriptor \param{fd} è nell'insieme.}
-}
-\end{funcbox}}
-
-
-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 (ad esempio in Linux, fino alla
-serie 2.0.x, c'era un limite di 256 file per processo), ma da quando, nelle
-versioni più recenti del kernel, questo limite è stato rimosso, esso indica le
-dimensioni massime dei numeri usati nei \textit{file descriptor set}, ed il
-suo valore, secondo lo standard POSIX 1003.1-2001, è definito in
-\headfile{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}.} 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. Si ricordi infatti 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 il puntatore ad 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, e così può essere utilizzata eseguire il \itindex{polling}
-\textit{polling} su un gruppo di file descriptor. Usare questo argomento con
-tutti i \textit{file descriptor set} vuoti è un modo portabile, disponibile
-anche su sistemi in cui non sono disponibili le funzioni avanzate di
-sez.~\ref{sec:sig_timer_adv}, per tenere un processo in stato di
-\textit{sleep} con precisioni inferiori al secondo.
-
-In caso di successo la funzione restituisce il numero di file descriptor
-pronti, seguendo il comportamento previsto dallo standard
-POSIX.1-2001,\footnote{si tenga però presente che esistono alcune versioni di
- Unix che non si comportano in questo modo, restituendo un valore positivo
- generico.} 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 scade il tempo indicato
-da \param{timout} viene restituito un valore nullo e i \textit{file descriptor
- set} non vengono modificati. In caso di errore la funzione restituisce $-1$, i
-valori dei tre insiemi e di \param{timeout} sono indefiniti e non si può fare
-nessun affidamento sul loro contenuto; nelle versioni più recenti della
-funzione invece i \textit{file descriptor set} non vengono modificati anche in
-caso di errore.
-
-Si tenga presente infine che su Linux, in caso di programmazione
-\textit{multithread} se un file descriptor viene chiuso in un altro
-\textit{thread} rispetto a quello in cui si sta usando \func{select}, questa
-non subisce nessun effetto. In altre varianti di sistemi unix-like invece
-\func{select} ritorna indicando che il file descriptor è pronto, con
-conseguente possibile errore nel caso lo si usi senza che sia stato
-riaperto. Lo standard non prevede niente al riguardo e non si deve dare per
-assunto nessuno dei due comportamenti se si vogliono scrivere programmi
-portabili.
-
-
-\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
-\func{select} fornisce solo di un suggerimento, esistono infatti condizioni in
-cui \func{select} può riportare in maniera spuria che un file descriptor è
-pronto, ma l'esecuzione di una operazione di I/O si bloccherebbe: ad esempio
-con Linux questo avviene quando su un socket arrivano dei dati che poi vengono
-scartati perché corrotti (ma sono possibili pure altri casi); in tal caso pur
-risultando il relativo file descriptor pronto in lettura una successiva
-esecuzione di una \func{read} si bloccherebbe. Per questo motivo quando si usa
-l'\textit{I/O multiplexing} è sempre raccomandato l'uso delle funzioni di
-lettura e scrittura in modalità non bloccante.
-
-Su Linux quando la \textit{system call} \func{select} viene interrotta da un
-segnale modifica il valore nella struttura puntata da \param{timeout},
-impostandolo al tempo restante. In tal caso infatti si ha un errore di
-\errcode{EINTR} ed occorre rilanciare la funzione per proseguire l'attesa, ed
-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. 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 e per questo motivo le \acr{glibc} nascondono il comportamento
-passando alla \textit{system call} una copia dell'argomento \param{timeout}.
-
-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 \headfile{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 \headfile{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{funcproto}{
-\fhead{sys/select.h}
-\fdecl{int pselect(int n, fd\_set *readfds, fd\_set *writefds,
- fd\_set *exceptfds, \\
-\phantom{int pselect(}struct timespec *timeout, sigset\_t *sigmask)}
-\fdesc{Attende che uno dei file descriptor degli insiemi specificati diventi
- attivo.}
-}
-{La funzione ritorna il numero (anche nullo) di file descriptor che sono
- attivi in caso di successo e $-1$ per un 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} nel suo significato generico.
-}
-\end{funcproto}
-
-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. In realtà anche in questo caso la \textit{system call}
-di Linux aggiorna il valore al tempo rimanente, ma la funzione fornita dalle
-\acr{glibc} modifica questo comportamento passando alla \textit{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.
-
-Rispetto a \func{select} la nuova funzione prende un argomento
-aggiuntivo \param{sigmask}, un puntatore ad una \index{maschera~dei~segnali}
-maschera di segnali (si veda sez.~\ref{sec:sig_sigmask}). Nell'esecuzione la
-maschera dei segnali corrente viene sostituita da quella così indicata
-immediatamente prima di eseguire l'attesa, e viene poi ripristinata al ritorno
-della funzione. L'uso di \param{sigmask} è stato introdotto allo scopo di
-prevenire possibili \textit{race condition} \itindex{race~condition} quando
-oltre alla presenza di dati sui file descriptor come nella \func{select}
-ordinaria, ci si deve porre in attesa anche dell'arrivo di un segnale.
-
-Come abbiamo visto in sez.~\ref{sec:sig_example} la tecnica classica per
-rilevare l'arrivo di un segnale è quella di utilizzare il gestore per
-impostare una \index{variabili!globali} variabile globale e controllare questa
-nel corpo principale del programma; abbiamo visto in quell'occasione come
-questo lasci spazio a possibili \itindex{race~condition} \textit{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 \textit{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 \textit{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 \textit{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 una
-interfaccia completamente diversa, basata sulla funzione di sistema
-\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, originariamente l'argomento \param{nfds} era di
- tipo \ctyp{unsigned int}, la funzione è stata inserita nello standard
- POSIX.1-2001 in cui è stato introdotto il tipo nativo \type{nfds\_t}.} il
-cui prototipo è:
-
-\begin{funcproto}{
-\fhead{sys/poll.h}
-\fdecl{int poll(struct pollfd *ufds, nfds\_t nfds, int timeout)}
-\fdesc{Attende un cambiamento di stato su un insieme di file
- descriptor.}
-}
-
-{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
- caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{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
- \const{RLIMIT\_NOFILE}.
- \end{errlist}
- ed inoltre \errval{EFAULT} e \errval{ENOMEM} nel loro significato generico.}
-\end{funcproto}
-
-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}.
-
-\begin{figure}[!htb]
- \footnotesize \centering
- \begin{minipage}[c]{0.90\textwidth}
- \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}
-
-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} ed il campo \var{revents} verrà azzerato, questo
-consente di eliminare temporaneamente un file descriptor dalla lista senza
-dover modificare il vettore \param{ufds}. 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.
-
-Le costanti che definiscono i valori relativi ai bit usati nelle maschere
-binarie dei campi \var{events} e \var{revents} sono riportate in
-tab.~\ref{tab:file_pollfd_flags}, insieme al loro significato. Le si sono
-suddivise in tre gruppi principali, nel primo gruppo si sono indicati i bit
-utilizzati per controllare l'attività in ingresso, nel secondo quelli per
-l'attività in uscita, infine 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 System V che usa i cosiddetti
-``\textit{stream}''. Si tratta di una interfaccia specifica di SysV non
-presente in Linux, che non ha nulla a che fare con gli \textit{stream} delle
-librerie standard del C visti in sez.~\ref{sec:file_stream}. Da essa derivano
-i nomi di alcune costanti poiché per quegli \textit{stream} 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.
-
-Le costanti relative ai diversi tipi di dati normali e prioritari che fanno
-riferimento alle implementazioni in stile System V sono \const{POLLRDNORM},
-\const{POLLWRNORM}, \const{POLLRDBAND} e \const{POLLWRBAND}. Le prime due sono
-equivalenti rispettivamente a \const{POLLIN} e \const{POLLOUT},
-\const{POLLRDBAND} non viene praticamente mai usata su Linux mentre
-\const{POLLWRBAND} ha senso solo sui socket. In ogni caso queste costanti sono
-utilizzabili soltanto qualora si sia definita la macro
-\macro{\_XOPEN\_SOURCE}.
-
-In caso di successo \func{poll} 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, avvalorando i relativi bit
-di \var{revents}. In caso di errori sui file vengono utilizzati i valori della
-terza sezione di tab.~\ref{tab:file_pollfd_flags} che hanno significato solo
-per \var{revents} (se specificati in \var{events} vengono ignorati). Un valore
-di ritorno 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. Infatti, 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{funcproto}{
-\fhead{sys/poll.h}
-\fdecl{int ppoll(struct pollfd *fds, nfds\_t nfds,
- const struct timespec *timeout, \\
-\phantom{int ppoll(}const sigset\_t *sigmask)}
-
-\fdesc{Attende un cambiamento di stato su un insieme di file descriptor.}
-}
-
-{La funzione ritorna il numero di file descriptor con attività in caso di
- successo, $0$ se c'è stato un timeout e $-1$ per un 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}] il valore di \param{nfds} eccede il limite
- \const{RLIMIT\_NOFILE}.
- \end{errlist}
-ed inoltre \errval{EFAULT} e \errval{ENOMEM} nel loro significato generico.
-}
-\end{funcproto}
-
-La funzione ha lo stesso comportamento di \func{poll}, solo che si può
-specificare, con l'argomento \param{sigmask}, il puntatore ad una
-\index{maschera~dei~segnali} 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
-\textit{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} anche se
-in questo caso non esiste nessuno standard che richieda questo comportamento.
-
-Infine anche per \func{poll} e \func{ppoll} valgono le considerazioni relative
-alla possibilità di avere delle notificazione spurie della disponibilita di
-accesso ai file descriptor illustrate per \func{select} in
-sez.~\ref{sec:file_select}, che non staremo a ripetere qui.
-
-\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 (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 \textit{user space} a \textit{kernel space} una lunga lista di
-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 (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 dati 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 è chiamata \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, il supporto
- è stato aggiunto nelle \acr{glibc} a partire dalla versione 2.3.2.} anche se
-sono state in discussione altre interfacce con le quali effettuare lo stesso
-tipo di operazioni; \textit{epoll} è in grado di operare sia in modalità
-\textit{level triggered} che \textit{edge triggered}.
-
-La prima versione di \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 ma poi si è passati all'uso di
-apposite \textit{system call}. Il primo passo per usare l'interfaccia di
-\textit{epoll} è pertanto quello ottenere detto file descriptor chiamando una
-delle due funzioni di sistema \funcd{epoll\_create} e \funcd{epoll\_create1},
-i cui prototipi sono:
-
-\begin{funcproto}{
-\fhead{sys/epoll.h}
-\fdecl{int epoll\_create(int size)}
-\fdecl{int epoll\_create1(int flags)}
-
-\fdesc{Apre un file descriptor per \textit{epoll}.}
-}
-{Le funzioni ritornano un file descriptor per \textit{epoll} in caso di
- successo e $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei
- valori:
- \begin{errlist}
- \item[\errcode{EINVAL}] si è specificato un valore di \param{size} non
- positivo o non valido per \param{flags}.
- \item[\errcode{EMFILE}] si è raggiunto il limite sul numero massimo di
- istanze di \textit{epoll} per utente stabilito da
- \sysctlfile{fs/epoll/max\_user\_instances}.
- \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{funcproto}
-
-Entrambe le funzioni restituiscono un file descriptor, detto anche
-\textit{epoll descriptor}; si tratta di un file descriptor speciale (per cui
-\func{read} e \func{write} non sono supportate) che viene associato alla
-infrastruttura utilizzata dal kernel per gestire la notifica degli eventi, e
-che può a sua volta essere messo sotto osservazione con una chiamata a
-\func{select}, \func{poll} o \func{epoll\_ctl}; in tal caso risulterà pronto
-quando saranno disponibili eventi da notificare riguardo i file descriptor da
-lui osservati.\footnote{è anche possibile inviarlo ad un altro processo
- attraverso un socket locale (vedi sez.~\ref{sec:sock_fd_passing}) ma
- l'operazione non ha alcun senso dato che il nuovo processo non avrà a
- disposizione le copie dei file descriptor messe sotto osservazione tramite
- esso.} Una volta che se ne sia terminato l'uso si potranno rilasciare tutte
-le risorse allocate chiudendolo semplicemente con \func{close}.
-
-Nel caso di \func{epoll\_create} l'argomento \param{size} serviva a dare
-l'indicazione del numero di file descriptor che si vorranno tenere sotto
-controllo, e costituiva solo un suggerimento per semplificare l'allocazione di
-risorse sufficienti, non un valore massimo, ma a partire dal kernel 2.6.8 esso
-viene totalmente ignorato e l'allocazione è sempre dinamica.
-
-La seconda versione della funzione, \func{epoll\_create1} è stata introdotta
-come estensione della precedente (è disponibile solo a partire dal kernel
-2.6.27) per poter passare dei flag di controllo come maschera binaria in fase
-di creazione del file descriptor. Al momento l'unico valore legale
-per \param{flags} (a parte lo zero) è \const{EPOLL\_CLOEXEC}, che consente di
-impostare in maniera atomica sul file descriptor il flag di
-\itindex{close-on-exec} \textit{close-on-exec} (si è trattato il significato
-di \const{O\_CLOEXEC} in sez.~\ref{sec:file_open_close}), senza che sia
-necessaria una successiva chiamata a \func{fcntl}.
-
-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 di sistema
-dell'interfaccia, \funcd{epoll\_ctl}, il cui prototipo è:
-
-\begin{funcproto}{
-\fhead{sys/epoll.h}
-\fdecl{int epoll\_ctl(int epfd, int op, int fd, struct epoll\_event *event)}
-
-\fdesc{Esegue le operazioni di controllo di \textit{epoll}.}
-}
-
-{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
- caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EBADF}] i 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{ENOSPC}] si è raggiunto il limite massimo di registrazioni
- per utente di file descriptor da osservare imposto da
- \sysctlfile{fs/epoll/max\_user\_watches}.
- \item[\errcode{EPERM}] il file associato a \param{fd} non supporta l'uso di
- \textit{epoll}.
- \end{errlist}
- }
-\end{funcproto}
-
-La funzione prende sempre come primo argomento un file descriptor di
-\textit{epoll}, \param{epfd}, che indica quale istanza di \textit{epoll} usare
-e deve pertanto essere stato ottenuto in precedenza con una chiamata a
-\func{epoll\_create} o \func{epoll\_create1}. 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}.
-
-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}
-
-% era stata aggiunta EPOLL_CTL_DISABLE in previsione del kernel 3.7, vedi
-% http://lwn.net/Articles/520012/ e http://lwn.net/Articles/520198/
-% ma non è mai stata inserita.
-
-Le modalità di utilizzo di \textit{epoll} prevedono che si definisca qual'è
-l'insieme dei file descriptor da tenere sotto controllo utilizzando 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 \code{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. 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}.
-
-Anche se è possibile tenere sotto controllo lo stesso file descriptor in due
-istanze distinte di \textit{epoll} in genere questo è sconsigliato in quanto
-entrambe riceveranno le notifiche, e gestire correttamente le notifiche
-multiple richiede molta attenzione. Se invece si cerca di inserire due volte
-lo stesso file descriptor nella stessa istanza di \textit{epoll} la funzione
-fallirà con un errore di \errval{EEXIST}. Tuttavia è possibile inserire nella
-stessa istanza file descriptor duplicati (si ricordi quanto visto in
-sez.~\ref{sec:file_dup}), una tecnica che può essere usata per registrarli con
-un valore diverso per \param{events} e classificare così diversi tipi di
-eventi.
-
-Si tenga presente che quando si chiude un file descriptor questo, se era stato
-posto sotto osservazione da una istanza di \textit{epoll}, viene rimosso
-automaticamente solo nel caso esso sia l'unico riferimento al file aperto
-sottostante (più precisamente alla struttura \kstruct{file}, si ricordi
-fig.~\ref{fig:file_dup}) e non è necessario usare
-\const{EPOLL\_CTL\_DEL}. Questo non avviene qualora esso sia stato duplicato
-(perché la suddetta struttura non viene disallocata) e si potranno ricevere
-eventi ad esso relativi anche dopo che lo si è chiuso; per evitare
-l'inconveniente è necessario rimuoverlo esplicitamente con
-\const{EPOLL\_CTL\_DEL}.
-
-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 \val{NULL} ma se si
- vuole mantenere la compatibilità con le versioni precedenti occorre usare un
- puntatore valido.}
-
-\begin{figure}[!htb]
- \footnotesize \centering
- \begin{minipage}[c]{0.90\textwidth}
- \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}. Nella prima parte della tabella si sono indicate
-le costanti che permettono di indicare il tipo di evento, che sono le
-equivalenti delle analoghe di tab.~\ref{tab:file_pollfd_flags} per
-\func{poll}. Queste sono anche quelle riportate nella struttura
-\struct{epoll\_event} restituita da \func{epoll\_wait} per indicare il tipo di
-evento presentatosi, insieme a quelle della seconda parte della tabella, che
-vengono comunque riportate anche se non le si sono impostate con
-\func{epoll\_ctl}. La terza parte della tabella contiene le costanti che
-modificano le modalità di notifica.
-
-\begin{table}[htb]
- \centering
- \footnotesize
- \begin{tabular}[c]{|l|p{10cm}|}
- \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}).\footnotemark\\
- \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.\\
- \hline
- \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; questa
- condizione viene comunque riportata in uscita, e non
- è necessaria impostarla in ingresso.\\
- \hline
- \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 (questa modalità è disponibile
- solo a partire dal kernel 2.6.2).\\
- \const{EPOLLWAKEUP} & Attiva la prevenzione della sospensione del sistema
- se il file descriptor che si è marcato con esso
- diventa pronto (aggiunto a partire dal kernel 3.5),
- può essere impostato solo dall'amministratore (o da
- un processo con la capacità
- \const{CAP\_BLOCK\_SUSPEND}).\\
- \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.17,
- ed è utile per riconoscere la chiusura di una connessione dall'altro capo di
- un socket quando si lavora in modalità \textit{edge triggered}.}
-
-Il secondo campo, \var{data}, è una \direct{union} che serve a identificare il
-file descriptor a cui si intende fare riferimento, ed in astratto può
-contenere un valore qualsiasi (specificabile in diverse forme) che ne permetta
-una indicazione univoca. Il modo più comune di usarlo però è quello in cui si
-specifica il terzo argomento di \func{epoll\_ctl} nella forma
-\var{event.data.fd}, assegnando come valore di questo campo lo stesso valore
-dell'argomento \param{fd}, cosa che permette una immediata identificazione del
-file descriptor.
-
-% TODO verificare se prima o poi epoll_ctlv verrà introdotta
-
-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}.
-
-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 (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 (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 (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 di sistema che consente di attendere
-l'occorrenza di uno di tali eventi è \funcd{epoll\_wait}, il cui prototipo è:
-
-\begin{funcproto}{
-\fhead{sys/epoll.h}
-\fdecl{int epoll\_wait(int epfd, struct epoll\_event * events, int maxevents,
- int timeout)}
-
-\fdesc{Attende che uno dei file descriptor osservati sia pronto.}
-}
-
-{La funzione ritorna il numero di file descriptor pronti in caso di successo e
- $-1$ per un 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{funcproto}
-
-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 (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, 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} avviene solo quando il file descriptor ha
-cambiato stato diventando pronto. Esso non sarà riportato nuovamente fino ad
-un altro cambiamento di stato, per cui occorre assicurarsi di aver
-completamente esaurito 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}, (è 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.
-
-Si tenga presente che in modalità \textit{edge triggered}, dovendo esaurire le
-attività di I/O dei file descriptor risultati pronti per poter essere
-rinotificati, la gestione elementare per cui li si trattano uno per uno in
-sequenza può portare ad un effetto denominato \textit{starvation}
-(``\textsl{carestia}''). Si rischia cioè di concentrare le operazioni sul
-primo file descriptor che dispone di molti dati, prolungandole per tempi molto
-lunghi con un ritardo che può risultare eccessivo nei confronti di quelle da
-eseguire sugli altri che verrebbero dopo. Per evitare questo tipo di
-problematiche viene consigliato di usare \func{epoll\_wait} per registrare un
-elenco dei file descriptor da gestire, e di trattarli a turno in maniera più
-equa.
-
-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. Valgono le osservazioni fatte in
-sez.~\ref{sec:file_select}, e per poterlo fare di nuovo è necessaria una
-variante della funzione di attesa che consenta di reimpostare all'uscita una
-\index{maschera~dei~segnali} maschera di segnali, analoga alle estensioni
-\func{pselect} e \func{ppoll} che abbiamo visto in precedenza per
-\func{select} e \func{poll}. In questo caso la funzione di sistema si chiama
-\funcd{epoll\_pwait}\footnote{la funzione è 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{funcproto}{
-\fhead{sys/epoll.h}
-\fdecl{int epoll\_pwait(int epfd, struct epoll\_event * events, int maxevents,
- int timeout, \\
-\phantom{int epoll\_pwait(}const sigset\_t *sigmask)}
-
-\fdesc{Attende che uno dei file descriptor osservati sia pronto, mascherando
- i segnali.} }
-
-{La funzione ritorna il numero di file descriptor pronti in caso di successo e
- $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei valori già
- visti con \funcd{epoll\_wait}.
-
-}
-\end{funcproto}
-
-La funzione è del tutto analoga \funcd{epoll\_wait}, soltanto che alla sua
-uscita viene ripristinata la \index{maschera~dei~segnali} 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'\textit{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'\textit{I/O multiplexing} appena
-illustrate.
-
-In questo tipo di interfacce infatti ci si aspetta che il processo gestisca
-gli eventi a cui deve reagire 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}. In sostanza se non ci
-fossero i segnali non ci sarebbe da preoccuparsi, fintanto che si effettuano
-operazioni all'interno di un processo, della non atomicità delle
-\index{system~call~lente} \textit{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'\textit{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 per la 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. Ovviamente si tratta di una funzionalità specifica
-di Linux, non presente in altri sistemi unix-like, e non prevista da nessuno
-standard, per cui va evitata se si ha a cuore la portabilità.
-
-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'\textit{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 di sistema che permette di abilitare la ricezione dei segnali
-tramite file descriptor è \funcd{signalfd},\footnote{in realtà quella
- riportata è l'interfaccia alla funzione fornita dalle \acr{glibc}, esistono
- infatti due versioni diverse della \textit{system call}; una prima versione,
- \func{signalfd}, introdotta nel kernel 2.6.22 e disponibile con le
- \acr{glibc} 2.8 che non supporta l'argomento \texttt{flags}, ed una seconda
- versione, \funcm{signalfd4}, introdotta con il kernel 2.6.27 e che è quella
- che viene sempre usata a partire dalle \acr{glibc} 2.9, che prende un
- argomento aggiuntivo \code{size\_t sizemask} che indica la dimensione della
- \index{maschera~dei~segnali} maschera dei segnali, il cui valore viene
- impostato automaticamente dalle \acr{glibc}.} il cui prototipo è:
-
-\begin{funcproto}{
-\fhead{sys/signalfd.h}
-\fdecl{int signalfd(int fd, const sigset\_t *mask, int flags)}
-
-\fdesc{Crea o modifica un file descriptor per la ricezione dei segnali.}
-}
-
-{La funzione ritorna un numero di file descriptor in caso di successo e $-1$
- per un 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{ENODEV}] il kernel non può montare internamente il
- dispositivo per la gestione anonima degli \itindex{inode} \textit{inode}
- associati al file descriptor.
- \item[\errcode{ENOMEM}] non c'è memoria sufficiente per creare un nuovo file
- descriptor di \func{signalfd}.
- \end{errlist}
- ed inoltre \errval{EMFILE} e \errval{ENFILE} nel loro significato generico.
-
-}
-\end{funcproto}
-
-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 ex-novo uno di questi 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 valore
-del nuovo file descriptor e nel secondo caso il valore indicato
-con \param{fd}, in caso di errore invece verrà 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 \index{maschera~dei~segnali} maschera di segnali creata con
-l'uso delle apposite macro già illustrate in sez.~\ref{sec:sig_sigset}. La
-maschera deve indicare su quali segnali si intende operare con
-\func{signalfd}; l'elenco può essere modificato con una successiva chiamata a
-\func{signalfd}. Dato che \signal{SIGKILL} e \signal{SIGSTOP} non possono
-essere intercettati (e non prevedono neanche la possibilità di un gestore) un
-loro inserimento nella maschera verrà ignorato senza generare errori.
-
-L'argomento \param{flags} consente di impostare direttamente in fase di
-creazione due flag per il file descriptor analoghi a quelli che si possono
-impostare con una creazione ordinaria con \func{open}, evitando una
-impostazione successiva con \func{fcntl} (si ricordi che questo è un argomento
-aggiuntivo, introdotto con la versione fornita a partire dal kernel 2.6.27,
-per kernel precedenti il valore deve essere nullo). L'argomento deve essere
-specificato come maschera binaria dei valori riportati in
-tab.~\ref{tab:signalfd_flags}.
-
-\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 i flag del file descriptor.}
- \label{tab:signalfd_flags}
-\end{table}
-
-Si tenga presente che la chiamata a \func{signalfd} non disabilita la gestione
-ordinaria dei segnali indicati da \param{mask}; questa, se si vuole effettuare
-la ricezione tramite il file descriptor, dovrà essere disabilitata
-esplicitamente bloccando gli stessi segnali con \func{sigprocmask}, altrimenti
-verranno comunque eseguite le azioni di default (o un eventuale gestore
-installato in precedenza). Il blocco non ha invece nessun effetto sul file
-descriptor restituito da \func{signalfd}, dal quale sarà possibile pertanto
-ricevere qualunque segnale, anche se questo risultasse bloccato.
-
-Si tenga presente inoltre che la lettura di una struttura
-\struct{signalfd\_siginfo} relativa ad un segnale pendente è equivalente alla
-esecuzione di un gestore, vale a dire che una volta letta il segnale non sarà
-più pendente e non potrà essere ricevuto, qualora si ripristino le normali
-condizioni di gestione, né da un gestore, né dalla funzione \func{sigwaitinfo}.
-
-Come anticipato, essendo questo lo scopo principale della nuova interfaccia,
-il file descriptor può essere tenuto sotto osservazione tramite le funzioni
-dell'\textit{I/O multiplexing} (vale a dire con le solite \func{select},
-\func{poll} e \funcd{epoll\_wait}), e risulterà accessibile in lettura quando
-uno o più dei segnali indicati tramite \param{mask} sarà pendente.
-
-La funzione può essere chiamata più volte dallo stesso processo, consentendo
-così di tenere sotto osservazione segnali diversi tramite file descriptor
-diversi. Inoltre è anche possibile tenere sotto osservazione lo stesso segnale
-con più file descriptor, anche se la pratica è sconsigliata; in tal caso la
-ricezione del segnale potrà essere effettuata con una lettura da uno qualunque
-dei file descriptor a cui è associato, ma questa potrà essere eseguita
-soltanto una volta. Questo significa che tutti i file descriptor su cui è
-presente lo stesso segnale risulteranno pronti in lettura per le funzioni di
-\textit{I/O multiplexing}, ma una volta eseguita la lettura su uno di essi il
-segnale sarà considerato ricevuto ed i relativi dati non saranno più
-disponibili sugli altri file descriptor, che (a meno di una ulteriore
-occorrenza del segnale nel frattempo) di non saranno più pronti.
-
-Quando il file descriptor per la ricezione dei segnali non serve più potrà
-essere chiuso con \func{close} liberando tutte le risorse da esso allocate. In
-tal caso qualora vi fossero segnali pendenti questi resteranno tali, e
-potranno essere ricevuti normalmente una volta che si rimuova il blocco
-imposto con \func{sigprocmask}.
-
-Oltre che con le funzioni dell'\textit{I/O multiplexing} l'uso del file
-descriptor restituito da \func{signalfd} cerca di seguire la semantica di un
-sistema unix-like anche con altre \textit{system call}; in particolare esso
-resta aperto (come ogni altro file descriptor) attraverso una chiamata ad
-\func{exec}, a meno che non lo si sia creato con il flag di
-\const{SFD\_CLOEXEC} o si sia successivamente impostato il
-\textit{close-on-exec} con \func{fcntl}. Questo comportamento corrisponde
-anche alla ordinaria semantica relativa ai segnali bloccati, che restano
-pendenti attraverso una \func{exec}.
-
-Analogamente il file descriptor resta sempre disponibile attraverso una
-\func{fork} per il processo figlio, che ne riceve una copia; in tal caso però
-il figlio potrà leggere dallo stesso soltanto i dati relativi ai segnali
-ricevuti da lui stesso. Nel caso di \textit{thread} viene nuovamente seguita
-la semantica ordinaria dei segnali, che prevede che un singolo \textit{thread}
-possa ricevere dal file descriptor solo le notifiche di segnali inviati
-direttamente a lui o al processo in generale, e non quelli relativi ad altri
-\textit{thread} appartenenti allo stesso processo.
-
-L'interfaccia fornita da \func{signalfd} prevede che la ricezione dei segnali
-sia eseguita leggendo i dati relativi ai segnali pendenti dal file descriptor
-restituito dalla funzione con una normalissima \func{read}. Qualora non vi
-siano segnali pendenti la \func{read} si bloccherà a meno di non aver
-impostato la modalità di I/O non bloccante sul file descriptor, o direttamente
-in fase di creazione con il flag \const{SFD\_NONBLOCK}, o in un momento
-successivo con \func{fcntl}.
-
-\begin{figure}[!htb]
- \footnotesize \centering
- \begin{minipage}[c]{0.90\textwidth}
- \includestruct{listati/signalfd_siginfo.h}
- \end{minipage}
- \normalsize
- \caption{La struttura \structd{signalfd\_siginfo}, restituita in lettura da
- un file descriptor creato con \func{signalfd}.}
- \label{fig:signalfd_siginfo}
-\end{figure}
-
-I dati letti dal file descriptor vengono scritti sul buffer indicato come
-secondo argomento di \func{read} nella forma di una sequenza di una o più
-strutture \struct{signalfd\_siginfo} (la cui definizione si è riportata in
-fig.~\ref{fig:signalfd_siginfo}) a seconda sia della dimensione del buffer che
-del numero di segnali pendenti. Per questo motivo il buffer deve essere almeno
-di dimensione pari a quella di \struct{signalfd\_siginfo}, qualora sia di
-dimensione maggiore potranno essere letti in unica soluzione i dati relativi
-ad eventuali più segnali pendenti, fino al numero massimo di strutture
-\struct{signalfd\_siginfo} che possono rientrare nel buffer.
-
-\begin{figure}[!htb]
- \footnotesize \centering
- \begin{minipage}[c]{\codesamplewidth}
- \includecodesample{listati/FifoReporter-init.c}
- \end{minipage}
- \normalsize
- \caption{Sezione di inizializzazione del codice del programma
- \file{FifoReporter.c}.}
- \label{fig:fiforeporter_code_init}
-\end{figure}
-
-Il contenuto di \struct{signalfd\_siginfo} ricalca da vicino quella della
-analoga struttura \struct{siginfo\_t} (illustrata in
-fig.~\ref{fig:sig_siginfo_t}) usata dall'interfaccia ordinaria dei segnali, e
-restituisce dati simili. Come per \struct{siginfo\_t} i campi che vengono
-avvalorati dipendono dal tipo di segnale e ricalcano i valori che abbiamo già
-illustrato in sez.~\ref{sec:sig_sigaction}.\footnote{si tenga presente però
- che per un bug i kernel fino al 2.6.25 non avvalorano correttamente i campi
- \var{ssi\_ptr} e \var{ssi\_int} per segnali inviati con \func{sigqueue}.}
-
-Come esempio di questa nuova interfaccia ed anche come esempio di applicazione
-della interfaccia di \itindex{epoll} \textit{epoll}, si è scritto un programma
-elementare che stampi sullo standard output sia quanto viene scritto da terzi
-su una \textit{named fifo}, che l'avvenuta ricezione di alcuni segnali. Il
-codice completo si trova al solito nei sorgenti allegati alla guida (nel file
-\texttt{FifoReporter.c}).
-
-In fig.~\ref{fig:fiforeporter_code_init} si è riportata la parte iniziale del
-programma in cui vengono effettuate le varie inizializzazioni necessarie per
-l'uso di \itindex{epoll} \textit{epoll} e \func{signalfd}, a partire
-(\texttt{\small 12-16}) dalla definizione delle varie variabili e strutture
-necessarie. Al solito si è tralasciata la parte dedicata alla decodifica delle
-opzioni che consentono ad esempio di cambiare il nome del file associato alla
-\textit{fifo}.
-
-Il primo passo (\texttt{\small 19-20}) è la creazione di un file descriptor
-\texttt{epfd} di \itindex{epoll} \textit{epoll} con \func{epoll\_create} che è
-quello che useremo per il controllo degli altri. É poi necessario
-disabilitare la ricezione dei segnali (nel caso \signal{SIGINT},
-\signal{SIGQUIT} e \signal{SIGTERM}) per i quali si vuole la notifica tramite
-file descriptor. Per questo prima li si inseriscono (\texttt{\small 22-25})
-in una \index{maschera~dei~segnali} maschera di segnali \texttt{sigmask} che
-useremo con (\texttt{\small 26}) \func{sigprocmask} per disabilitarli. Con la
-stessa maschera si potrà per passare all'uso (\texttt{\small 28-29}) di
-\func{signalfd} per abilitare la notifica sul file descriptor
-\var{sigfd}. Questo poi (\texttt{\small 30-33}) dovrà essere aggiunto con
-\func{epoll\_ctl} all'elenco di file descriptor controllati con \texttt{epfd}.
-
-Occorrerà infine (\texttt{\small 35-38}) creare la \textit{named fifo} se
-questa non esiste ed aprirla per la lettura (\texttt{\small 39-40}); una
-volta fatto questo sarà necessario aggiungere il relativo file descriptor
-(\var{fifofd}) a quelli osservati da \itindex{epoll} \textit{epoll} in maniera
-del tutto analoga a quanto fatto con quello relativo alla notifica dei
-segnali.
-
-\begin{figure}[!htb]
- \footnotesize \centering
- \begin{minipage}[c]{\codesamplewidth}
- \includecodesample{listati/FifoReporter-main.c}
- \end{minipage}
- \normalsize
- \caption{Ciclo principale del codice del programma \file{FifoReporter.c}.}
- \label{fig:fiforeporter_code_body}
-\end{figure}
-
-Una volta completata l'inizializzazione verrà eseguito indefinitamente il
-ciclo principale del programma (\texttt{\small 2-45}) che si è riportato in
-fig.~\ref{fig:fiforeporter_code_body}, fintanto che questo non riceva un
-segnale di \signal{SIGINT} (ad esempio con la pressione di \texttt{C-c}). Il
-ciclo prevede che si attenda (\texttt{\small 2-3}) la presenza di un file
-descriptor pronto in lettura con \func{epoll\_wait} (si ricordi che entrambi i
-file descriptor \var{fifofd} e \var{sigfd} sono stati posti in osservazioni
-per eventi di tipo \const{EPOLLIN}) che si bloccherà fintanto che non siano
-stati scritti dati sulla \textit{fifo} o che non sia arrivato un
-segnale.\footnote{per semplificare il codice non si è trattato il caso in cui
- \func{epoll\_wait} viene interrotta da un segnale, assumendo che tutti
- quelli che possano interessare siano stati predisposti per la notifica
- tramite file descriptor, per gli altri si otterrà semplicemente l'uscita dal
- programma.}
-
-Anche se in questo caso i file descriptor pronti possono essere al più due, si
-è comunque adottato un approccio generico in cui questi verranno letti
-all'interno di un opportuno ciclo (\texttt{\small 5-44}) sul numero
-restituito da \func{epoll\_wait}, esaminando i risultati presenti nel vettore
-\var{events} all'interno di una catena di condizionali alternativi sul valore
-del file descriptor riconosciuto come pronto, controllando cioè a quale dei
-due file descriptor possibili corrisponde il campo relativo,
-\var{events[i].data.fd}.
-
-Il primo condizionale (\texttt{\small 6-24}) è relativo al caso che si sia
-ricevuto un segnale e che il file descriptor pronto corrisponda
-(\texttt{\small 6}) a \var{sigfd}. Dato che in generale si possono ricevere
-anche notifiche relativi a più di un singolo segnale, si è scelto di leggere
-una struttura \struct{signalfd\_siginfo} alla volta, eseguendo la lettura
-all'interno di un ciclo (\texttt{\small 8-24}) che prosegue fintanto che vi
-siano dati da leggere.
-
-Per questo ad ogni lettura si esamina (\texttt{\small 9-14}) se il valore di
-ritorno della funzione \func{read} è negativo, uscendo dal programma
-(\texttt{\small 11}) in caso di errore reale, o terminando il ciclo
-(\texttt{\small 13}) con un \texttt{break} qualora si ottenga un errore di
-\errcode{EAGAIN} per via dell'esaurimento dei dati. Si ricordi infatti come
-sia la \textit{fifo} che il file descriptor per i segnali siano stati aperti in
-modalità non-bloccante, come previsto per l’\textit{I/O multiplexing},
-pertanto ci si aspetta di ricevere un errore di \errcode{EAGAIN} quando non vi
-saranno più dati da leggere.
-
-In presenza di dati invece il programma proseguirà l'esecuzione stampando
-(\texttt{\small 19-20}) il nome del segnale ottenuto all'interno della
-struttura \struct{signalfd\_siginfo} letta in \var{siginf} ed il \textit{pid}
-del processo da cui lo ha ricevuto;\footnote{per la stampa si è usato il
- vettore \var{sig\_names} a ciascun elemento del quale corrisponde il nome
- del segnale avente il numero corrispondente, la cui definizione si è omessa
- dal codice di fig.~\ref{fig:fiforeporter_code_init} per brevità.} inoltre
-(\texttt{\small 21-24}) si controllerà anche se il segnale ricevuto è
-\signal{SIGINT}, che si è preso come segnale da utilizzare per la terminazione
-del programma, che verrà eseguita dopo aver rimosso il file della \textit{name
- fifo}.
-
-Il secondo condizionale (\texttt{\small 26-39}) è invece relativo al caso in
-cui ci siano dati pronti in lettura sulla \textit{fifo} e che il file
-descriptor pronto corrisponda (\texttt{\small 26}) a \var{fifofd}. Di nuovo si
-effettueranno le letture in un ciclo (\texttt{\small 28-39}) ripetendole fin
-tanto che la funzione \func{read} non restituisce un errore di
-\errcode{EAGAIN} (\texttt{\small 29-35}). Il procedimento è lo stesso adottato
-per il file descriptor associato al segnale, in cui si esce dal programma in
-caso di errore reale, in questo caso però alla fine dei dati prima di uscire
-si stampa anche (\texttt{\small 32}) un messaggio di chiusura.
-
-Se invece vi sono dati validi letti dalla \textit{fifo} si inserirà
-(\texttt{\small 36}) una terminazione di stringa sul buffer e si stamperà il
-tutto (\texttt{\small 37-38}) sullo \textit{standard output}. L'ultimo
-condizionale (\texttt{\small 40-44}) è semplicemente una condizione di cattura
-per una eventualità che comunque non dovrebbe mai verificarsi, e che porta
-alla uscita dal programma con una opportuna segnalazione di errore.
-
-A questo punto si potrà eseguire il comando lanciandolo su un terminale, ed
-osservarne le reazioni agli eventi generati da un altro terminale; lanciando
-il programma otterremo qualcosa del tipo:
-\begin{Console}
-piccardi@hain:~/gapil/sources$ \textbf{./a.out}
-FifoReporter starting, pid 4568
-\end{Console}
-%$
-e scrivendo qualcosa sull'altro terminale con:
-\begin{Console}
-root@hain:~# \textbf{echo prova > /tmp/reporter.fifo}
-\end{Console}
-si otterrà:
-\begin{Console}
-Message from fifo:
-prova
-end message
-\end{Console}
-mentre inviando un segnale:
-\begin{Console}
-root@hain:~# \textbf{kill 4568}
-\end{Console}
-si avrà:
-\begin{Console}
-Signal received:
-Got SIGTERM
-From pid 3361
-\end{Console}
-ed infine premendo \texttt{C-\bslash} sul terminale in cui è in esecuzione si
-vedrà:
-\begin{Console}
-^\\Signal received:
-Got SIGQUIT
-From pid 0
-\end{Console}
-e si potrà far uscire il programma con \texttt{C-c} ottenendo:
-\begin{Console}
-^CSignal received:
-Got SIGINT
-From pid 0
-SIGINT means exit
-\end{Console}
-
-Lo stesso paradigma di notifica tramite file descriptor usato per i segnali è
-stato adottato anche per i timer. In questo caso, rispetto a quanto visto in
-sez.~\ref{sec:sig_timer_adv}, la scadenza di un timer potrà essere letta da un
-file descriptor senza dover ricorrere ad altri meccanismi di notifica come un
-segnale o un \textit{thread}. Di nuovo questo ha il vantaggio di poter
-utilizzare le funzioni dell'\textit{I/O multiplexing} per attendere allo
-stesso tempo la disponibilità di dati o la ricezione della scadenza di un
-timer. In realtà per questo sarebbe già sufficiente \func{signalfd} per
-ricevere i segnali associati ai timer, ma la nuova interfaccia semplifica
-notevolmente la gestione e consente di fare tutto con una sola \textit{system
- call}.
-
-Le funzioni di questa nuova interfaccia ricalcano da vicino la struttura delle
-analoghe versioni ordinarie introdotte con lo standard POSIX.1-2001, che
-abbiamo già illustrato in sez.~\ref{sec:sig_timer_adv}.\footnote{questa
- interfaccia è stata introdotta in forma considerata difettosa con il kernel
- 2.6.22, per cui è stata immediatamente tolta nel successivo 2.6.23 e
- reintrodotta in una forma considerata adeguata nel kernel 2.6.25, il
- supporto nelle \acr{glibc} è stato introdotto a partire dalla versione
- 2.8.6, la versione del kernel 2.6.22, presente solo su questo kernel, non è
- supportata e non deve essere usata.} La prima funzione di sistema prevista,
-quella che consente di creare un timer, è \funcd{timerfd\_create}, il cui
-prototipo è:
-
-\begin{funcproto}{
-\fhead{sys/timerfd.h}
-\fdecl{int timerfd\_create(int clockid, int flags)}
-
-\fdesc{Crea un timer associato ad un file descriptor di notifica.}
-}
-
-{La funzione ritorna un numero di file descriptor in caso di successo e $-1$
- per un errore, nel qual caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EINVAL}] l'argomento \param{clockid} non è
- \const{CLOCK\_MONOTONIC} o \const{CLOCK\_REALTIME}, o
- l'argomento \param{flag} non è valido, o è diverso da zero per kernel
- precedenti il 2.6.27.
- \item[\errcode{ENODEV}] il kernel non può montare internamente il
- dispositivo per la gestione anonima degli \itindex{inode} \textit{inode}
- associati al file descriptor.
- \item[\errcode{ENOMEM}] non c'è memoria sufficiente per creare un nuovo file
- descriptor di \func{signalfd}.
- \end{errlist}
- ed inoltre \errval{EMFILE} e \errval{ENFILE} nel loro significato generico.
-}
-\end{funcproto}
-
-La funzione prende come primo argomento un intero che indica il tipo di
-orologio a cui il timer deve fare riferimento, i valori sono gli stessi delle
-funzioni dello standard POSIX-1.2001 già illustrati in
-tab.~\ref{tab:sig_timer_clockid_types}, ma al momento i soli utilizzabili sono
-\const{CLOCK\_REALTIME} e \const{CLOCK\_MONOTONIC}. L'argomento \param{flags},
-come l'analogo di \func{signalfd}, consente di impostare i flag per l'I/O non
-bloccante ed il \textit{close-on-exec} sul file descriptor
-restituito,\footnote{il flag è stato introdotto a partire dal kernel 2.6.27,
- per le versioni precedenti deve essere passato un valore nullo.} e deve
-essere specificato come una maschera binaria delle costanti riportate in
-tab.~\ref{tab:timerfd_flags}.
-
-\begin{table}[htb]
- \centering
- \footnotesize
- \begin{tabular}[c]{|l|p{8cm}|}
- \hline
- \textbf{Valore} & \textbf{Significato} \\
- \hline
- \hline
- \const{TFD\_NONBLOCK}& imposta sul file descriptor il flag di
- \const{O\_NONBLOCK} per renderlo non bloccante.\\
- \const{TFD\_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{timerfd\_create} che consentono di impostare i flag del file
- descriptor.}
- \label{tab:timerfd_flags}
-\end{table}
-
-In caso di successo la funzione restituisce un file descriptor sul quale
-verranno notificate le scadenze dei timer. Come per quelli restituiti da
-\func{signalfd} anche questo file descriptor segue la semantica dei sistemi
-unix-like, in particolare resta aperto attraverso una \func{exec} (a meno che
-non si sia impostato il flag di \textit{close-on exec} con
-\const{TFD\_CLOEXEC}) e viene duplicato attraverso una \func{fork}; questa
-ultima caratteristica comporta però che anche il figlio può utilizzare i dati
-di un timer creato nel padre, a differenza di quanto avviene invece con i
-timer impostati con le funzioni ordinarie. Si ricordi infatti che, come
-illustrato in sez.~\ref{sec:proc_fork}, allarmi, timer e segnali pendenti nel
-padre vengono cancellati per il figlio dopo una \func{fork}.
-
-Una volta creato il timer con \func{timerfd\_create} per poterlo utilizzare
-occorre \textsl{armarlo} impostandone un tempo di scadenza ed una eventuale
-periodicità di ripetizione, per farlo si usa una funzione di sistema omologa
-di \func{timer\_settime} per la nuova interfaccia; questa è
-\funcd{timerfd\_settime} ed il suo prototipo è:
-
-\begin{funcproto}{
-\fhead{sys/timerfd.h}
-\fdecl{int timerfd\_settime(int fd, int flags,
- const struct itimerspec *new\_value,\\
-\phantom{int timerfd\_settime(}struct itimerspec *old\_value)}
-
-\fdesc{Arma un timer associato ad un file descriptor di notifica.}
-}
-
-{La funzione ritorna un numero di file descriptor in caso di successo e $-1$
- per un errore, nel qual caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EBADF}] l'argomento \param{fd} non corrisponde ad un file
- descriptor.
- \item[\errcode{EFAULT}] o \param{new\_value} o \param{old\_value} non sono
- puntatori validi.
- \item[\errcode{EINVAL}] il file descriptor \param{fd} non è stato ottenuto
- con \func{timerfd\_create}, o i valori di \param{flag} o dei campi
- \var{tv\_nsec} in \param{new\_value} non sono validi.
- \end{errlist}
-}
-\end{funcproto}
-
-In questo caso occorre indicare su quale timer si intende operare specificando
-come primo argomento il file descriptor ad esso associato, che deve essere
-stato ottenuto da una precedente chiamata a \func{timerfd\_create}. I restanti
-argomenti sono del tutto analoghi a quelli della omologa funzione
-\func{timer\_settime}, e prevedono l'uso di strutture \struct{itimerspec}
-(vedi fig.~\ref{fig:struct_itimerspec}) per le indicazioni di temporizzazione.
-
-I valori ed il significato di questi argomenti sono gli stessi che sono già
-stati illustrati in dettaglio in sez.~\ref{sec:sig_timer_adv} e non staremo a
-ripetere quanto detto in quell'occasione; per brevità si ricordi che
-con \param{new\_value.it\_value} si indica la prima scadenza del timer e
-con \param{new\_value.it\_interval} la sua periodicità. L'unica differenza
-riguarda l'argomento \param{flags} che serve sempre ad indicare se il tempo di
-scadenza del timer è da considerarsi relativo o assoluto rispetto al valore
-corrente dell'orologio associato al timer, ma che in questo caso ha come
-valori possibili rispettivamente soltanto $0$ e \const{TFD\_TIMER\_ABSTIME}
-(l'analogo di \const{TIMER\_ABSTIME}).
-
-L'ultima funzione di sistema prevista dalla nuova interfaccia è
-\funcd{timerfd\_gettime}, che è l'analoga di \func{timer\_gettime}, il suo
-prototipo è:
-
-\begin{funcproto}{
-\fhead{sys/timerfd.h}
-\fdecl{int timerfd\_gettime(int fd, struct itimerspec *curr\_value)}
-
-\fdesc{Legge l'impostazione di un timer associato ad un file descriptor di
- notifica.}
-}
-
-{La funzione ritorna un numero di file descriptor in caso di successo e $-1$
- per un errore, nel qual caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EBADF}] l'argomento \param{fd} non corrisponde ad un file
- descriptor.
- \item[\errcode{EINVAL}] il file descriptor \param{fd} non è stato ottenuto
- con \func{timerfd\_create}.
- \item[\errcode{EFAULT}] o \param{curr\_value} non è un puntatore valido.
- \end{errlist}
-}
-\end{funcproto}
-
-La funzione consente di rileggere le impostazioni del timer associato al file
-descriptor \param{fd} nella struttura \struct{itimerspec} puntata
-da \param{curr\_value}. Il campo \var{it\_value} riporta il tempo rimanente
-alla prossima scadenza del timer, che viene sempre espresso in forma relativa,
-anche se lo si è armato specificando \const{TFD\_TIMER\_ABSTIME}. Un valore
-nullo (di entrambi i campi di \var{it\_value}) indica invece che il timer non
-è stato ancora armato. Il campo \var{it\_interval} riporta la durata
-dell'intervallo di ripetizione del timer, ed un valore nullo (di entrambi i
-campi) indica che il timer è stato impostato per scadere una sola volta.
-
-Il timer creato con \func{timerfd\_create} notificherà la sua scadenza
-rendendo pronto per la lettura il file descriptor ad esso associato, che
-pertanto potrà essere messo sotto controllo con una qualunque delle varie
-funzioni dell'I/O multiplexing viste in precedenza. Una volta che il file
-descriptor risulta pronto sarà possibile leggere il numero di volte che il
-timer è scaduto con una ordinaria \func{read}.
-
-La funzione legge il valore in un dato di tipo \type{uint64\_t}, e necessita
-pertanto che le si passi un buffer di almeno 8 byte, fallendo con
-\errval{EINVAL} in caso contrario, in sostanza la lettura deve essere
-effettuata con una istruzione del tipo:
-\includecodesnip{listati/readtimerfd.c}
-
-Il valore viene restituito da \func{read} seguendo l'ordinamento dei bit
-(\textit{big-endian} o \textit{little-endian}) nativo della macchina in uso,
-ed indica il numero di volte che il timer è scaduto dall'ultima lettura
-eseguita con successo, o, se lo si legge per la prima volta, da quando lo si è
-impostato con \func{timerfd\_settime}. Se il timer non è scaduto la funzione
-si blocca fino alla prima scadenza, a meno di non aver creato il file
-descriptor in modalità non bloccante con \const{TFD\_NONBLOCK} o aver
-impostato la stessa con \func{fcntl}, nel qual caso fallisce con l'errore di
-\errval{EAGAIN}.
-
-
-% TODO trattare qui eventfd introdotto con il 2.6.22
-
-
-\section{L'accesso \textsl{asincrono} ai file}
-\label{sec:file_asyncronous_operation}
-
-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:signal_driven_io}
-
-\itindbeg{signal~driven~I/O}
-
-Abbiamo accennato in sez.~\ref{sec:file_open_close} che è definito un flag
-\const{O\_ASYNC}, che consentirebbe di aprire un file in modalità asincrona,
-anche se in realtà è opportuno 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_ioctl}).\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.} 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à il sistema
-genera un apposito segnale, \signal{SIGIO}, tutte le volte che diventa
-possibile leggere o scrivere dal file descriptor; 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, per
-\textit{fifo} e \textit{pipe}. Inoltre è possibile, come illustrato in
-sez.~\ref{sec:file_fcntl_ioctl}, 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 \textit{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
-\textit{real-time} (vedi sez.~\ref{sec:sig_real_time}) impostando
-esplicitamente con il comando \const{F\_SETSIG} di \func{fcntl} un segnale
-\textit{real-time} da inviare in caso di I/O asincrono (il segnale predefinito
-è \signal{SIGIO}). In questo caso il gestore, tutte le volte che riceverà
-\const{SI\_SIGIO} come valore del campo \var{si\_code} di \struct{siginfo\_t},
-troverà nel campo \var{si\_fd} il valore del file descriptor che ha generato
-il segnale. Si noti che il valore di\var{si\_code} resta \const{SI\_SIGIO}
-qualunque sia il segnale che si è associato all'I/O, in quanto indica che il
-segnale è stato generato a causa di attività di I/O.
-
-Un secondo vantaggio dell'uso dei segnali \textit{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
-\textit{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 \textit{real-time},
-invierà al suo posto un solo \signal{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 \textit{real-time}
-ad una dimensione identica al valore massimo del numero di file descriptor
-utilizzabili, vale a dire impostare il contenuto di
-\sysctlfile{kernel/rtsig-max} allo stesso valore del contenuto di
-\sysctlfile{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, 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 se il loro file di configurazione è stato modificato, perché possano
-rileggerlo e riconoscere le modifiche; in genere questo vien fatto inviandogli
-un segnale di \signal{SIGHUP} che, per una convenzione adottata dalla gran
-parte di detti programmi, causa la rilettura della configurazione.
-
-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_ioctl}), che divengono disponibili soltanto se si è
-definita la macro \macro{\_GNU\_SOURCE} prima di includere \headfile{fcntl.h}.
-
-\itindbeg{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 \signal{SIGIO}, ma questo segnale può essere
-modificato usando il comando \const{F\_SETSIG} di \func{fcntl} (anche in
-questo caso si può rispecificare lo stesso \signal{SIGIO}).
-
-Se si è fatto questo (ed in genere è opportuno farlo, come in precedenza, per
-utilizzare segnali \textit{real-time}) e se inoltre 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_ioctl} 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
-(\textit{pipe} e dispositivi sono quindi esclusi). Inoltre un processo non
-privilegiato può ottenere un \textit{lease} soltanto per un file appartenente
-ad un \ids{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.