-%% fileadv.tex
-%%
-%% Copyright (C) 2000-2007 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. In particolare tratteremo delle funzioni di input/output avanzato,
-che permettono una gestione più sofisticata dell'I/O su file, a partire da
-quelle che permettono di gestire l'accesso contemporaneo a più file, per
-concludere con la gestione dell'I/O mappato in memoria. Dedicheremo poi la
-fine del capitolo alle problematiche del \textit{file locking}.
-
-
-\section{L'\textit{I/O multiplexing}}
-\label{sec:file_multiplexing}
-
-Uno dei problemi che si presentano quando si deve operare contemporaneamente
-su molti file usando le funzioni illustrate in
-cap.~\ref{cha:file_unix_interface} e cap.~\ref{cha:files_std_interface} è che
-si può essere bloccati nelle operazioni su un file mentre un altro potrebbe
-essere disponibile. L'\textit{I/O multiplexing} nasce risposta a questo
-problema. In questa sezione forniremo una introduzione a questa problematica
-ed analizzeremo le varie funzioni usate per implementare questa modalità di
-I/O.
-
-
-\subsection{La problematica dell'\textit{I/O multiplexing}}
-\label{sec:file_noblocking}
-
-Abbiamo visto in sez.~\ref{sec:sig_gen_beha}, affrontando la suddivisione fra
-\textit{fast} e \textit{slow} system call,\index{system~call~lente} che in
-certi casi le funzioni di I/O possono bloccarsi indefinitamente.\footnote{si
- ricordi però che questo può accadere solo per le pipe, i socket ed alcuni
- file di dispositivo\index{file!di~dispositivo}; sui file normali le funzioni
- di lettura e scrittura ritornano sempre subito.} Ad esempio le operazioni
-di lettura possono bloccarsi quando non ci sono dati disponibili sul
-descrittore su cui si sta operando.
-
-Questo comportamento causa uno dei problemi più comuni che ci si trova ad
-affrontare nelle operazioni di I/O, che si verifica quando si deve operare con
-più file descriptor eseguendo funzioni che possono bloccarsi senza che sia
-possibile prevedere quando questo può avvenire (il caso più classico è quello
-di un server in attesa di dati in ingresso da vari client). Quello che può
-accadere è di restare bloccati nell'eseguire una operazione su un file
-descriptor che non è ``\textsl{pronto}'', quando ce ne potrebbe essere un
-altro disponibile. Questo comporta nel migliore dei casi una operazione
-ritardata inutilmente nell'attesa del completamento di quella bloccata, mentre
-nel peggiore dei casi (quando la conclusione della operazione bloccata dipende
-da quanto si otterrebbe dal file descriptor ``\textsl{disponibile}'') si
-potrebbe addirittura arrivare ad un \itindex{deadlock} \textit{deadlock}.
-
-Abbiamo già accennato in sez.~\ref{sec:file_open} che è possibile prevenire
-questo tipo di comportamento delle funzioni di I/O aprendo un file in
-\textsl{modalità non-bloccante}, attraverso l'uso del flag \const{O\_NONBLOCK}
-nella chiamata di \func{open}. In questo caso le funzioni di input/output
-eseguite sul file che si sarebbero bloccate, ritornano immediatamente,
-restituendo l'errore \errcode{EAGAIN}. L'utilizzo di questa modalità di I/O
-permette di risolvere il problema controllando a turno i vari file descriptor,
-in un ciclo in cui si ripete l'accesso fintanto che esso non viene garantito.
-Ovviamente questa tecnica, detta \itindex{polling} \textit{polling}, è
-estremamente inefficiente: si tiene costantemente impiegata la CPU solo per
-eseguire in continuazione delle system call che nella gran parte dei casi
-falliranno.
-
-Per superare questo problema è stato introdotto il concetto di \textit{I/O
- multiplexing}, una nuova modalità di operazioni che consenta di tenere sotto
-controllo più file descriptor in contemporanea, permettendo di bloccare un
-processo quando le operazioni volute non sono possibili, e di riprenderne
-l'esecuzione una volta che almeno una di quelle richieste sia disponibile, 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 in sez.~\ref{sec:TCP_sock_multiplexing}.
-
-
-\subsection{Le funzioni \func{select} e \func{pselect}}
-\label{sec:file_select}
-
-Il primo kernel unix-like ad introdurre una interfaccia per l'\textit{I/O
- multiplexing} è stato BSD,\footnote{la funzione \func{select} è apparsa in
- BSD4.2 e standardizzata in BSD4.4, ma è stata portata su tutti i sistemi che
- supportano i socket, compreso le varianti di System V.} con la funzione
-\funcd{select}, il cui prototipo è:
-\begin{functions}
- \headdecl{sys/time.h}
- \headdecl{sys/types.h}
- \headdecl{unistd.h}
- \funcdecl{int select(int n, fd\_set *readfds, fd\_set *writefds, fd\_set
- *exceptfds, struct timeval *timeout)}
-
- Attende che uno dei file descriptor degli insiemi specificati diventi
- attivo.
-
- \bodydesc{La funzione in caso di successo restituisce il numero di file
- descriptor (anche nullo) che sono attivi, e -1 in caso di errore, nel qual
- caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno
- degli insiemi.
- \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale.
- \item[\errcode{EINVAL}] Si è specificato per \param{n} un valore negativo o
- un valore non valido per \param{timeout}.
- \end{errlist}
- ed inoltre \errval{ENOMEM}.
-}
-\end{functions}
-
-La funzione mette il processo in stato di \textit{sleep} (vedi
-tab.~\ref{tab:proc_proc_states}) fintanto che almeno uno dei file descriptor
-degli insiemi specificati (\param{readfds}, \param{writefds} e
-\param{exceptfds}), non diventa attivo, per un tempo massimo specificato da
-\param{timeout}.
-
-\itindbeg{file~descriptor~set}
-
-Per specificare quali file descriptor si intende \textsl{selezionare}, la
-funzione usa un particolare oggetto, il \textit{file descriptor set},
-identificato dal tipo \type{fd\_set}, che serve ad identificare un insieme di
-file descriptor, in maniera analoga a come un \itindex{signal~set}
-\textit{signal set} (vedi sez.~\ref{sec:sig_sigset}) identifica un insieme di
-segnali. Per la manipolazione di questi \textit{file descriptor set} si
-possono usare delle opportune macro di preprocessore:
-\begin{functions}
- \headdecl{sys/time.h}
- \headdecl{sys/types.h}
- \headdecl{unistd.h}
- \funcdecl{void \macro{FD\_ZERO}(fd\_set *set)}
- Inizializza l'insieme (vuoto).
-
- \funcdecl{void \macro{FD\_SET}(int fd, fd\_set *set)}
- Inserisce il file descriptor \param{fd} nell'insieme.
-
- \funcdecl{void \macro{FD\_CLR}(int fd, fd\_set *set)}
- Rimuove il file descriptor \param{fd} dall'insieme.
-
- \funcdecl{int \macro{FD\_ISSET}(int fd, fd\_set *set)}
- Controlla se il file descriptor \param{fd} è nell'insieme.
-\end{functions}
-
-In genere un \textit{file descriptor set} può contenere fino ad un massimo di
-\const{FD\_SETSIZE} file descriptor. Questo valore in origine corrispondeva
-al limite per il numero massimo di file aperti\footnote{ad esempio in Linux,
- fino alla serie 2.0.x, c'era un limite di 256 file per processo.}, ma da
-quando, come nelle versioni più recenti del kernel, non c'è più un limite
-massimo, esso indica le dimensioni massime dei numeri usati nei \textit{file
- descriptor set}.\footnote{il suo valore, secondo lo standard POSIX
- 1003.1-2001, è definito in \file{sys/select.h}, ed è pari a 1024.} Si tenga
-presente che i \textit{file descriptor set} devono sempre essere inizializzati
-con \macro{FD\_ZERO}; passare a \func{select} un valore non inizializzato può
-dar luogo a comportamenti non prevedibili.
-
-La funzione richiede di specificare tre insiemi distinti di file descriptor;
-il primo, \param{readfds}, verrà osservato per rilevare la disponibilità di
-effettuare una lettura,\footnote{per essere precisi la funzione ritornerà in
- tutti i casi in cui la successiva esecuzione di \func{read} risulti non
- bloccante, quindi anche in caso di \textit{end-of-file}; inoltre con Linux
- possono verificarsi casi particolari, ad esempio quando arrivano dati su un
- socket dalla rete che poi risultano corrotti e vengono scartati, può
- accadere che \func{select} riporti il relativo file descriptor come
- leggibile, ma una successiva \func{read} si blocchi.} il secondo,
-\param{writefds}, per verificare la possibilità 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 numero massimo dei 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{n}, che
-deve corrispondere al valore massimo aumentato di uno.\footnote{i file
- descriptor infatti sono contati a partire da zero, ed il valore indica il
- numero di quelli da tenere sotto controllo; dimenticarsi di aumentare di uno
- il valore di \param{n} è un errore comune.} Infine l'argomento
-\param{timeout}, specifica un tempo massimo di attesa prima che la funzione
-ritorni; se impostato a \val{NULL} la funzione attende indefinitamente. Si può
-specificare anche un tempo nullo (cioè una struttura \struct{timeval} con i
-campi impostati a zero), qualora si voglia semplicemente controllare lo stato
-corrente dei file descriptor.
-
-La funzione restituisce il numero di file descriptor pronti,\footnote{questo è
- il comportamento previsto dallo standard, ma la standardizzazione della
- funzione è recente, ed esistono ancora alcune versioni di Unix che non si
- comportano in questo modo.} e ciascun insieme viene sovrascritto per
-indicare quali sono i file descriptor pronti per le operazioni ad esso
-relative, in modo da poterli controllare con \macro{FD\_ISSET}. Se invece si
-ha un timeout viene restituito un valore nullo e gli insiemi non vengono
-modificati. In caso di errore la funzione restituisce -1, ed i valori dei tre
-insiemi sono indefiniti e non si può fare nessun affidamento sul loro
-contenuto.
-
-\itindend{file~descriptor~set}
-
-In Linux \func{select} modifica anche il valore di \param{timeout},
-impostandolo al tempo restante in caso di interruzione prematura; questo è
-utile quando la funzione viene interrotta da un segnale, in tal caso infatti
-si ha un errore di \errcode{EINTR}, ed occorre rilanciare la funzione; in
-questo modo non è necessario ricalcolare tutte le volte il tempo
-rimanente.\footnote{questo può causare problemi di portabilità sia quando si
- trasporta 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 la
- caratteristica è disponibile nei sistemi che derivano da System V e non
- disponibile per quelli che derivano da BSD.}
-
-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{n} un valore 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
-anche molto ampio anche se i file descriptor sono solo poche unità; tutto ciò
-ha ovviamente delle conseguenze ampiamente negative per le prestazioni.
-
-Inoltre c'è anche il problema che il numero massimo dei file che si possono
-tenere sotto controllo, la funzione è nata quando il kernel consentiva un
-numero massimo di 1024 file descriptor per processo, adesso che il numero può
-essere arbitrario si viene a creare una dipendenza del tutto artificiale dalle
-dimensioni della struttura \type{fd\_set}, che può necessitare di essere
-estesa, con ulteriori perdite di prestazioni.
-
-Lo standard POSIX è rimasto a lungo senza primitive per l'\textit{I/O
- multiplexing}, introdotto solo con le ultime revisioni dello standard (POSIX
-1003.1g-2000 e POSIX 1003.1-2001). La scelta è stata quella di seguire
-l'interfaccia creata da BSD, ma prevede che tutte le funzioni ad esso relative
-vengano dichiarate nell'header \file{sys/select.h}, che sostituisce i
-precedenti, ed inoltre aggiunge a \func{select} una nuova funzione
-\funcd{pselect},\footnote{il supporto per lo standard POSIX 1003.1-2001, ed
- l'header \file{sys/select.h}, compaiono in Linux a partire dalle \acr{glibc}
- 2.1. Le \acr{libc4} e \acr{libc5} non contengono questo header, le
- \acr{glibc} 2.0 contengono una definizione sbagliata di \func{psignal},
- senza l'argomento \param{sigmask}, la definizione corretta è presente dalle
- \acr{glibc} 2.1-2.2.1 se si è definito \macro{\_GNU\_SOURCE} e nelle
- \acr{glibc} 2.2.2-2.2.4 se si è definito \macro{\_XOPEN\_SOURCE} con valore
- maggiore di 600.} il cui prototipo è:
-\begin{prototype}{sys/select.h}
- {int pselect(int n, fd\_set *readfds, fd\_set *writefds, fd\_set *exceptfds,
- struct timespec *timeout, sigset\_t *sigmask)}
-
- Attende che uno dei file descriptor degli insiemi specificati diventi
- attivo.
-
- \bodydesc{La funzione in caso di successo restituisce il numero di file
- descriptor (anche nullo) che sono attivi, e -1 in caso di errore, nel qual
- caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno
- degli insiemi.
- \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale.
- \item[\errcode{EINVAL}] Si è specificato per \param{n} un valore negativo o
- un valore non valido per \param{timeout}.
- \end{errlist}
- ed inoltre \errval{ENOMEM}.}
-\end{prototype}
-
-La funzione è sostanzialmente identica a \func{select}, solo che usa una
-struttura \struct{timespec} (vedi fig.~\ref{fig:sys_timeval_struct}) per
-indicare con maggiore precisione il timeout e non ne aggiorna il valore in
-caso di interruzione.\footnote{in realtà la system call di Linux aggiorna il
- valore al tempo rimanente, ma la funzione fornita dalle \acr{glibc} modifica
- questo comportamento passando alla system call una variabile locale, in modo
- da mantenere l'aderenza allo standard POSIX che richiede che il valore di
- \param{timeout} non sia modificato.} Inoltre prende un argomento aggiuntivo
-\param{sigmask} che è il puntatore ad una maschera di segnali (si veda
-sez.~\ref{sec:sig_sigmask}). La maschera corrente viene sostituita da questa
-immediatamente prima di eseguire l'attesa, e ripristinata al ritorno della
-funzione.
-
-L'uso di \param{sigmask} è stato introdotto allo scopo di prevenire possibili
-\textit{race condition} \itindex{race~condition} quando ci si deve porre in
-attesa sia di un segnale che di dati. La tecnica classica è quella di
-utilizzare il gestore per impostare una variabile globale e controllare questa
-nel corpo principale del programma; abbiamo visto in
-sez.~\ref{sec:sig_example} come questo lasci spazio a possibili race
-condition, per cui diventa essenziale utilizzare \func{sigprocmask} per
-disabilitare la ricezione del segnale prima di eseguire il controllo e
-riabilitarlo dopo l'esecuzione delle relative operazioni, onde evitare
-l'arrivo di un segnale immediatamente dopo il controllo, che andrebbe perso.
-
-Nel nostro caso il problema si pone quando oltre al segnale si devono tenere
-sotto controllo anche dei file descriptor con \func{select}, in questo caso si
-può fare conto sul fatto che all'arrivo di un segnale essa verrebbe interrotta
-e si potrebbero eseguire di conseguenza le operazioni relative al segnale e
-alla gestione dati con un ciclo del tipo:
-\includecodesnip{listati/select_race.c}
-qui però emerge una \itindex{race~condition} \textit{race condition}, perché
-se il segnale arriva prima della chiamata a \func{select}, questa non verrà
-interrotta, e la ricezione del segnale non sarà rilevata.
-
-Per questo è stata introdotta \func{pselect} che attraverso l'argomento
-\param{sigmask} permette di riabilitare la ricezione il segnale
-contestualmente all'esecuzione della funzione,\footnote{in Linux però, fino al
- kernel 2.6.16, non era presente la relativa system call, e la funzione era
- implementata nelle \acr{glibc} attraverso \func{select} (vedi \texttt{man
- select\_tut}) per cui la possibilità di \itindex{race~condition}
- \textit{race condition} permaneva; in tale situazione si può ricorrere ad una
- soluzione alternativa, chiamata \itindex{self-pipe trick} \textit{self-pipe
- trick}, che consiste nell'aprire una pipe (vedi sez.~\ref{sec:ipc_pipes})
- ed usare \func{select} sul capo in lettura della stessa; si può indicare
- l'arrivo di un segnale scrivendo sul capo in scrittura all'interno del
- gestore dello stesso; in questo modo anche se il segnale va perso prima
- della chiamata di \func{select} questa lo riconoscerà comunque dalla
- presenza di dati sulla pipe.} ribloccandolo non appena essa ritorna, così
-che il precedente codice potrebbe essere riscritto nel seguente modo:
-\includecodesnip{listati/pselect_norace.c}
-in questo caso utilizzando \var{oldmask} durante l'esecuzione di
-\func{pselect} la ricezione del segnale sarà abilitata, ed in caso di
-interruzione si potranno eseguire le relative operazioni.
-
-
-\subsection{Le funzioni \func{poll} e \func{ppoll}}
-\label{sec:file_poll}
-
-Nello sviluppo di System V, invece di utilizzare l'interfaccia di
-\func{select}, che è una estensione tipica di BSD, è stata introdotta un'altra
-interfaccia, basata sulla funzione \funcd{poll},\footnote{la funzione è
- prevista dallo standard XPG4, ed è stata introdotta in Linux come system
- call a partire dal kernel 2.1.23 ed inserita nelle \acr{libc} 5.4.28.} il
-cui prototipo è:
-\begin{prototype}{sys/poll.h}
- {int poll(struct pollfd *ufds, unsigned int nfds, int timeout)}
-
- La funzione attende un cambiamento di stato su un insieme di file
- descriptor.
-
- \bodydesc{La funzione restituisce il numero di file descriptor con attività
- in caso di successo, o 0 se c'è stato un timeout e -1 in caso di errore,
- ed in quest'ultimo caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno
- degli insiemi.
- \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale.
- \item[\errcode{EINVAL}] Il valore di \param{nfds} eccede il limite
- \macro{RLIMIT\_NOFILE}.
- \end{errlist}
- ed inoltre \errval{EFAULT} e \errval{ENOMEM}.}
-\end{prototype}
-
-La funzione permette di tenere sotto controllo contemporaneamente \param{ndfs}
-file descriptor, specificati attraverso il puntatore \param{ufds} ad un
-vettore di strutture \struct{pollfd}. Come con \func{select} si può
-interrompere l'attesa dopo un certo tempo, questo deve essere specificato con
-l'argomento \param{timeout} in numero di millisecondi: un valore negativo
-indica un'attesa indefinita, mentre un valore nullo comporta il ritorno
-immediato (e può essere utilizzato per impiegare \func{poll} in modalità
-\textsl{non-bloccante}).
-
-Per ciascun file da controllare deve essere inizializzata una struttura
-\struct{pollfd} nel vettore indicato dall'argomento \param{ufds}. La
-struttura, la cui definizione è riportata in fig.~\ref{fig:file_pollfd},
-prevede tre campi: in \var{fd} deve essere indicato il numero del file
-descriptor da controllare, in \var{events} deve essere specificata una
-maschera binaria di flag che indichino il tipo di evento che si vuole
-controllare, mentre in \var{revents} il kernel restituirà il relativo
-risultato. Usando un valore negativo per \param{fd} la corrispondente
-struttura sarà ignorata da \func{poll}. Dato che i dati in ingresso sono del
-tutto indipendenti da quelli in uscita (che vengono restituiti in
-\var{revents}) non è necessario reinizializzare tutte le volte il valore delle
-strutture \struct{pollfd} a meno di non voler cambiare qualche condizione.
-
-\begin{figure}[!htb]
- \footnotesize \centering
- \begin{minipage}[c]{15cm}
- \includestruct{listati/pollfd.h}
- \end{minipage}
- \normalsize
- \caption{La struttura \structd{pollfd}, utilizzata per specificare le
- modalità di controllo di un file descriptor alla funzione \func{poll}.}
- \label{fig:file_pollfd}
-\end{figure}
-
-Le costanti che definiscono i valori relativi ai bit usati nelle maschere
-binarie dei campi \var{events} e \var{revents} sono riportati in
-tab.~\ref{tab:file_pollfd_flags}, insieme al loro significato. Le si sono
-suddivise in tre gruppi, nel primo gruppo si sono indicati i bit utilizzati
-per controllare l'attività in ingresso, nel secondo quelli per l'attività in
-uscita, mentre il terzo gruppo contiene dei valori che vengono utilizzati solo
-nel campo \var{revents} per notificare delle condizioni di errore.
-
-\begin{table}[htb]
- \centering
- \footnotesize
- \begin{tabular}[c]{|l|l|}
- \hline
- \textbf{Flag} & \textbf{Significato} \\
- \hline
- \hline
- \const{POLLIN} & È possibile la lettura.\\
- \const{POLLRDNORM}& Sono disponibili in lettura dati normali.\\
- \const{POLLRDBAND}& Sono disponibili in lettura dati prioritari. \\
- \const{POLLPRI} & È possibile la lettura di \itindex{out-of-band} dati
- urgenti.\\
- \hline
- \const{POLLOUT} & È possibile la scrittura immediata.\\
- \const{POLLWRNORM}& È possibile la scrittura di dati normali. \\
- \const{POLLWRBAND}& È possibile la scrittura di dati prioritari. \\
- \hline
- \const{POLLERR} & C'è una condizione di errore.\\
- \const{POLLHUP} & Si è verificato un hung-up.\\
- \const{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}
-
-Il valore \const{POLLMSG} non viene utilizzato ed è definito solo per
-compatibilità con l'implementazione di SysV che usa gli
-\textit{stream};\footnote{essi sono una interfaccia specifica di SysV non
- presente in Linux, e non hanno nulla a che fare con i file \textit{stream}
- delle librerie standard del C.} è da questi che derivano i nomi di alcune
-costanti, in quanto per essi sono definite tre classi di dati:
-\textsl{normali}, \textit{prioritari} ed \textit{urgenti}. In Linux la
-distinzione ha senso solo per i dati urgenti \itindex{out-of-band} dei socket
-(vedi sez.~\ref{sec:TCP_urgent_data}), ma su questo e su come \func{poll}
-reagisce alle varie condizioni dei socket torneremo in
-sez.~\ref{sec:TCP_serv_poll}, dove vedremo anche un esempio del suo utilizzo.
-Si tenga conto comunque che le costanti relative ai diversi tipi di dati (come
-\const{POLLRDNORM} e \const{POLLRDBAND}) sono utilizzabili soltanto qualora si
-sia definita la macro \macro{\_XOPEN\_SOURCE}.\footnote{e ci si ricordi di
- farlo sempre in testa al file, definirla soltanto prima di includere
- \file{sys/poll.h} non è sufficiente.}
-
-In caso di successo funzione ritorna restituendo il numero di file (un valore
-positivo) per i quali si è verificata una delle condizioni di attesa richieste
-o per i quali si è verificato un errore (nel qual caso vengono utilizzati i
-valori di tab.~\ref{tab:file_pollfd_flags} esclusivi di \var{revents}). Un
-valore nullo indica che si è raggiunto il timeout, mentre un valore negativo
-indica un errore nella chiamata, il cui codice viene riportato al solito
-tramite \var{errno}.
-
-L'uso di \func{poll} consente di superare alcuni dei problemi illustrati in
-precedenza per \func{select}; anzitutto, dato che in questo caso si usa un
-vettore di strutture \struct{pollfd} di dimensione arbitraria, non esiste il
-limite introdotto dalle dimesioni massime di un \itindex{file~descriptor~set}
-\textit{file descriptor set} e la dimensione dei dati passati al kernel
-dipende solo dal numero dei file descriptor che si vogliono controllare, non
-dal loro valore.\footnote{anche se usando dei bit un \textit{file descriptor
- set} può essere più efficiente di un vettore di strutture \struct{pollfd},
- qualora si debba osservare un solo file descriptor con un valore molto alto
- ci si troverà ad utilizzare inutilmente un maggiore quantitativo di
- memoria.}
-
-Inoltre con \func{select} lo stesso \itindex{file~descriptor~set} \textit{file
- descriptor set} è usato sia in ingresso che in uscita, e questo significa
-che tutte le volte che si vuole ripetere l'operazione occorre reinizializzarlo
-da capo. Questa operazione, che può essere molto onerosa se i file descriptor
-da tenere sotto osservazione sono molti, non è invece necessaria con
-\func{poll}.
-
-Abbiamo visto in sez.~\ref{sec:file_select} come lo standard POSIX preveda una
-variante di \func{select} che consente di gestire correttamente la ricezione
-dei segnali nell'attesa su un file descriptor. Con l'introduzione di una
-implementazione reale di \func{pselect} nel kernel 2.6.16, è stata aggiunta
-anche una analoga funzione che svolga lo stesso ruolo per \func{poll}.
-
-In questo caso si tratta di una estensione che è specifica di Linux e non è
-prevista da nessuno standard; essa può essere utilizzata esclusivamente se si
-definisce la macro \macro{\_GNU\_SOURCE} ed ovviamente non deve essere usata
-se si ha a cuore la portabilità. La funzione è \funcd{ppoll}, ed il suo
-prototipo è:
-\begin{prototype}{sys/poll.h}
- {int ppoll(struct pollfd *fds, nfds\_t nfds, const struct timespec *timeout,
- const sigset\_t *sigmask)}
-
- La funzione attende un cambiamento di stato su un insieme di file
- descriptor.
-
- \bodydesc{La funzione restituisce il numero di file descriptor con attività
- in caso di successo, o 0 se c'è stato un timeout e -1 in caso di errore,
- ed in quest'ultimo caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno
- degli insiemi.
- \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale.
- \item[\errcode{EINVAL}] Il valore di \param{nfds} eccede il limite
- \macro{RLIMIT\_NOFILE}.
- \end{errlist}
- ed inoltre \errval{EFAULT} e \errval{ENOMEM}.}
-\end{prototype}
-
-La funzione ha lo stesso comportamento di \func{poll}, solo che si può
-specificare, con l'argomento \param{sigmask}, il puntatore ad una maschera di
-segnali; questa sarà la maschera utilizzata per tutto il tempo che la funzione
-resterà in attesa, all'uscita viene ripristinata la maschera originale. L'uso
-di questa funzione è cioè equivalente, come illustrato nella pagina di
-manuale, all'esecuzione atomica del seguente codice:
-\includecodesnip{listati/ppoll_means.c}
-
-Eccetto per \param{timeout}, che come per \func{pselect} deve essere un
-puntatore ad una struttura \struct{timespec}, gli altri argomenti comuni con
-\func{poll} hanno lo stesso significato, e la funzione restituisce gli stessi
-risultati illustrati in precedenza.
-
-
-\subsection{L'interfaccia di \textit{epoll}}
-\label{sec:file_epoll}
-
-\itindbeg{epoll}
-
-Nonostante \func{poll} presenti alcuni vantaggi rispetto a \func{select},
-anche questa funzione non è molto efficiente quando deve essere utilizzata con
-un gran numero di file descriptor,\footnote{in casi del genere \func{select}
- viene scartata a priori, perché può avvenire che il numero di file
- descriptor ecceda le dimensioni massime di un \itindex{file~descriptor~set}
- \textit{file descriptor set}.} in particolare nel caso in cui solo pochi di
-questi diventano attivi. Il problema in questo caso è che il tempo impiegato
-da \func{poll} a trasferire i dati da e verso il kernel è proporzionale al
-numero di file descriptor osservati, non a quelli che presentano attività.
-
-Quando ci sono decine di migliaia di file descriptor osservati e migliaia di
-eventi al secondo,\footnote{il caso classico è quello di un server web di un
- sito con molti accessi.} l'uso di \func{poll} comporta la necessità di
-trasferire avanti ed indietro da user space a kernel space la lunga lista
-delle strutture \struct{pollfd} migliaia di volte al secondo. A questo poi si
-aggiunge il fatto che la maggior parte del tempo di esecuzione sarà impegnato
-ad eseguire una scansione su tutti i file descriptor tenuti sotto controllo
-per determinare quali di essi (in genere una piccola percentuale) sono
-diventati attivi. In una situazione come questa l'uso delle funzioni classiche
-dell'interfaccia dell'\textit{I/O multiplexing} viene a costituire un collo di
-bottiglia che degrada irrimediabilmente le prestazioni.
-
-Per risolvere questo tipo di situazioni è stata creata una nuova
-interfaccia,\footnote{l'interfaccia è stata creata da Davide Libernzi, ed è
- stata introdotta per la prima volta nel kernel 2.5.44, ma la sua forma
- definitiva è stata raggiunta nel kernel 2.5.66.} detta \textit{epoll}, il
-cui concetto fondamentale è quello di restituire solamente le informazioni
-relative ai file descriptor osservati che presentano una attività, evitando
-così tutti le problematiche appena illustrate.
-
-
-
-
-
-\itindend{epoll}
-
-% TODO epoll
-%
-
-\section{L'accesso \textsl{asincrono} ai file}
-\label{sec:file_asyncronous_access}
-
-Benché l'\textit{I/O multiplexing} sia stata la prima, e sia tutt'ora una fra
-le più diffuse modalità di gestire l'I/O in situazioni complesse in cui si
-debba operare su più file contemporaneamente, esistono altre modalità di
-gestione delle stesse problematiche. In particolare sono importanti in questo
-contesto le modalità di accesso ai file eseguibili in maniera
-\textsl{asincrona}, quelle cioè in cui un processo non deve bloccarsi in
-attesa della disponibilità dell'accesso al file, ma può proseguire
-nell'esecuzione utilizzando invece un meccanismo di notifica asincrono (di
-norma un segnale), per essere avvisato della possibilità di eseguire le
-operazioni di I/O volute.
-
-
-\subsection{Operazioni asincrone sui file}
-\label{sec:file_asyncronous_operation}
-
-Abbiamo accennato in sez.~\ref{sec:file_open} che è possibile, attraverso l'uso
-del flag \const{O\_ASYNC},\footnote{l'uso del flag di \const{O\_ASYNC} e dei
- comandi \const{F\_SETOWN} e \const{F\_GETOWN} per \func{fcntl} è specifico
- di Linux e BSD.} aprire un file in modalità asincrona, così come è possibile
-attivare in un secondo tempo questa modalità impostando questo flag attraverso
-l'uso di \func{fcntl} con il comando \const{F\_SETFL} (vedi
-sez.~\ref{sec:file_fcntl}).
-
-In realtà in questo caso non si tratta di eseguire delle operazioni di lettura
-o scrittura del file in modo asincrono (tratteremo questo, che più
-propriamente è detto \textsl{I/O asincrono} in
-sez.~\ref{sec:file_asyncronous_io}), quanto di un meccanismo asincrono di
-notifica delle variazione dello stato del file descriptor aperto in questo
-modo.
-
-Quello che succede in questo caso è che il sistema genera un segnale
-(normalmente \const{SIGIO}, ma è possibile usarne altri con il comando
-\const{F\_SETSIG} di \func{fcntl}) tutte le volte che diventa possibile
-leggere o scrivere dal file descriptor che si è posto in questa modalità. Si
-può inoltre selezionare, con il comando \const{F\_SETOWN} di \func{fcntl},
-quale processo (o gruppo di processi) riceverà il segnale. Se pertanto si
-effettuano le operazioni di I/O in risposta alla ricezione del segnale non ci
-sarà più la necessità di restare bloccati in attesa della disponibilità di
-accesso ai file; per questo motivo Stevens chiama questa modalità
-\textit{signal driven I/O}.
-
-Questa è un'altra modalità di gestione I/O, alternativa all'uso di
-\itindex{epoll} \textit{epoll}, che consente di evitare l'uso delle funzioni
-\func{poll} o \func{select} che, come illustrato in sez.~\ref{sec:file_epoll},
-quando vengono usate con un numero molto grande di file descriptor, non hanno
-buone prestazioni.
-
-Tuttavia con l'implementazione classica dei segnali questa modalità di I/O
-presenta notevoli problemi, dato che non è possibile determinare, quando i
-file descriptor sono più di uno, qual è quello responsabile dell'emissione del
-segnale. Inoltre dato che i segnali normali non si accodano (si ricordi quanto
-illustrato in sez.~\ref{sec:sig_notification}), in presenza di più file
-descriptor attivi contemporaneamente, più segnali emessi nello stesso momento
-verrebbero notificati una volta sola.
-
-Linux però supporta le estensioni POSIX.1b dei segnali real-time, che vengono
-accodati e che permettono di riconoscere il file descriptor che li ha emessi.
-In questo caso infatti si può fare ricorso alle informazioni aggiuntive
-restituite attraverso la struttura \struct{siginfo\_t}, utilizzando la forma
-estesa \var{sa\_sigaction} del gestore installata con il flag
-\const{SA\_SIGINFO} (si riveda quanto illustrato in
-sez.~\ref{sec:sig_sigaction}).
-
-Per far questo però occorre utilizzare le funzionalità dei segnali real-time
-(vedi sez.~\ref{sec:sig_real_time}) impostando esplicitamente con il comando
-\const{F\_SETSIG} di \func{fcntl} un segnale real-time da inviare in caso di
-I/O asincrono (il segnale predefinito è \const{SIGIO}). In questo caso il
-gestore, tutte le volte che riceverà \const{SI\_SIGIO} come valore del
-campo \var{si\_code}\footnote{il valore resta \const{SI\_SIGIO} qualunque sia
- il segnale che si è associato all'I/O asincrono, ed indica appunto che il
- segnale è stato generato a causa di attività nell'I/O asincrono.} di
-\struct{siginfo\_t}, troverà nel campo \var{si\_fd} il valore del file
-descriptor che ha generato il segnale.
-
-Un secondo vantaggio dell'uso dei segnali real-time è che essendo questi
-ultimi dotati di una coda di consegna ogni segnale sarà associato ad uno solo
-file descriptor; inoltre sarà possibile stabilire delle priorità nella
-risposta a seconda del segnale usato, dato che i segnali real-time supportano
-anche questa funzionalità. In questo modo si può identificare immediatamente
-un file su cui l'accesso è diventato possibile evitando completamente l'uso di
-funzioni come \func{poll} e \func{select}, almeno fintanto che non si satura
-la coda.
-
-Se infatti si eccedono le dimensioni di quest'ultima, il kernel, non potendo
-più assicurare il comportamento corretto per un segnale real-time, invierà al
-suo posto un solo \const{SIGIO}, su cui si saranno accumulati tutti i segnali
-in eccesso, e si dovrà allora determinare con un ciclo quali sono i file
-diventati attivi.
-
-% TODO fare esempio che usa O_ASYNC
-
-
-\subsection{I meccanismi di notifica asincrona.}
-\label{sec:file_asyncronous_lease}
-
-Una delle domande più frequenti nella programmazione in ambiente unix-like è
-quella di come fare a sapere quando un file viene modificato. La
-risposta\footnote{o meglio la non risposta, tanto che questa nelle Unix FAQ
- \cite{UnixFAQ} viene anche chiamata una \textit{Frequently Unanswered
- Question}.} è che nell'architettura classica di Unix questo non è
-possibile. Al contrario di altri sistemi operativi infatti un kernel unix-like
-non prevede alcun meccanismo per cui un processo possa essere
-\textsl{notificato} di eventuali modifiche avvenute su un file. Questo è il
-motivo per cui i demoni devono essere \textsl{avvisati} in qualche
-modo\footnote{in genere questo vien fatto inviandogli un segnale di
- \const{SIGHUP} che, per convenzione adottata dalla gran parte di detti
- programmi, causa la rilettura della configurazione.} se il loro file di
-configurazione è stato modificato, perché possano rileggerlo e riconoscere le
-modifiche.
-
-Questa scelta è stata fatta perché provvedere un simile meccanismo a livello
-generale comporterebbe un notevole aumento di complessità dell'architettura
-della gestione dei file, per fornire una funzionalità necessaria soltanto in
-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 (molto richiesta specialmente nello sviluppo dei programmi ad interfaccia
-grafica) sono state successivamente introdotte delle estensioni che
-permettessero la creazione di meccanismi di notifica più efficienti dell'unica
-soluzione disponibile con l'interfaccia tradizionale, che è quella del
-\itindex{polling} \textit{polling}.
-
-Queste nuove funzionalità sono delle estensioni specifiche, non
-standardizzate, che sono disponibili soltanto su Linux (anche se altri kernel
-supportano meccanismi simili). Alcune di esse sono realizzate, e solo a
-partire dalla versione 2.4 del kernel, attraverso l'uso di alcuni
-\textsl{comandi} aggiuntivi per la funzione \func{fcntl} (vedi
-sez.~\ref{sec:file_fcntl}), che divengono disponibili soltanto se si è
-definita la macro \macro{\_GNU\_SOURCE} prima di includere \file{fcntl.h}.
-
-\index{file!lease|(}
-
-La prima di queste funzionalità è quella del cosiddetto \textit{file lease};
-questo è un meccanismo che consente ad un processo, detto \textit{lease
- holder}, di essere notificato quando un altro processo, chiamato a sua volta
-\textit{lease breaker}, cerca di eseguire una \func{open} o una
-\func{truncate} sul file del quale l'\textit{holder} detiene il
-\textit{lease}.
-
-La notifica avviene in maniera analoga a come illustrato in precedenza per
-l'uso di \const{O\_ASYNC}: di default viene inviato al \textit{lease holder}
-il segnale \const{SIGIO}, ma questo segnale può essere modificato usando il
-comando \const{F\_SETSIG} di \func{fcntl}.\footnote{anche in questo caso si
- può rispecificare lo stesso \const{SIGIO}.} Se si è fatto questo\footnote{è
- in genere è opportuno farlo, come in precedenza, per utilizzare segnali
- real-time.} e si è installato il gestore del segnale con \const{SA\_SIGINFO}
-si riceverà nel campo \var{si\_fd} della struttura \struct{siginfo\_t} il
-valore del file descriptor del file sul quale è stato compiuto l'accesso; in
-questo modo un processo può mantenere anche più di un \textit{file lease}.
-
-Esistono due tipi di \textit{file lease}: di lettura (\textit{read lease}) e
-di scrittura (\textit{write lease}). Nel primo caso la notifica avviene quando
-un altro processo esegue l'apertura del file in scrittura o usa
-\func{truncate} per troncarlo. Nel secondo caso la notifica avviene anche se
-il file viene aperto il lettura; in quest'ultimo caso però il \textit{lease}
-può essere ottenuto solo se nessun altro processo ha aperto lo stesso file.
-
-Come accennato in sez.~\ref{sec:file_fcntl} il comando di \func{fcntl} che
-consente di acquisire un \textit{file lease} è \const{F\_SETLEASE}, che viene
-utilizzato anche per rilasciarlo. In tal caso il file descriptor \param{fd}
-passato a \func{fcntl} servirà come riferimento per il file su cui si vuole
-operare, mentre per indicare il tipo di operazione (acquisizione o rilascio)
-occorrerà specificare come valore dell'argomento \param{arg} di \func{fcntl}
-uno dei tre valori di tab.~\ref{tab:file_lease_fctnl}.
-
-\begin{table}[htb]
- \centering
- \footnotesize
- \begin{tabular}[c]{|l|l|}
- \hline
- \textbf{Valore} & \textbf{Significato} \\
- \hline
- \hline
- \const{F\_RDLCK} & Richiede un \textit{read lease}.\\
- \const{F\_WRLCK} & Richiede un \textit{write lease}.\\
- \const{F\_UNLCK} & Rilascia un \textit{file lease}.\\
- \hline
- \end{tabular}
- \caption{Costanti per i tre possibili valori dell'argomento \param{arg} di
- \func{fcntl} quando usata con i comandi \const{F\_SETLEASE} e
- \const{F\_GETLEASE}.}
- \label{tab:file_lease_fctnl}
-\end{table}