capacità \const{CAP\_LEASE}, vedi sez.~\ref{sec:proc_capabilities}) può
acquisire \textit{lease} su qualunque file.

Se su un file è presente un \textit{lease} quando il \textit{lease breaker}
esegue una \func{truncate} o una \func{open} che confligge con
esso,\footnote{in realtà \func{truncate} confligge sempre, mentre \func{open},
  se eseguita in sola lettura, non confligge se si tratta di un \textit{read
    lease}.} la funzione si blocca (a meno di non avere aperto il file con
\const{O\_NONBLOCK}, nel qual caso \func{open} fallirebbe con un errore di
\errcode{EWOULDBLOCK}) e viene eseguita la notifica al \textit{lease holder},
così che questo possa completare le sue operazioni sul file e rilasciare il
\textit{lease}.  In sostanza con un \textit{read lease} si rilevano i
tentativi di accedere al file per modificarne i dati da parte di un altro
processo, mentre con un \textit{write lease} si rilevano anche i tentativi di
accesso in lettura.  Si noti comunque che le operazioni di notifica avvengono
solo in fase di apertura del file e non sulle singole operazioni di lettura e
scrittura.

L'utilizzo dei \textit{file lease} consente al \textit{lease holder} di
assicurare la consistenza di un file, a seconda dei due casi, prima che un
altro processo inizi con le sue operazioni di scrittura o di lettura su di
esso. In genere un \textit{lease holder} che riceve una notifica deve
provvedere a completare le necessarie operazioni (ad esempio scaricare
eventuali buffer), per poi rilasciare il \textit{lease} così che il
\textit{lease breaker} possa eseguire le sue operazioni. Questo si fa con il
comando \const{F\_SETLEASE}, o rimuovendo il \textit{lease} con
\const{F\_UNLCK}, o, nel caso di \textit{write lease} che confligge con una
operazione di lettura, declassando il \textit{lease} a lettura con
\const{F\_RDLCK}.

Se il \textit{lease holder} non provvede a rilasciare il \textit{lease} entro
il numero di secondi specificato dal parametro di sistema mantenuto in
\sysctlfile{fs/lease-break-time} sarà il kernel stesso a rimuoverlo o
declassarlo automaticamente (questa è una misura di sicurezza per evitare che
un processo blocchi indefinitamente l'accesso ad un file acquisendo un
\textit{lease}). Una volta che un \textit{lease} è stato rilasciato o
declassato (che questo sia fatto dal \textit{lease holder} o dal kernel è lo
stesso) le chiamate a \func{open} o \func{truncate} eseguite dal \textit{lease
  breaker} rimaste bloccate proseguono automaticamente.

Benché possa risultare utile per sincronizzare l'accesso ad uno stesso file da
parte di più processi, l'uso dei \textit{file lease} non consente comunque di
risolvere il problema di rilevare automaticamente quando un file o una
directory vengono modificati,\footnote{questa funzionalità venne aggiunta
  principalmente ad uso di Samba per poter facilitare l'emulazione del
  comportamento di Windows sui file, ma ad oggi viene considerata una
  interfaccia mal progettata ed il suo uso è fortemente sconsigliato a favore
  di \textit{inotify}.} che è quanto necessario ad esempio ai programma di
gestione dei file dei vari desktop grafici.

\itindbeg{dnotify}

Per risolvere questo problema a partire dal kernel 2.4 è stata allora creata
un'altra interfaccia,\footnote{si ricordi che anche questa è una interfaccia
  specifica di Linux che deve essere evitata se si vogliono scrivere programmi
  portabili, e che le funzionalità illustrate sono disponibili soltanto se è
  stata definita la macro \macro{\_GNU\_SOURCE}.} chiamata \textit{dnotify},
che consente di richiedere una notifica quando una directory, o uno qualunque
dei file in essa contenuti, viene modificato.  Come per i \textit{file lease}
la notifica avviene di default attraverso il segnale \signal{SIGIO}, ma se ne
può utilizzare un altro, e di nuovo, per le ragioni già esposte in precedenza,
è opportuno che si utilizzino dei segnali \textit{real-time}.  Inoltre, come
in precedenza, si potrà ottenere nel gestore del segnale il file descriptor
che è stato modificato tramite il contenuto della struttura
\struct{siginfo\_t}.

\itindend{file~lease}

\begin{table}[htb]
  \centering
  \footnotesize
  \begin{tabular}[c]{|l|p{8cm}|}
    \hline
    \textbf{Valore}  & \textbf{Significato} \\
    \hline
    \hline
    \const{DN\_ACCESS} & Un file è stato acceduto, con l'esecuzione di una fra
                         \func{read}, \func{pread}, \func{readv}.\\ 
    \const{DN\_MODIFY} & Un file è stato modificato, con l'esecuzione di una
                         fra \func{write}, \func{pwrite}, \func{writev}, 
                         \func{truncate}, \func{ftruncate}.\\ 
    \const{DN\_CREATE} & È stato creato un file nella directory, con
                         l'esecuzione di una fra \func{open}, \func{creat},
                         \func{mknod}, \func{mkdir}, \func{link},
                         \func{symlink}, \func{rename} (da un'altra
                         directory).\\
    \const{DN\_DELETE} & È stato cancellato un file dalla directory con
                         l'esecuzione di una fra \func{unlink}, \func{rename}
                         (su un'altra directory), \func{rmdir}.\\
    \const{DN\_RENAME} & È stato rinominato un file all'interno della
                         directory (con \func{rename}).\\
    \const{DN\_ATTRIB} & È stato modificato un attributo di un file con
                         l'esecuzione di una fra \func{chown}, \func{chmod},
                         \func{utime}.\\ 
    \const{DN\_MULTISHOT}& Richiede una notifica permanente di tutti gli
                         eventi.\\ 
    \hline    
  \end{tabular}
  \caption{Le costanti che identificano le varie classi di eventi per i quali
    si richiede la notifica con il comando \const{F\_NOTIFY} di \func{fcntl}.} 
  \label{tab:file_notify}
\end{table}

Ci si può registrare per le notifiche dei cambiamenti al contenuto di una
certa directory eseguendo la funzione \func{fcntl} su un file descriptor
associato alla stessa con il comando \const{F\_NOTIFY}. In questo caso
l'argomento \param{arg} di \func{fcntl} serve ad indicare per quali classi
eventi si vuole ricevere la notifica, e prende come valore una maschera
binaria composta dall'OR aritmetico di una o più delle costanti riportate in
tab.~\ref{tab:file_notify}.

A meno di non impostare in maniera esplicita una notifica permanente usando il
valore \const{DN\_MULTISHOT}, la notifica è singola: viene cioè inviata una
sola volta quando si verifica uno qualunque fra gli eventi per i quali la si è
richiesta. Questo significa che un programma deve registrarsi un'altra volta
se desidera essere notificato di ulteriori cambiamenti. Se si eseguono diverse
chiamate con \const{F\_NOTIFY} e con valori diversi per \param{arg} questi
ultimi si \textsl{accumulano}; cioè eventuali nuovi classi di eventi
specificate in chiamate successive vengono aggiunte a quelle già impostate
nelle precedenti.  Se si vuole rimuovere la notifica si deve invece
specificare un valore nullo.

\itindbeg{inotify}

Il maggiore problema di \textit{dnotify} è quello della scalabilità: si deve
usare un file descriptor per ciascuna directory che si vuole tenere sotto
controllo, il che porta facilmente ad avere un eccesso di file aperti. Inoltre
quando la directory che si controlla è all'interno di un dispositivo
rimovibile, mantenere il relativo file descriptor aperto comporta
l'impossibilità di smontare il dispositivo e di rimuoverlo, il che in genere
complica notevolmente la gestione dell'uso di questi dispositivi.

Un altro problema è che l'interfaccia di \textit{dnotify} consente solo di
tenere sotto controllo il contenuto di una directory; la modifica di un file
viene segnalata, ma poi è necessario verificare di quale file si tratta
(operazione che può essere molto onerosa quando una directory contiene un gran
numero di file).  Infine l'uso dei segnali come interfaccia di notifica
comporta tutti i problemi di gestione visti in sez.~\ref{sec:sig_management} e
sez.~\ref{sec:sig_adv_control}.  Per tutta questa serie di motivi in generale
quella di \textit{dnotify} viene considerata una interfaccia di usabilità
problematica ed il suo uso oggi è fortemente sconsigliato.

\itindend{dnotify}

Per risolvere i problemi appena illustrati è stata introdotta una nuova
interfaccia per l'osservazione delle modifiche a file o directory, chiamata
\textit{inotify}.\footnote{l'interfaccia è disponibile a partire dal kernel
  2.6.13, le relative funzioni sono state introdotte nelle glibc 2.4.}  Anche
questa è una interfaccia specifica di Linux (pertanto non deve essere usata se
si devono scrivere programmi portabili), ed è basata sull'uso di una coda di
notifica degli eventi associata ad un singolo file descriptor, il che permette
di risolvere il principale problema di \itindex{dnotify} \textit{dnotify}.  La
coda viene creata attraverso la funzione di sistema \funcd{inotify\_init}, il
cui prototipo è:

\begin{funcproto}{
\fhead{sys/inotify.h}
\fdecl{int inotify\_init(void)}
\fdesc{Inizializza una istanza di \textit{inotify}.}
}

{La funzione ritornaun file descriptor in caso di successo, o $-1$ in caso di
  errore, nel qual caso \var{errno} assumerà uno dei valori:
  \begin{errlist}
  \item[\errcode{EMFILE}] si è raggiunto il numero massimo di istanze di
    \textit{inotify} consentite all'utente.
  \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}

La funzione non prende alcun argomento; inizializza una istanza di
\textit{inotify} e restituisce un file descriptor attraverso il quale verranno
effettuate le operazioni di notifica; si tratta di un file descriptor speciale
che non è associato a nessun file su disco, e che viene utilizzato solo per
notificare gli eventi che sono stati posti in osservazione. Per evitare abusi
delle risorse di sistema è previsto che un utente possa utilizzare un numero
limitato di istanze di \textit{inotify}; il valore di default del limite è di
128, ma questo valore può essere cambiato con \func{sysctl} o usando il file
\sysctlfile{fs/inotify/max\_user\_instances}.

Dato che questo file descriptor non è associato a nessun file o directory
reale, l'inconveniente di non poter smontare un filesystem i cui file sono
tenuti sotto osservazione viene completamente eliminato; anzi, una delle
capacità dell'interfaccia di \textit{inotify} è proprio quella di notificare
il fatto che il filesystem su cui si trova il file o la directory osservata è
stato smontato.

Inoltre trattandosi di un file descriptor a tutti gli effetti, esso potrà
essere utilizzato come argomento per le funzioni \func{select} e \func{poll} e
con l'interfaccia di \textit{epoll}, ed a partire dal kernel 2.6.25 è stato
introdotto anche il supporto per il \itindex{signal~driven~I/O}
\texttt{signal-driven I/O}.  Siccome gli eventi vengono notificati come dati
disponibili in lettura, dette funzioni ritorneranno tutte le volte che si avrà
un evento di notifica. 

Così, invece di dover utilizzare i segnali, considerati una pessima scelta dal
punto di vista dell'interfaccia utente, si potrà gestire l'osservazione degli
eventi con una qualunque delle modalità di \textit{I/O multiplexing}
illustrate in sez.~\ref{sec:file_multiplexing}. Qualora si voglia cessare
l'osservazione, sarà sufficiente chiudere il file descriptor e tutte le
risorse allocate saranno automaticamente rilasciate. Infine l'interfaccia di
\textit{inotify} consente di mettere sotto osservazione, oltre che una
directory, anche singoli file.

Una volta creata la coda di notifica si devono definire gli eventi da tenere
sotto osservazione; questo viene fatto attraverso una \textsl{lista di
  osservazione} (o \textit{watch list}) che è associata alla coda. Per gestire
la lista di osservazione l'interfaccia fornisce due funzioni di sistema, la
prima di queste è \funcd{inotify\_add\_watch}, il cui prototipo è:

\begin{funcproto}{
\fhead{sys/inotify.h}
\fdecl{int inotify\_add\_watch(int fd, const char *pathname, uint32\_t mask)}
\fdesc{Aggiunge un evento di osservazione a una lista di osservazione.} 
}

{La funzione ritorna un valore positivo in caso di successo, o $-1$ per un
  errore, nel qual caso \var{errno} assumerà uno dei valori:
  \begin{errlist}
  \item[\errcode{EACCES}] non si ha accesso in lettura al file indicato.
  \item[\errcode{EINVAL}] \param{mask} non contiene eventi legali o \param{fd}
    non è un file descriptor di \textit{inotify}.
  \item[\errcode{ENOSPC}] si è raggiunto il numero massimo di voci di
    osservazione o il kernel non ha potuto allocare una risorsa necessaria.
  \end{errlist}
  ed inoltre \errval{EFAULT}, \errval{ENOMEM} e \errval{EBADF} nel loro
  significato generico.}
\end{funcproto}

La funzione consente di creare un ``\textsl{osservatore}'' (il cosiddetto
``\textit{watch}'') nella lista di osservazione di una coda di notifica, che
deve essere indicata specificando il file descriptor ad essa associato
nell'argomento \param{fd}, che ovviamente dovrà essere un file descriptor
creato con \func{inotify\_init}.  Il file o la directory da porre sotto
osservazione vengono invece indicati per nome, da passare
nell'argomento \param{pathname}.  Infine il terzo argomento, \param{mask},
indica che tipo di eventi devono essere tenuti sotto osservazione e le
modalità della stessa.  L'operazione può essere ripetuta per tutti i file e le
directory che si vogliono tenere sotto osservazione,\footnote{anche in questo
  caso c'è un limite massimo che di default è pari a 8192, ed anche questo
  valore può essere cambiato con \func{sysctl} o usando il file
  \sysctlfile{fs/inotify/max\_user\_watches}.} e si utilizzerà sempre un solo
file descriptor.

Il tipo di evento che si vuole osservare deve essere specificato
nell'argomento \param{mask} come maschera binaria, combinando i valori delle
costanti riportate in tab.~\ref{tab:inotify_event_watch} che identificano i
singoli bit della maschera ed il relativo significato. In essa si sono marcati
con un ``$\bullet$'' gli eventi che, quando specificati per una directory,
vengono osservati anche su tutti i file che essa contiene.  Nella seconda
parte della tabella si sono poi indicate alcune combinazioni predefinite dei
flag della prima parte.

\begin{table}[htb]
  \centering
  \footnotesize
  \begin{tabular}[c]{|l|c|p{8cm}|}
    \hline
    \textbf{Valore}  & & \textbf{Significato} \\
    \hline
    \hline
    \const{IN\_ACCESS}        &$\bullet$& C'è stato accesso al file in
                                          lettura.\\  
    \const{IN\_ATTRIB}        &$\bullet$& Ci sono stati cambiamenti sui dati
                                          dell'\itindex{inode} \textit{inode}
                                          (o sugli attributi estesi, vedi
                                          sez.~\ref{sec:file_xattr}).\\ 
    \const{IN\_CLOSE\_WRITE}  &$\bullet$& È stato chiuso un file aperto in
                                          scrittura.\\  
    \const{IN\_CLOSE\_NOWRITE}&$\bullet$& È stato chiuso un file aperto in
                                          sola lettura.\\
    \const{IN\_CREATE}        &$\bullet$& È stato creato un file o una
                                          directory in una directory sotto
                                          osservazione.\\  
    \const{IN\_DELETE}        &$\bullet$& È stato cancellato un file o una
                                          directory in una directory sotto
                                          osservazione.\\ 
    \const{IN\_DELETE\_SELF}  & --      & È stato cancellato il file (o la
                                          directory) sotto osservazione.\\ 
    \const{IN\_MODIFY}        &$\bullet$& È stato modificato il file.\\ 
    \const{IN\_MOVE\_SELF}    &         & È stato rinominato il file (o la
                                          directory) sotto osservazione.\\ 
    \const{IN\_MOVED\_FROM}   &$\bullet$& Un file è stato spostato fuori dalla
                                          directory sotto osservazione.\\ 
    \const{IN\_MOVED\_TO}     &$\bullet$& Un file è stato spostato nella
                                          directory sotto osservazione.\\ 
    \const{IN\_OPEN}          &$\bullet$& Un file è stato aperto.\\ 
    \hline    
    \const{IN\_CLOSE}         &         & Combinazione di
                                          \const{IN\_CLOSE\_WRITE} e
                                          \const{IN\_CLOSE\_NOWRITE}.\\  
    \const{IN\_MOVE}          &         & Combinazione di
                                          \const{IN\_MOVED\_FROM} e
                                          \const{IN\_MOVED\_TO}.\\
    \const{IN\_ALL\_EVENTS}   &         & Combinazione di tutti i flag
                                          possibili.\\
    \hline    
  \end{tabular}
  \caption{Le costanti che identificano i bit della maschera binaria
    dell'argomento \param{mask} di \func{inotify\_add\_watch} che indicano il
    tipo di evento da tenere sotto osservazione.} 
  \label{tab:inotify_event_watch}
\end{table}

Oltre ai flag di tab.~\ref{tab:inotify_event_watch}, che indicano il tipo di
evento da osservare e che vengono utilizzati anche in uscita per indicare il
tipo di evento avvenuto, \func{inotify\_add\_watch} supporta ulteriori
flag,\footnote{i flag \const{IN\_DONT\_FOLLOW}, \const{IN\_MASK\_ADD} e
  \const{IN\_ONLYDIR} sono stati introdotti a partire dalle glibc 2.5, se si
  usa la versione 2.4 è necessario definirli a mano.}  riportati in
tab.~\ref{tab:inotify_add_watch_flag}, che indicano le modalità di
osservazione (da passare sempre nell'argomento \param{mask}) e che al
contrario dei precedenti non vengono mai impostati nei risultati in uscita.

\begin{table}[htb]
  \centering
  \footnotesize
  \begin{tabular}[c]{|l|p{8cm}|}
    \hline
    \textbf{Valore}  & \textbf{Significato} \\
    \hline
    \hline
    \const{IN\_DONT\_FOLLOW}& Non dereferenzia \param{pathname} se questo è un
                              link simbolico.\\
    \const{IN\_MASK\_ADD}   & Aggiunge a quelli già impostati i flag indicati
                              nell'argomento \param{mask}, invece di
                              sovrascriverli.\\
    \const{IN\_ONESHOT}     & Esegue l'osservazione su \param{pathname} per una
                              sola volta, rimuovendolo poi dalla \textit{watch
                                list}.\\ 
    \const{IN\_ONLYDIR}     & Se \param{pathname} è una directory riporta
                              soltanto gli eventi ad essa relativi e non
                              quelli per i file che contiene.\\ 
    \hline    
  \end{tabular}
  \caption{Le costanti che identificano i bit della maschera binaria
    dell'argomento \param{mask} di \func{inotify\_add\_watch} che indicano le
    modalità di osservazione.} 
  \label{tab:inotify_add_watch_flag}
\end{table}

Se non esiste nessun \textit{watch} per il file o la directory specificata
questo verrà creato per gli eventi specificati dall'argomento \param{mask},
altrimenti la funzione sovrascriverà le impostazioni precedenti, a meno che
non si sia usato il flag \const{IN\_MASK\_ADD}, nel qual caso gli eventi
specificati saranno aggiunti a quelli già presenti.

Come accennato quando si tiene sotto osservazione una directory vengono
restituite le informazioni sia riguardo alla directory stessa che ai file che
essa contiene; questo comportamento può essere disabilitato utilizzando il
flag \const{IN\_ONLYDIR}, che richiede di riportare soltanto gli eventi
relativi alla directory stessa. Si tenga presente inoltre che quando si
osserva una directory vengono riportati solo gli eventi sui file che essa
contiene direttamente, non quelli relativi a file contenuti in eventuali
sottodirectory; se si vogliono osservare anche questi sarà necessario creare
ulteriori \textit{watch} per ciascuna sottodirectory.

Infine usando il flag \const{IN\_ONESHOT} è possibile richiedere una notifica
singola;\footnote{questa funzionalità però è disponibile soltanto a partire dal
  kernel 2.6.16.} una volta verificatosi uno qualunque fra gli eventi
richiesti con \func{inotify\_add\_watch} l'\textsl{osservatore} verrà
automaticamente rimosso dalla lista di osservazione e nessun ulteriore evento
sarà più notificato.

In caso di successo \func{inotify\_add\_watch} ritorna un intero positivo,
detto \textit{watch descriptor}, che identifica univocamente un
\textsl{osservatore} su una coda di notifica; esso viene usato per farvi
riferimento sia riguardo i risultati restituiti da \textit{inotify}, che per
la eventuale rimozione dello stesso. 

La seconda funzione di sistema per la gestione delle code di notifica, che
permette di rimuovere un \textsl{osservatore}, è \funcd{inotify\_rm\_watch},
ed il suo prototipo è:

\begin{funcproto}{
\fhead{sys/inotify.h}
\fdecl{int inotify\_rm\_watch(int fd, uint32\_t wd)}
\fdesc{Rimuove un \textsl{osservatore} da una coda di notifica.} 
}

{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}] non si è specificato in \param{fd} un file descriptor
    valido.
  \item[\errcode{EINVAL}] il valore di \param{wd} non è corretto, o \param{fd}
    non è associato ad una coda di notifica.
  \end{errlist}
}
\end{funcproto}

La funzione rimuove dalla coda di notifica identificata dall'argomento
\param{fd} l'osservatore identificato dal \textit{watch descriptor}
\param{wd}; ovviamente deve essere usato per questo argomento un valore
ritornato da \func{inotify\_add\_watch}, altrimenti si avrà un errore di
\errval{EINVAL}. In caso di successo della rimozione, contemporaneamente alla
cancellazione dell'osservatore, sulla coda di notifica verrà generato un
evento di tipo \const{IN\_IGNORED} (vedi
tab.~\ref{tab:inotify_read_event_flag}). Si tenga presente che se un file
viene cancellato o un filesystem viene smontato i relativi osservatori vengono
rimossi automaticamente e non è necessario utilizzare
\func{inotify\_rm\_watch}.

Come accennato l'interfaccia di \textit{inotify} prevede che gli eventi siano
notificati come dati presenti in lettura sul file descriptor associato alla
coda di notifica. Una applicazione pertanto dovrà leggere i dati da detto file
con una \func{read}, che ritornerà sul buffer i dati presenti nella forma di
una o più strutture di tipo \struct{inotify\_event} (la cui definizione è
riportata in fig.~\ref{fig:inotify_event}). Qualora non siano presenti dati la
\func{read} si bloccherà (a meno di non aver impostato il file descriptor in
modalità non bloccante) fino all'arrivo di almeno un evento.

\begin{figure}[!htb]
  \footnotesize \centering
  \begin{minipage}[c]{0.90\textwidth}
    \includestruct{listati/inotify_event.h}
  \end{minipage} 
  \normalsize 
  \caption{La struttura \structd{inotify\_event} usata dall'interfaccia di
    \textit{inotify} per riportare gli eventi.}
  \label{fig:inotify_event}
\end{figure}

Una ulteriore caratteristica dell'interfaccia di \textit{inotify} è che essa
permette di ottenere con \func{ioctl}, come per i file descriptor associati ai
socket (si veda sez.~\ref{sec:sock_ioctl_IP}), il numero di byte disponibili
in lettura sul file descriptor, utilizzando su di esso l'operazione
\const{FIONREAD}.\footnote{questa è una delle operazioni speciali per i file
  (vedi sez.~\ref{sec:file_fcntl_ioctl}), che è disponibile solo per i socket
  e per i file descriptor creati con \func{inotify\_init}.} Si può così
utilizzare questa operazione, oltre che per predisporre una operazione di
lettura con un buffer di dimensioni adeguate, anche per ottenere rapidamente
il numero di file che sono cambiati.

Una volta effettuata la lettura con \func{read} a ciascun evento sarà
associata una struttura \struct{inotify\_event} contenente i rispettivi dati.
Per identificare a quale file o directory l'evento corrisponde viene
restituito nel campo \var{wd} il \textit{watch descriptor} con cui il relativo
osservatore è stato registrato. Il campo \var{mask} contiene invece una
maschera di bit che identifica il tipo di evento verificatosi; in essa
compariranno sia i bit elencati nella prima parte di
tab.~\ref{tab:inotify_event_watch}, che gli eventuali valori aggiuntivi di
tab.~\ref{tab:inotify_read_event_flag} (questi compaiono solo nel campo
\var{mask} di \struct{inotify\_event}, e non sono utilizzabili in fase di
registrazione dell'osservatore).

\begin{table}[htb]
  \centering
  \footnotesize
  \begin{tabular}[c]{|l|p{10cm}|}
    \hline
    \textbf{Valore}  & \textbf{Significato} \\
    \hline
    \hline
    \const{IN\_IGNORED}    & L'osservatore è stato rimosso, sia in maniera 
                             esplicita con l'uso di \func{inotify\_rm\_watch}, 
                             che in maniera implicita per la rimozione 
                             dell'oggetto osservato o per lo smontaggio del
                             filesystem su cui questo si trova.\\
    \const{IN\_ISDIR}      & L'evento avvenuto fa riferimento ad una directory
                             (consente così di distinguere, quando si pone
                             sotto osservazione una directory, fra gli eventi
                             relativi ad essa e quelli relativi ai file che
                             essa contiene).\\
    \const{IN\_Q\_OVERFLOW}& Si sono eccedute le dimensioni della coda degli
                             eventi (\textit{overflow} della coda); in questo
                             caso il valore di \var{wd} è $-1$.\footnotemark\\
    \const{IN\_UNMOUNT}    & Il filesystem contenente l'oggetto posto sotto
                             osservazione è stato smontato.\\
    \hline    
  \end{tabular}
  \caption{Le costanti che identificano i bit aggiuntivi usati nella maschera
    binaria del campo \var{mask} di \struct{inotify\_event}.} 
  \label{tab:inotify_read_event_flag}
\end{table}

\footnotetext{la coda di notifica ha una dimensione massima che viene
  controllata dal parametro di sistema
  \sysctlfile{fs/inotify/max\_queued\_events}, che indica il numero massimo di
  eventi che possono essere mantenuti sulla stessa; quando detto valore viene
  ecceduto gli ulteriori eventi vengono scartati, ma viene comunque generato
  un evento di tipo \const{IN\_Q\_OVERFLOW}.}

Il campo \var{cookie} contiene invece un intero univoco che permette di
identificare eventi correlati (per i quali avrà lo stesso valore), al momento
viene utilizzato soltanto per rilevare lo spostamento di un file, consentendo
così all'applicazione di collegare la corrispondente coppia di eventi
\const{IN\_MOVED\_TO} e \const{IN\_MOVED\_FROM}.

Infine due campi \var{name} e \var{len} sono utilizzati soltanto quando
l'evento è relativo ad un file presente in una directory posta sotto
osservazione, in tal caso essi contengono rispettivamente il nome del file
(come \itindsub{pathname}{relativo} \textit{pathname} relativo alla directory
osservata) e la relativa dimensione in byte. Il campo \var{name} viene sempre
restituito come stringa terminata da NUL, con uno o più zeri di terminazione,
a seconda di eventuali necessità di allineamento del risultato, ed il valore
di \var{len} corrisponde al totale della dimensione di \var{name}, zeri
aggiuntivi compresi. La stringa con il nome del file viene restituita nella
lettura subito dopo la struttura \struct{inotify\_event}; questo significa che
le dimensioni di ciascun evento di \textit{inotify} saranno pari a
\code{sizeof(\struct{inotify\_event}) + len}.

Vediamo allora un esempio dell'uso dell'interfaccia di \textit{inotify} con un
semplice programma che permette di mettere sotto osservazione uno o più file e
directory. Il programma si chiama \texttt{inotify\_monitor.c} ed il codice
completo è disponibile coi sorgenti allegati alla guida, il corpo principale
del programma, che non contiene la sezione di gestione delle opzioni e le
funzioni di ausilio è riportato in fig.~\ref{fig:inotify_monitor_example}.

\begin{figure}[!htbp]
  \footnotesize \centering
  \begin{minipage}[c]{\codesamplewidth}
    \includecodesample{listati/inotify_monitor.c}
  \end{minipage}
  \normalsize
  \caption{Esempio di codice che usa l'interfaccia di \textit{inotify}.}
  \label{fig:inotify_monitor_example}
\end{figure}

Una volta completata la scansione delle opzioni il corpo del programma inizia
controllando (\texttt{\small 11-15}) che sia rimasto almeno un argomento che
indichi quale file o directory mettere sotto osservazione (e qualora questo
non avvenga esce stampando la pagina di aiuto); dopo di che passa
(\texttt{\small 16-20}) all'inizializzazione di \textit{inotify} ottenendo con
\func{inotify\_init} il relativo file descriptor (o si esce in caso di
errore).

Il passo successivo è aggiungere (\texttt{\small 21-30}) alla coda di
notifica gli opportuni osservatori per ciascuno dei file o directory indicati
all'invocazione del comando; questo viene fatto eseguendo un ciclo
(\texttt{\small 22-29}) fintanto che la variabile \var{i}, inizializzata a
zero (\texttt{\small 21}) all'inizio del ciclo, è minore del numero totale di
argomenti rimasti. All'interno del ciclo si invoca (\texttt{\small 23})
\func{inotify\_add\_watch} per ciascuno degli argomenti, usando la maschera
degli eventi data dalla variabile \var{mask} (il cui valore viene impostato
nella scansione delle opzioni), in caso di errore si esce dal programma
altrimenti si incrementa l'indice (\texttt{\small 29}).

Completa l'inizializzazione di \textit{inotify} inizia il ciclo principale
(\texttt{\small 32-56}) del programma, nel quale si resta in attesa degli
eventi che si intendono osservare. Questo viene fatto eseguendo all'inizio del
ciclo (\texttt{\small 33}) una \func{read} che si bloccherà fintanto che non
si saranno verificati eventi.

Dato che l'interfaccia di \textit{inotify} può riportare anche più eventi in
una sola lettura, si è avuto cura di passare alla \func{read} un buffer di
dimensioni adeguate, inizializzato in (\texttt{\small 7}) ad un valore di
approssimativamente 512 eventi (si ricordi che la quantità di dati restituita
da \textit{inotify} è variabile a causa della diversa lunghezza del nome del
file restituito insieme a \struct{inotify\_event}). In caso di errore di
lettura (\texttt{\small 35-40}) il programma esce con un messaggio di errore
(\texttt{\small 37-39}), a meno che non si tratti di una interruzione della
\textit{system call}, nel qual caso (\texttt{\small 36}) si ripete la lettura.

Se la lettura è andata a buon fine invece si esegue un ciclo (\texttt{\small
  43-52}) per leggere tutti gli eventi restituiti, al solito si inizializza
l'indice \var{i} a zero (\texttt{\small 42}) e si ripetono le operazioni
(\texttt{\small 43}) fintanto che esso non supera il numero di byte restituiti
in lettura. Per ciascun evento all'interno del ciclo si assegna alla variabile
\var{event} (si noti come si sia eseguito un opportuno \textit{casting} del
puntatore) l'indirizzo nel buffer della corrispondente struttura
\struct{inotify\_event} (\texttt{\small 44}), e poi si stampano il numero di
\textit{watch descriptor} (\texttt{\small 45}) ed il file a cui questo fa
riferimento (\texttt{\small 46}), ricavato dagli argomenti passati a riga di
comando sfruttando il fatto che i \textit{watch descriptor} vengono assegnati
in ordine progressivo crescente a partire da 1.

Qualora sia presente il riferimento ad un nome di file associato all'evento lo
si stampa (\texttt{\small 47-49}); si noti come in questo caso si sia
controllato il valore del campo \var{event->len} e non il fatto che
\var{event->name} riporti o meno un puntatore nullo. L'interfaccia infatti,
qualora il nome non sia presente, non tocca il campo \var{event->name}, che
si troverà pertanto a contenere quello che era precedentemente presente nella
rispettiva locazione di memoria, nel caso più comune il puntatore al nome di
un file osservato in precedenza.

Si utilizza poi (\texttt{\small 50}) la funzione \code{printevent}, che
interpreta il valore del campo \var{event->mask}, per stampare il tipo di
eventi accaduti.\footnote{per il relativo codice, che non riportiamo in quanto
  non essenziale alla comprensione dell'esempio, si possono utilizzare
  direttamente i sorgenti allegati alla guida.} Infine (\texttt{\small 51}) si
provvede ad aggiornare l'indice \var{i} per farlo puntare all'evento
successivo.

Se adesso usiamo il programma per mettere sotto osservazione una directory, e
da un altro terminale eseguiamo il comando \texttt{ls} otterremo qualcosa del
tipo di:
\begin{Console}
piccardi@gethen:~/gapil/sources$ \textbf{./inotify_monitor -a /home/piccardi/gapil/}
Watch descriptor 1
Observed event on /home/piccardi/gapil/
IN_OPEN, 
Watch descriptor 1
Observed event on /home/piccardi/gapil/
IN_CLOSE_NOWRITE, 
\end{Console}
%$

I lettori più accorti si saranno resi conto che nel ciclo di lettura degli
eventi appena illustrato non viene trattato il caso particolare in cui la
funzione \func{read} restituisce in \var{nread} un valore nullo. Lo si è fatto
perché con \textit{inotify} il ritorno di una \func{read} con un valore nullo
avviene soltanto, come forma di avviso, quando si sia eseguita la funzione
specificando un buffer di dimensione insufficiente a contenere anche un solo
evento. Nel nostro caso le dimensioni erano senz'altro sufficienti, per cui
tale evenienza non si verificherà mai.

Ci si potrà però chiedere cosa succede se il buffer è sufficiente per un
evento, ma non per tutti gli eventi verificatisi. Come si potrà notare nel
codice illustrato in precedenza non si è presa nessuna precauzione per
verificare che non ci fossero stati troncamenti dei dati. Anche in questo caso
il comportamento scelto è corretto, perché l'interfaccia di \textit{inotify}
garantisce automaticamente, anche quando ne sono presenti in numero maggiore,
di restituire soltanto il numero di eventi che possono rientrare completamente
nelle dimensioni del buffer specificato.\footnote{si avrà cioè, facendo
  riferimento sempre al codice di fig.~\ref{fig:inotify_monitor_example}, che
  \var{read} sarà in genere minore delle dimensioni di \var{buffer} ed uguale
  soltanto qualora gli eventi corrispondano esattamente alle dimensioni di
  quest'ultimo.} Se gli eventi sono di più saranno restituiti solo quelli che
entrano interamente nel buffer e gli altri saranno restituiti alla successiva
chiamata di \func{read}.

Infine un'ultima caratteristica dell'interfaccia di \textit{inotify} è che gli
eventi restituiti nella lettura formano una sequenza ordinata, è cioè
garantito che se si esegue uno spostamento di un file gli eventi vengano
generati nella sequenza corretta. L'interfaccia garantisce anche che se si
verificano più eventi consecutivi identici (vale a dire con gli stessi valori
dei campi \var{wd}, \var{mask}, \var{cookie}, e \var{name}) questi vengono
raggruppati in un solo evento.

\itindend{inotify}

% TODO trattare fanotify, vedi http://lwn.net/Articles/339399/ e 
% http://lwn.net/Articles/343346/ (incluso nel 2.6.36)


\subsection{L'interfaccia POSIX per l'I/O asincrono}
\label{sec:file_asyncronous_io}

Una modalità alternativa all'uso dell'\textit{I/O multiplexing} per gestione
dell'I/O simultaneo su molti file è costituita dal cosiddetto \textsl{I/O
  asincrono} o ``AIO''. Il concetto base dell'\textsl{I/O asincrono} è che le
funzioni di I/O non attendono il completamento delle operazioni prima di
ritornare, così che il processo non viene bloccato.  In questo modo diventa ad
esempio possibile effettuare una richiesta preventiva di dati, in modo da
poter effettuare in contemporanea le operazioni di calcolo e quelle di I/O.

Benché la modalità di apertura asincrona di un file vista in
sez.~\ref{sec:signal_driven_io} possa risultare utile in varie occasioni (in
particolar modo con i socket e gli altri file per i quali le funzioni di I/O
sono \index{system~call~lente} \textit{system call} lente), essa è comunque
limitata alla notifica della disponibilità del file descriptor per le
operazioni di I/O, e non ad uno svolgimento asincrono delle medesime.  Lo
standard POSIX.1b definisce una interfaccia apposita per l'I/O asincrono vero
e proprio,\footnote{questa è stata ulteriormente perfezionata nelle successive
  versioni POSIX.1-2001 e POSIX.1-2008.} che prevede un insieme di funzioni
dedicate per la lettura e la scrittura dei file, completamente separate
rispetto a quelle usate normalmente.

In generale questa interfaccia è completamente astratta e può essere
implementata sia direttamente nel kernel che in \textit{user space} attraverso
l'uso di \itindex{thread} \textit{thread}. Per le versioni del kernel meno
recenti esiste una implementazione di questa interfaccia fornita completamente
delle \acr{glibc} a partire dalla versione 2.1, che è realizzata completamente
in \textit{user space}, ed è accessibile linkando i programmi con la libreria
\file{librt}. A partire dalla versione 2.5.32 è stato introdotto nel kernel
una nuova infrastruttura per l'I/O asincrono, ma ancora il supporto è parziale
ed insufficiente ad implementare tutto l'AIO POSIX.

Lo standard POSIX prevede che tutte le operazioni di I/O asincrono siano
controllate attraverso l'uso di una apposita struttura \struct{aiocb} (il cui
nome sta per \textit{asyncronous I/O control block}), che viene passata come
argomento a tutte le funzioni dell'interfaccia. La sua definizione, come
effettuata in \headfile{aio.h}, è riportata in
fig.~\ref{fig:file_aiocb}. Nello steso file è definita la macro
\macro{\_POSIX\_ASYNCHRONOUS\_IO}, che dichiara la disponibilità
dell'interfaccia per l'I/O asincrono.

\begin{figure}[!htb]
  \footnotesize \centering
  \begin{minipage}[c]{0.90\textwidth}
    \includestruct{listati/aiocb.h}
  \end{minipage}
  \normalsize 
  \caption{La struttura \structd{aiocb}, usata per il controllo dell'I/O
    asincrono.}
  \label{fig:file_aiocb}
\end{figure}

Le operazioni di I/O asincrono possono essere effettuate solo su un file già
aperto; il file deve inoltre supportare la funzione \func{lseek}, pertanto
terminali e \textit{pipe} sono esclusi. Non c'è limite al numero di operazioni
contemporanee effettuabili su un singolo file.  Ogni operazione deve
inizializzare opportunamente un \textit{control block}.  Il file descriptor su
cui operare deve essere specificato tramite il campo \var{aio\_fildes}; dato
che più operazioni possono essere eseguita in maniera asincrona, il concetto
di posizione corrente sul file viene a mancare; pertanto si deve sempre
specificare nel campo \var{aio\_offset} la posizione sul file da cui i dati
saranno letti o scritti.  Nel campo \var{aio\_buf} deve essere specificato
l'indirizzo del buffer usato per l'I/O, ed in \var{aio\_nbytes} la lunghezza
del blocco di dati da trasferire.

Il campo \var{aio\_reqprio} permette di impostare la priorità delle operazioni
di I/O, in generale perché ciò sia possibile occorre che la piattaforma
supporti questa caratteristica, questo viene indicato dal fatto che le macro
\macro{\_POSIX\_PRIORITIZED\_IO}, e \macro{\_POSIX\_PRIORITY\_SCHEDULING} sono
definite. La priorità viene impostata a partire da quella del processo
chiamante (vedi sez.~\ref{sec:proc_priority}), cui viene sottratto il valore
di questo campo.  Il campo \var{aio\_lio\_opcode} è usato solo dalla funzione
\func{lio\_listio}, che, come vedremo, permette di eseguire con una sola
chiamata una serie di operazioni, usando un vettore di \textit{control
  block}. Tramite questo campo si specifica quale è la natura di ciascuna di
esse.

Infine il campo \var{aio\_sigevent} è una struttura di tipo \struct{sigevent}
(illustrata in in fig.~\ref{fig:struct_sigevent}) che serve a specificare il
modo in cui si vuole che venga effettuata la notifica del completamento delle
operazioni richieste; per la trattazione delle modalità di utilizzo della
stessa si veda quanto già visto in proposito in sez.~\ref{sec:sig_timer_adv}.

Le due funzioni base dell'interfaccia per l'I/O asincrono sono
\funcd{aio\_read} ed \funcd{aio\_write}.  Esse permettono di richiedere una
lettura od una scrittura asincrona di dati usando la struttura \struct{aiocb}
appena descritta; i rispettivi prototipi sono:

\begin{funcproto}{
\fhead{aio.h}
\fdecl{int aio\_read(struct aiocb *aiocbp)}
\fdesc{Richiede una lettura asincrona.} 
\fdecl{int aio\_write(struct aiocb *aiocbp)}
\fdesc{Richiede una scrittura asincrona.} 
}

{Le funzioni ritornano $0$ in caso di successo e $-1$ per un errore, nel qual
  caso \var{errno} assumerà uno dei valori: 
  \begin{errlist}
  \item[\errcode{EAGAIN}] la coda delle richieste è momentaneamente piena.
  \item[\errcode{EBADF}] si è specificato un file descriptor sbagliato.
  \item[\errcode{EINVAL}] si è specificato un valore non valido per i campi
    \var{aio\_offset} o \var{aio\_reqprio} di \param{aiocbp}.
  \item[\errcode{ENOSYS}] la funzione non è implementata.
  \end{errlist}
}
\end{funcproto}


Entrambe le funzioni ritornano immediatamente dopo aver messo in coda la
richiesta, o in caso di errore. Non è detto che gli errori \errcode{EBADF} ed
\errcode{EINVAL} siano rilevati immediatamente al momento della chiamata,
potrebbero anche emergere nelle fasi successive delle operazioni. Lettura e
scrittura avvengono alla posizione indicata da \var{aio\_offset}, a meno che
il file non sia stato aperto in \textit{append mode} (vedi
sez.~\ref{sec:file_open_close}), nel qual caso le scritture vengono effettuate
comunque alla fine del file, nell'ordine delle chiamate a \func{aio\_write}.

Si tenga inoltre presente che deallocare la memoria indirizzata da
\param{aiocbp} o modificarne i valori prima della conclusione di una
operazione può dar luogo a risultati impredicibili, perché l'accesso ai vari
campi per eseguire l'operazione può avvenire in un momento qualsiasi dopo la
richiesta. Questo comporta che non si devono usare per \param{aiocbp}
\index{variabili!automatiche} variabili automatiche e che non si deve
riutilizzare la stessa struttura per un'altra operazione fintanto che la
precedente non sia stata ultimata. In generale per ogni operazione si deve
utilizzare una diversa struttura \struct{aiocb}.

Dato che si opera in modalità asincrona, il successo di \func{aio\_read} o
\func{aio\_write} non implica che le operazioni siano state effettivamente
eseguite in maniera corretta; per verificarne l'esito l'interfaccia prevede
altre due funzioni, che permettono di controllare lo stato di esecuzione. La
prima è \funcd{aio\_error}, che serve a determinare un eventuale stato di
errore; il suo prototipo è:

\begin{funcproto}{
\fhead{aio.h}
\fdecl{int aio\_error(const struct aiocb *aiocbp)} 
\fdesc{Determina lo stato di errore di una operazione di I/O asincrono.} 
}

{La funzione ritorna $0$ se le operazioni si sono concluse con successo,
  altrimenti restituisce \errval{EINPROGRESS} se non sono concluse,
  \errcode{ECANCELED} se sono state cancellate o il relativo codice di errore
  se sono fallite.}
\end{funcproto}

Se l'operazione non si è ancora completata viene sempre restituito l'errore di
\errcode{EINPROGRESS}, mentre se è stata cancellata ritorna
\errcode{ECANCELED}. La funzione ritorna zero quando l'operazione si è
conclusa con successo, altrimenti restituisce il codice dell'errore
verificatosi, ed esegue la corrispondente impostazione di \var{errno}. Il
codice può essere sia \errcode{EINVAL} ed \errcode{EBADF}, dovuti ad un valore
errato per \param{aiocbp}, che uno degli errori possibili durante l'esecuzione
dell'operazione di I/O richiesta, nel qual caso saranno restituiti, a seconda
del caso, i codici di errore delle \textit{system call} \func{read},
\func{write}, \func{fsync} e \func{fdatasync}.

Una volta che si sia certi che le operazioni siano state concluse (cioè dopo
che una chiamata ad \func{aio\_error} non ha restituito
\errcode{EINPROGRESS}), si potrà usare la funzione \funcd{aio\_return}, che
permette di verificare il completamento delle operazioni di I/O asincrono; il
suo prototipo è:

\begin{funcproto}{
\fhead{aio.h}
\fdecl{ssize\_t aio\_return(const struct aiocb *aiocbp)}
\fdesc{Ottiene lo stato dei risultati di una operazione di I/O asincrono.} 
}

{La funzione ritorna lo stato di uscita dell'operazione eseguita (il valore
  che avrebbero restituito le equivalenti funzioni eseguite in maniera
  sincrona).}
\end{funcproto}

La funzione recupera il valore dello stato di ritorno delle operazioni di I/O
associate a \param{aiocbp} e deve essere chiamata una sola volta per ciascuna
operazione asincrona, essa infatti fa sì che il sistema rilasci le risorse ad
essa associate. É per questo motivo che occorre chiamare la funzione solo dopo
che l'operazione cui \param{aiocbp} fa riferimento si è completata
verificandolo con \func{aio\_error}, ed usarla una sola volta. Una chiamata
precedente il completamento delle operazioni darebbe risultati indeterminati,
così come chiamarla più di una volta.

La funzione restituisce il valore di ritorno relativo all'operazione eseguita,
così come ricavato dalla sottostante \textit{system call} (il numero di byte
letti, scritti o il valore di ritorno di \func{fsync} o \func{fdatasync}).  É
importante chiamare sempre questa funzione, altrimenti le risorse disponibili
per le operazioni di I/O asincrono non verrebbero liberate, rischiando di
arrivare ad un loro esaurimento.

Oltre alle operazioni di lettura e scrittura l'interfaccia POSIX.1b mette a
disposizione un'altra operazione, quella di sincronizzazione dell'I/O,
compiuta dalla funzione \funcd{aio\_fsync}, che ha lo stesso effetto della
analoga \func{fsync}, ma viene eseguita in maniera asincrona; il suo prototipo
è:

\begin{funcproto}{
\fhead{aio.h}
\fdecl{int aio\_fsync(int op, struct aiocb *aiocbp)} 
\fdesc{Richiede la sincronizzazione dei dati su disco.} 
}

{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
  caso \var{errno} assumerà gli stessi valori visti \func{aio\_read} con lo
  stesso significato.
}
\end{funcproto}

La funzione richiede la sincronizzazione dei dati delle operazioni di I/O
relative al file descriptor indicato in \texttt{aiocbp->aio\_fildes},
ritornando immediatamente. Si tenga presente che la funzione mette
semplicemente in coda la richiesta, l'esecuzione effettiva della
sincronizzazione dovrà essere verificata con \func{aio\_error} e
\func{aio\_return} come per le operazioni di lettura e
scrittura. L'argomento \param{op} permette di indicare la modalità di
esecuzione, se si specifica il valore \const{O\_DSYNC} le operazioni saranno
completate con una chiamata a \func{fdatasync}, se si specifica
\const{O\_SYNC} con una chiamata a \func{fsync} (per i dettagli vedi
sez.~\ref{sec:file_sync}).

Il successo della chiamata assicura la richiesta di sincronizzazione dei dati
relativi operazioni di I/O asincrono richieste fino a quel momento, niente è
garantito riguardo la sincronizzazione dei dati relativi ad eventuali
operazioni richieste successivamente. Se si è specificato un meccanismo di
notifica questo sarà innescato una volta che le operazioni di sincronizzazione
dei dati saranno completate (\texttt{aio\_sigevent} è l'unico altro campo
di \param{aiocbp} che viene usato.

In alcuni casi può essere necessario interrompere le operazioni di I/O (in
genere quando viene richiesta un'uscita immediata dal programma), per questo
lo standard POSIX.1b prevede una funzione apposita, \funcd{aio\_cancel}, che
permette di cancellare una operazione richiesta in precedenza; il suo
prototipo è:

\begin{funcproto}{
\fhead{aio.h}
\fdecl{int aio\_cancel(int fd, struct aiocb *aiocbp)}
\fdesc{Richiede la cancellazione delle operazioni di I/O asincrono.} 
}

{La funzione ritorna un intero positivo che indica il risultato
  dell'operazione in caso di successo e $-1$ per un errore, nel qual caso
  \var{errno} assumerà uno dei valori:
  \begin{errlist}
  \item[\errcode{EBADF}] \param{fd} non è un file descriptor valido.
  \item[\errcode{ENOSYS}] la funzione non è implementata.
  \end{errlist}
}
\end{funcproto}

La funzione permette di cancellare una operazione specifica sul file
\param{fd}, idicata con \param{aiocbp}, o tutte le operazioni pendenti,
specificando \val{NULL} come valore di \param{aiocbp}. Quando una operazione
viene cancellata una successiva chiamata ad \func{aio\_error} riporterà
\errcode{ECANCELED} come codice di errore, ed mentre il valore di ritorno per
\func{aio\_return} sarà $-1$, inoltre il meccanismo di notifica non verrà
invocato. Se con \param{aiocbp} si specifica una operazione relativa ad un
file descriptor diverso da \param{fd} il risultato è indeterminato.  In caso
di successo, i possibili valori di ritorno per \func{aio\_cancel} (anch'essi
definiti in \headfile{aio.h}) sono tre:
\begin{basedescript}{\desclabelwidth{3.0cm}}
\item[\const{AIO\_ALLDONE}] indica che le operazioni di cui si è richiesta la
  cancellazione sono state già completate,
  
\item[\const{AIO\_CANCELED}] indica che tutte le operazioni richieste sono
  state cancellate,  
  
\item[\const{AIO\_NOTCANCELED}] indica che alcune delle operazioni erano in
  corso e non sono state cancellate.
\end{basedescript}

Nel caso si abbia \const{AIO\_NOTCANCELED} occorrerà chiamare
\func{aio\_error} per determinare quali sono le operazioni effettivamente
cancellate. Le operazioni che non sono state cancellate proseguiranno il loro
corso normale, compreso quanto richiesto riguardo al meccanismo di notifica
del loro avvenuto completamento.

Benché l'I/O asincrono preveda un meccanismo di notifica, l'interfaccia
fornisce anche una apposita funzione, \funcd{aio\_suspend}, che permette di
sospendere l'esecuzione del processo chiamante fino al completamento di una
specifica operazione; il suo prototipo è:

\begin{funcproto}{
\fhead{aio.h}
\fdecl{int aio\_suspend(const struct aiocb * const list[], int nent, \\
\phantom{int aio\_suspend(}const struct timespec *timeout)}
\fdesc{Attende il completamento di una operazione di I/O asincrono.} 
}

{La funzione ritorna $0$ se una (o più) operazioni sono state completate e
  $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei valori:
  \begin{errlist}
    \item[\errcode{EAGAIN}] nessuna operazione è stata completata entro
      \param{timeout}.
    \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale.
    \item[\errcode{ENOSYS}] la funzione non è implementata.
  \end{errlist}
}
\end{funcproto}
  
La funzione permette di bloccare il processo fintanto che almeno una delle
\param{nent} operazioni specificate nella lista \param{list} è completata, per
un tempo massimo specificato dalla struttura \struct{timespec} puntata
da \param{timout}, o fintanto che non arrivi un segnale (si tenga conto che
questo segnale potrebbe essere anche quello utilizzato come meccanismo di
notifica). La lista deve essere inizializzata con delle strutture
\struct{aiocb} relative ad operazioni effettivamente richieste, ma può
contenere puntatori nulli, che saranno ignorati. In caso si siano specificati
valori non validi l'effetto è indefinito.  
Un valore \val{NULL} per \param{timout} comporta l'assenza di timeout, mentre
se si vuole effettuare un \textit{polling} sulle operazioni occorrerà
specificare un puntatore valido ad una struttura \texttt{timespec} (vedi
fig.~\ref{fig:sys_timespec_struct}) contenente valori nulli, e verificare poi
con \func{aio\_error} quale delle operazioni della lista \param{list} è stata
completata.

Lo standard POSIX.1b infine ha previsto pure una funzione, \funcd{lio\_listio},
che permette di effettuare la richiesta di una intera lista di operazioni di
lettura o scrittura; il suo prototipo è:


\begin{funcproto}{
\fhead{aio.h}
\fdecl{int lio\_listio(int mode, struct aiocb * const list[], int nent, struct
    sigevent *sig)}

\fdesc{Richiede l'esecuzione di una serie di operazioni di I/O.} 
}

{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}] nessuna operazione è stata completata entro
      \param{timeout}.
    \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale.
    \item[\errcode{EINVAL}] si è passato un valore di \param{mode} non valido
      o un numero di operazioni \param{nent} maggiore di
      \const{AIO\_LISTIO\_MAX}.
    \item[\errcode{ENOSYS}] la funzione non è implementata.
  \end{errlist}
}
\end{funcproto}

La funzione esegue la richiesta delle \param{nent} operazioni indicate nella
lista \param{list} un vettore di puntatori a strutture \struct{aiocb}
indicanti le operazioni da compiere (che verranno eseguite senza un ordine
particolare). La lista può contenere anche puntatori nulli, che saranno
ignorati (si possono così eliminare facilmente componenti della lista senza
doverla rigenerare).

Ciascuna struttura \struct{aiocb} della lista deve contenere un
\textit{control block} opportunamente inizializzato; in particolare per
ognuna di esse dovrà essere specificato il tipo di operazione con il campo
\var{aio\_lio\_opcode}, che può prendere i valori:
\begin{basedescript}{\desclabelwidth{2.0cm}}
\item[\const{LIO\_READ}]  si richiede una operazione di lettura.
\item[\const{LIO\_WRITE}] si richiede una operazione di scrittura.
na operazione.
\end{basedescript}
dove \const{LIO\_NOP} viene usato quando si ha a che fare con un vettore di
dimensione fissa, per poter specificare solo alcune operazioni, o quando si
sono dovute cancellare delle operazioni e si deve ripetere la richiesta per
quelle non completate. 

L'argomento \param{mode} controlla il comportamento della funzione, se viene
usato il valore \const{LIO\_WAIT} la funzione si blocca fino al completamento
di tutte le operazioni richieste; se si usa \const{LIO\_NOWAIT} la funzione
ritorna immediatamente dopo aver messo in coda tutte le richieste. In tal caso
il chiamante può richiedere la notifica del completamento di tutte le
richieste, impostando l'argomento \param{sig} in maniera analoga a come si fa
per il campo \var{aio\_sigevent} di \struct{aiocb}.

% TODO: trattare libaio e le system call del kernel per l'I/O asincrono, vedi
% http://lse.sourceforge.net/io/aio.html,
% http://webfiveoh.com/content/guides/2012/aug/mon-13th/linux-asynchronous-io-and-libaio.html, 
% https://code.google.com/p/kernel/wiki/AIOUserGuide,
% http://bert-hubert.blogspot.de/2012/05/on-linux-asynchronous-file-io.html 


\section{Altre modalità di I/O avanzato}
\label{sec:file_advanced_io}

Oltre alle precedenti modalità di \textit{I/O multiplexing} e \textsl{I/O
  asincrono}, esistono altre funzioni che implementano delle modalità di
accesso ai file più evolute rispetto alle normali funzioni di lettura e
scrittura che abbiamo esaminato in sez.~\ref{sec:file_unix_interface}. In
questa sezione allora prenderemo in esame le interfacce per l'\textsl{I/O
  mappato in memoria}, per l'\textsl{I/O vettorizzato} e altre funzioni di I/O
avanzato.


\subsection{File mappati in memoria}
\label{sec:file_memory_map}

\itindbeg{memory~mapping}
Una modalità alternativa di I/O, che usa una interfaccia completamente diversa
rispetto a quella classica vista in sez.~\ref{sec:file_unix_interface}, è il
cosiddetto \textit{memory-mapped I/O}, che attraverso il meccanismo della
\textsl{paginazione} \index{paginazione} usato dalla memoria virtuale (vedi
sez.~\ref{sec:proc_mem_gen}) permette di \textsl{mappare} il contenuto di un
file in una sezione dello spazio di indirizzi del processo che lo ha allocato.

\begin{figure}[htb]
  \centering
  \includegraphics[width=12cm]{img/mmap_layout}
  \caption{Disposizione della memoria di un processo quando si esegue la
  mappatura in memoria di un file.}
  \label{fig:file_mmap_layout}
\end{figure}

Il meccanismo è illustrato in fig.~\ref{fig:file_mmap_layout}, una sezione del
file viene \textsl{mappata} direttamente nello spazio degli indirizzi del
programma.  Tutte le operazioni di lettura e scrittura su variabili contenute
in questa zona di memoria verranno eseguite leggendo e scrivendo dal contenuto
del file attraverso il sistema della memoria virtuale illustrato in
sez.~\ref{sec:proc_mem_gen} che in maniera analoga a quanto avviene per le
pagine che vengono salvate e rilette nella \textit{swap}, si incaricherà di
sincronizzare il contenuto di quel segmento di memoria con quello del file
mappato su di esso.  Per questo motivo si può parlare tanto di \textsl{file
  mappato in memoria}, quanto di \textsl{memoria mappata su file}.

L'uso del \textit{memory-mapping} comporta una notevole semplificazione delle
operazioni di I/O, in quanto non sarà più necessario utilizzare dei buffer
intermedi su cui appoggiare i dati da traferire, poiché questi potranno essere
acceduti direttamente nella sezione di memoria mappata; inoltre questa
interfaccia è più efficiente delle usuali funzioni di I/O, in quanto permette
di caricare in memoria solo le parti del file che sono effettivamente usate ad
un dato istante.

Infatti, dato che l'accesso è fatto direttamente attraverso la memoria
virtuale, la sezione di memoria mappata su cui si opera sarà a sua volta letta
o scritta sul file una pagina alla volta e solo per le parti effettivamente
usate, il tutto in maniera completamente trasparente al processo; l'accesso
alle pagine non ancora caricate avverrà allo stesso modo con cui vengono
caricate in memoria le pagine che sono state salvate sullo \textit{swap}.

Infine in situazioni in cui la memoria è scarsa, le pagine che mappano un file
vengono salvate automaticamente, così come le pagine dei programmi vengono
scritte sulla \textit{swap}; questo consente di accedere ai file su dimensioni
il cui solo limite è quello dello spazio di indirizzi disponibile, e non della
memoria su cui possono esserne lette delle porzioni.

L'interfaccia POSIX implementata da Linux prevede varie funzioni di sistema
per la gestione del \textit{memory mapped I/O}, la prima di queste, che serve
ad eseguire la mappatura in memoria di un file, è \funcd{mmap}; il suo
prototipo è:

\begin{funcproto}{
%\fhead{unistd.h}
\fhead{sys/mman.h} 
\fdecl{void * mmap(void * start, size\_t length, int prot, int flags, int
    fd, off\_t offset)}
\fdesc{Esegue la mappatura in memoria di una sezione di un file.} 
}

{La funzione ritorna il puntatore alla zona di memoria mappata in caso di
  successo, e \const{MAP\_FAILED} (\texttt{(void *) -1}) per un errore, nel
  qual caso \var{errno} assumerà uno dei valori:
  \begin{errlist}
    \item[\errcode{EACCES}] o \param{fd} non si riferisce ad un file regolare,
      o si è usato \const{MAP\_PRIVATE} ma \param{fd} non è aperto in lettura,
      o si è usato \const{MAP\_SHARED} e impostato \const{PROT\_WRITE} ed
      \param{fd} non è aperto in lettura/scrittura, o si è impostato
      \const{PROT\_WRITE} ed \param{fd} è in \textit{append-only}.
    \item[\errcode{EAGAIN}] il file è bloccato, o si è bloccata troppa memoria
      rispetto a quanto consentito dai limiti di sistema (vedi
      sez.~\ref{sec:sys_resource_limit}).
    \item[\errcode{EBADF}] il file descriptor non è valido, e non si è usato
      \const{MAP\_ANONYMOUS}.
    \item[\errcode{EINVAL}] i valori di \param{start}, \param{length} o
      \param{offset} non sono validi (o troppo grandi o non allineati sulla
      dimensione delle pagine), o \param{lengh} è zero (solo dal 2.6.12)
      o \param{flags} contiene sia \const{MAP\_PRIVATE} che
      \const{MAP\_SHARED} o nessuno dei due.
    \item[\errcode{ENFILE}] si è superato il limite del sistema sul numero di
      file aperti (vedi sez.~\ref{sec:sys_resource_limit}).
    \item[\errcode{ENODEV}] il filesystem di \param{fd} non supporta il memory
      mapping.
    \item[\errcode{ENOMEM}] non c'è memoria o si è superato il limite sul
      numero di mappature possibili.
    \item[\errcode{EOVERFLOW}] su architettura a 32 bit con il supporto per i
      \textit{large file} (che hanno una dimensione a 64 bit) il numero di
      pagine usato per \param{lenght} aggiunto a quello usato
      per \param{offset} eccede i 32 bit (\texttt{unsigned long}).
    \item[\errcode{EPERM}] l'argomento \param{prot} ha richiesto
      \const{PROT\_EXEC}, ma il filesystem di \param{fd} è montato con
      l'opzione \texttt{noexec}.
    \item[\errcode{ETXTBSY}] si è impostato \const{MAP\_DENYWRITE} ma
      \param{fd} è aperto in scrittura.
  \end{errlist}
}
\end{funcproto}

La funzione richiede di mappare in memoria la sezione del file \param{fd} a
partire da \param{offset} per \param{length} byte, preferibilmente
all'indirizzo \param{start}. Il valore \param{start} viene normalmente
considerato come un suggerimento, ma l'uso di un qualunque valore diverso da
\val{NULL}, in cui si rimette completamente al kernel la scelta
dell'indirizzo, viene sconsigliato per ragioni di portabilità. Il valore
di \param{offset} deve essere un multiplo della dimensione di una pagina di
memoria.

\begin{table}[htb]
  \centering
  \footnotesize
  \begin{tabular}[c]{|l|l|}
    \hline
    \textbf{Valore} & \textbf{Significato} \\
    \hline
    \hline
    \const{PROT\_EXEC}  & Le pagine possono essere eseguite.\\
    \const{PROT\_READ}  & Le pagine possono essere lette.\\
    \const{PROT\_WRITE} & Le pagine possono essere scritte.\\
    \const{PROT\_NONE}  & L'accesso alle pagine è vietato.\\
    \hline    
  \end{tabular}
  \caption{Valori dell'argomento \param{prot} di \func{mmap}, relativi alla
    protezione applicate alle pagine del file mappate in memoria.}
  \label{tab:file_mmap_prot}
\end{table}

Il valore dell'argomento \param{prot} indica la protezione\footnote{come
  accennato in sez.~\ref{sec:proc_memory} in Linux la memoria reale è divisa
  in pagine, ogni processo vede la sua memoria attraverso uno o più segmenti
  lineari di memoria virtuale; per ciascuno di questi segmenti il kernel
  mantiene nella \textit{page table} la mappatura sulle pagine di memoria
  reale, ed le modalità di accesso (lettura, esecuzione, scrittura); una loro
  violazione causa quella una \textit{segment violation}, e la relativa
  emissione del segnale \signal{SIGSEGV}.} da applicare al segmento di memoria
e deve essere specificato come maschera binaria ottenuta dall'OR di uno o più
dei valori riportati in tab.~\ref{tab:file_mmap_prot}; il valore specificato
deve essere compatibile con la modalità di accesso con cui si è aperto il
file.

\begin{table}[!htb]
  \centering
  \footnotesize
  \begin{tabular}[c]{|l|p{11cm}|}
    \hline
    \textbf{Valore} & \textbf{Significato} \\
    \hline
    \hline
    \const{MAP\_32BIT}     & Esegue la mappatura sui primi 2Gb dello spazio
                             degli indirizzi, viene supportato solo sulle
                             piattaforme \texttt{x86-64} per compatibilità con
                             le applicazioni a 32 bit. Viene ignorato se si è
                             richiesto \const{MAP\_FIXED} (dal kernel 2.4.20).\\
    \const{MAP\_ANON}      & Sinonimo di \const{MAP\_ANONYMOUS}, deprecato.\\
    \const{MAP\_ANONYMOUS} & La mappatura non è associata a nessun file. Gli
                             argomenti \param{fd} e \param{offset} sono
                             ignorati. L'uso di questo flag con
                             \const{MAP\_SHARED} è stato implementato in Linux
                             a partire dai kernel della serie 2.4.x.\\
    \const{MAP\_DENYWRITE} & In Linux viene ignorato per evitare
                             \textit{DoS}
                             (veniva usato per segnalare che tentativi di
                             scrittura sul file dovevano fallire con
                             \errcode{ETXTBSY}).\\ 
    \const{MAP\_EXECUTABLE}& Ignorato.\\
    \const{MAP\_FILE}      & Valore di compatibilità, ignorato.\\
    \const{MAP\_FIXED}     & Non permette di restituire un indirizzo diverso
                             da \param{start}, se questo non può essere usato
                             \func{mmap} fallisce. Se si imposta questo flag il
                             valore di \param{start} deve essere allineato
                             alle dimensioni di una pagina.\\
    \const{MAP\_GROWSDOWN} & Usato per gli \textit{stack}. 
                             Indica che la mappatura deve essere effettuata 
                             con gli indirizzi crescenti verso il basso.\\
    \const{MAP\_HUGETLB}   & Esegue la mappatura usando le cosiddette
                             ``\textit{huge pages}'' (dal kernel 2.6.32).\\
    \const{MAP\_LOCKED}    & Se impostato impedisce lo \textit{swapping} delle
                             pagine mappate (dal kernel 2.5.37).\\
    \const{MAP\_NONBLOCK}  & Esegue un \textit{prefaulting} più limitato che
                             non causa I/O (dal kernel 2.5.46).\\
    \const{MAP\_NORESERVE} & Si usa con \const{MAP\_PRIVATE}. Non riserva
                             delle pagine di \textit{swap} ad uso del meccanismo
                             del \textit{copy on write} 
                             per mantenere le modifiche fatte alla regione
                             mappata, in questo caso dopo una scrittura, se
                             non c'è più memoria disponibile, si ha
                             l'emissione di un \signal{SIGSEGV}.\\
    \const{MAP\_POPULATE}  & Esegue il \textit{prefaulting} delle pagine di
                             memoria necessarie alla mappatura (dal kernel
                             2.5.46).\\ 
    \const{MAP\_PRIVATE}   & I cambiamenti sulla memoria mappata non vengono
                             riportati sul file. Ne viene fatta una copia
                             privata cui solo il processo chiamante ha
                             accesso.  Incompatibile con \const{MAP\_SHARED}.\\
    \const{MAP\_SHARED}    & I cambiamenti sulla memoria mappata vengono
                             riportati sul file e saranno immediatamente
                             visibili agli altri processi che mappano lo stesso
                             file. Incompatibile
                             con \const{MAP\_PRIVATE}.\\ 
    \const{MAP\_STACK}     & Al momento è ignorato, è stato fornito (dal kernel
                             2.6.27) a supporto della implementazione dei
                             thread nelle \acr{glibc}, per allocare memoria in
                             uno spazio utilizzabile come \textit{stack} per le
                             architetture hardware che richiedono un
                             trattamento speciale di quest'ultimo.\\
    \const{MAP\_UNINITIALIZED}& Specifico per i sistemi embedded ed
                             utilizzabile dal kernel 2.6.33 solo se è stata
                             abilitata in fase di compilazione dello stesso
                             l'opzione
                             \texttt{CONFIG\_MMAP\_ALLOW\_UNINITIALIZED}. Se
                             usato le pagine di memoria usate nella mappatura
                             anonima non vengono cancellate; questo migliora
                             le prestazioni sui sistemi con risorse minime, ma
                             comporta la possibilità di rileggere i dati di
                             altri processi che han chiuso una mappatura, per
                             cui viene usato solo quando (come si suppone sia
                             per i sistemi embedded) si ha il completo
                             controllo dell'uso della memoria da parte degli
                             utenti.\\ 
%     \const{MAP\_DONTEXPAND}& Non consente una successiva espansione dell'area
%                              mappata con \func{mremap}, proposto ma pare non
%                              implementato.\\
    \hline
  \end{tabular}
  \caption{Valori possibili dell'argomento \param{flag} di \func{mmap}.}
  \label{tab:file_mmap_flag}
\end{table}

% TODO trattare MAP_HUGETLB introdotto con il kernel 2.6.32, e modifiche
% introdotte con il 3.8 per le dimensioni variabili delle huge pages

L'argomento \param{flags} specifica infine qual è il tipo di oggetto mappato,
le opzioni relative alle modalità con cui è effettuata la mappatura e alle
modalità con cui le modifiche alla memoria mappata vengono condivise o
mantenute private al processo che le ha effettuate. Deve essere specificato
come maschera binaria ottenuta dall'OR di uno o più dei valori riportati in
tab.~\ref{tab:file_mmap_flag}. Fra questi comunque deve sempre essere
specificato o \const{MAP\_PRIVATE} o \const{MAP\_SHARED} per indicare la
modalità con cui viene effettuata la mappatura.

Esistono infatti due modalità alternative di eseguire la mappatura di un file;
la più comune è \const{MAP\_SHARED} in cui la memoria è condivisa e le
modifiche effettuate su di essa sono visibili a tutti i processi che hanno
mappato lo stesso file. In questo caso le modifiche vengono anche riportate su
disco, anche se questo può non essere immediato a causa della bufferizzazione:
si potrà essere sicuri dell'aggiornamento solo in seguito alla chiamata di
\func{msync} o \func{munmap}, e solo allora le modifiche saranno visibili sul
file con l'I/O convenzionale.

Con \const{MAP\_PRIVATE} invece viene creata una copia privata del file,
questo non viene mai modificato e solo il processo chiamante ha accesso alla
mappatura. Le modifiche eseguite dal processo sulla mappatura vengono
effettuate utilizzando il meccanismo del \textit{copy on write}, mentenute in
memoria e salvate su \textit{swap} in caso di necessità.  Non è specificato se
i cambiamenti sul file originale vengano riportati sulla regione mappata.

Gli altri valori di \func{flag} modificano le caratteristiche della
mappatura. Fra questi il più rilevante è probabilmente \const{MAP\_ANONYMOUS}
che consente di creare segmenti di memoria condivisa fra processi diversi
senza appoggiarsi a nessun file (torneremo sul suo utilizzo in
sez.~\ref{sec:ipc_mmap_anonymous}). In tal caso gli argomenti \param{fd}
e \param{offset} vangono ignorati, anche se alcune implementazioni richiedono
che invece \param{fd} sia $-1$, convenzione che è opportuno seguire se si ha a
cuore la portabilità dei programmi.

Gli effetti dell'accesso ad una zona di memoria mappata su file possono essere
piuttosto complessi, essi si possono comprendere solo tenendo presente che
tutto quanto è comunque basato sul meccanismo della memoria virtuale. Questo
comporta allora una serie di conseguenze. La più ovvia è che se si cerca di
scrivere su una zona mappata in sola lettura si avrà l'emissione di un segnale
di violazione di accesso (\signal{SIGSEGV}), dato che i permessi sul segmento
di memoria relativo non consentono questo tipo di accesso.

È invece assai diversa la questione relativa agli accessi al di fuori della
regione di cui si è richiesta la mappatura. A prima vista infatti si potrebbe
ritenere che anch'essi debbano generare un segnale di violazione di accesso;
questo però non tiene conto del fatto che, essendo basata sul meccanismo della
paginazione, la mappatura in memoria non può che essere eseguita su un
segmento di dimensioni rigorosamente multiple di quelle di una pagina, ed in
generale queste potranno non corrispondere alle dimensioni effettive del file
o della sezione che si vuole mappare.

\begin{figure}[!htb] 
  \centering
  \includegraphics[height=6cm]{img/mmap_boundary}
  \caption{Schema della mappatura in memoria di una sezione di file di
    dimensioni non corrispondenti al bordo di una pagina.}
  \label{fig:file_mmap_boundary}
\end{figure}

Il caso più comune è quello illustrato in fig.~\ref{fig:file_mmap_boundary},
in cui la sezione di file non rientra nei confini di una pagina: in tal caso
il file sarà mappato su un segmento di memoria che si estende fino al
bordo della pagina successiva.  In questo caso è possibile accedere a quella
zona di memoria che eccede le dimensioni specificate da \param{length}, senza
ottenere un \signal{SIGSEGV} poiché essa è presente nello spazio di indirizzi
del processo, anche se non è mappata sul file. Il comportamento del sistema è
quello di restituire un valore nullo per quanto viene letto, e di non
riportare su file quanto viene scritto.

Un caso più complesso è quello che si viene a creare quando le dimensioni del
file mappato sono più corte delle dimensioni della mappatura, oppure quando il
file è stato troncato, dopo che è stato mappato, ad una dimensione inferiore a
quella della mappatura in memoria.  In questa situazione, per la sezione di
pagina parzialmente coperta dal contenuto del file, vale esattamente quanto
visto in precedenza; invece per la parte che eccede, fino alle dimensioni date
da \param{length}, l'accesso non sarà più possibile, ma il segnale emesso non
sarà \signal{SIGSEGV}, ma \signal{SIGBUS}, come illustrato in
fig.~\ref{fig:file_mmap_exceed}.

Non tutti i file possono venire mappati in memoria, dato che, come illustrato
in fig.~\ref{fig:file_mmap_layout}, la mappatura introduce una corrispondenza
biunivoca fra una sezione di un file ed una sezione di memoria. Questo
comporta che ad esempio non è possibile mappare in memoria file descriptor
relativi a \textit{pipe}, socket e \textit{fifo}, per i quali non ha senso
parlare di \textsl{sezione}. Lo stesso vale anche per alcuni file di
dispositivo, che non dispongono della relativa operazione \func{mmap} (si
ricordi quanto esposto in sez.~\ref{sec:file_vfs_work}). Si tenga presente
però che esistono anche casi di dispositivi (un esempio è l'interfaccia al
ponte PCI-VME del chip Universe) che sono utilizzabili solo con questa
interfaccia.

\begin{figure}[htb]
  \centering
  \includegraphics[height=6cm]{img/mmap_exceed}
  \caption{Schema della mappatura in memoria di file di dimensioni inferiori
    alla lunghezza richiesta.}
  \label{fig:file_mmap_exceed}
\end{figure}

Dato che passando attraverso una \func{fork} lo spazio di indirizzi viene
copiato integralmente, i file mappati in memoria verranno ereditati in maniera
trasparente dal processo figlio, mantenendo gli stessi attributi avuti nel
padre; così se si è usato \const{MAP\_SHARED} padre e figlio accederanno allo
stesso file in maniera condivisa, mentre se si è usato \const{MAP\_PRIVATE}
ciascuno di essi manterrà una sua versione privata indipendente. Non c'è
invece nessun passaggio attraverso una \func{exec}, dato che quest'ultima
sostituisce tutto lo spazio degli indirizzi di un processo con quello di un
nuovo programma.

Quando si effettua la mappatura di un file vengono pure modificati i tempi ad
esso associati (di cui si è trattato in sez.~\ref{sec:file_file_times}). Il
valore di \var{st\_atime} può venir cambiato in qualunque istante a partire
dal momento in cui la mappatura è stata effettuata: il primo riferimento ad
una pagina mappata su un file aggiorna questo tempo.  I valori di
\var{st\_ctime} e \var{st\_mtime} possono venir cambiati solo quando si è
consentita la scrittura sul file (cioè per un file mappato con
\const{PROT\_WRITE} e \const{MAP\_SHARED}) e sono aggiornati dopo la scrittura
o in corrispondenza di una eventuale \func{msync}.

Dato per i file mappati in memoria le operazioni di I/O sono gestite
direttamente dalla memoria virtuale, occorre essere consapevoli delle
interazioni che possono esserci con operazioni effettuate con l'interfaccia
dei file ordinaria illustrata in sez.~\ref{sec:file_unix_interface}. Il
problema è che una volta che si è mappato un file, le operazioni di lettura e
scrittura saranno eseguite sulla memoria, e riportate su disco in maniera
autonoma dal sistema della memoria virtuale.

Pertanto se si modifica un file con l'interfaccia ordinaria queste modifiche
potranno essere visibili o meno a seconda del momento in cui la memoria
virtuale trasporterà dal disco in memoria quella sezione del file, perciò è
del tutto imprevedibile il risultato della modifica di un file nei confronti
del contenuto della memoria su cui è mappato.

Per questo è sempre sconsigliabile eseguire scritture su un file attraverso
l'interfaccia ordinaria quando lo si è mappato in memoria, è invece possibile
usare l'interfaccia ordinaria per leggere un file mappato in memoria, purché
si abbia una certa cura; infatti l'interfaccia dell'I/O mappato in memoria
mette a disposizione la funzione \funcd{msync} per sincronizzare il contenuto
della memoria mappata con il file su disco; il suo prototipo è:

\begin{funcproto}{
%\fhead{unistd.h}
\fhead{sys/mman.h}
\fdecl{int msync(const void *start, size\_t length, int flags)}
\fdesc{Sincronizza i contenuti di una sezione di un file mappato in memoria.} 
}

{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{EBUSY}] si è indicato \const{MS\_INVALIDATE} ma
      nell'intervallo di memoria specificato è presente un \textit{memory lock}.
    \item[\errcode{EFAULT}] l'intervallo indicato, o parte di esso, non
      risulta mappato (prima del kernel 2.4.19).
    \item[\errcode{EINVAL}] o \param{start} non è multiplo di
      \const{PAGE\_SIZE}, o si è specificato un valore non valido per
      \param{flags}.
    \item[\errcode{ENOMEM}] l'intervallo indicato, o parte di esso, non
      risulta mappato (dal kernel 2.4.19).
  \end{errlist}
}
\end{funcproto}

La funzione esegue la sincronizzazione di quanto scritto nella sezione di
memoria indicata da \param{start} e \param{offset}, scrivendo le modifiche sul
file (qualora questo non sia già stato fatto).  Provvede anche ad aggiornare i
relativi tempi di modifica. In questo modo si è sicuri che dopo l'esecuzione
di \func{msync} le funzioni dell'interfaccia ordinaria troveranno un contenuto
del file aggiornato.


\begin{table}[htb]
  \centering
  \footnotesize
  \begin{tabular}[c]{|l|p{11cm}|}
    \hline
    \textbf{Valore} & \textbf{Significato} \\
    \hline
    \hline
    \const{MS\_SYNC}       & richiede una sincronizzazione e ritorna soltanto
                             quando questa è stata completata.\\
    \const{MS\_ASYNC}      & richiede una sincronizzazione, ma ritorna subito 
                             non attendendo che questa sia finita.\\
    \const{MS\_INVALIDATE} & invalida le pagine per tutte le mappature
                             in memoria così da rendere necessaria una
                             rilettura immediata delle stesse.\\
    \hline
  \end{tabular}
  \caption{Valori possibili dell'argomento \param{flag} di \func{msync}.}
  \label{tab:file_mmap_msync}
\end{table}

L'argomento \param{flag} è specificato come maschera binaria composta da un OR
dei valori riportati in tab.~\ref{tab:file_mmap_msync}, di questi però
\const{MS\_ASYNC} e \const{MS\_SYNC} sono incompatibili; con il primo valore
infatti la funzione si limita ad inoltrare la richiesta di sincronizzazione al
meccanismo della memoria virtuale, ritornando subito, mentre con il secondo
attende che la sincronizzazione sia stata effettivamente eseguita. Il terzo
valore fa sì che vengano invalidate, per tutte le mappature dello stesso file,
le pagine di cui si è richiesta la sincronizzazione, così che esse possano
essere immediatamente aggiornate con i nuovi valori.

Una volta che si sono completate le operazioni di I/O si può eliminare la
mappatura della memoria usando la funzione \funcd{munmap}, il suo prototipo è:

\begin{funcproto}{
%\fhead{unistd.h}
\fhead{sys/mman.h}
\fdecl{int munmap(void *start, size\_t length)}
\fdesc{Rilascia la mappatura sulla sezione di memoria specificata.} 
}

{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{EINVAL}] l'intervallo specificato non ricade in una zona
      precedentemente mappata.
  \end{errlist}
}
\end{funcproto}

La funzione cancella la mappatura per l'intervallo specificato con
\param{start} e \param{length}; ogni successivo accesso a tale regione causerà
un errore di accesso in memoria. L'argomento \param{start} deve essere
allineato alle dimensioni di una pagina, e la mappatura di tutte le pagine
contenute anche parzialmente nell'intervallo indicato, verrà rimossa.
Indicare un intervallo che non contiene mappature non è un errore.  Si tenga
presente inoltre che alla conclusione di un processo ogni pagina mappata verrà
automaticamente rilasciata, mentre la chiusura del file descriptor usato per
il \textit{memory mapping} non ha alcun effetto su di esso.

Lo standard POSIX prevede anche una funzione che permetta di cambiare le
protezioni delle pagine di memoria; lo standard prevede che essa si applichi
solo ai \textit{memory mapping} creati con \func{mmap}, ma nel caso di Linux
la funzione può essere usata con qualunque pagina valida nella memoria
virtuale. Questa funzione di sistema è \funcd{mprotect} ed il suo prototipo è:

\begin{funcproto}{
\fhead{sys/mman.h} 
\fdecl{int mprotect(const void *addr, size\_t len, int prot)}
\fdesc{Modifica le protezioni delle pagine di memoria.} 
}

{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{EINVAL}] il valore di \param{addr} non è valido o non è un
      multiplo di \const{PAGE\_SIZE}.
    \item[\errcode{EACCES}] l'operazione non è consentita, ad esempio si è
      cercato di marcare con \const{PROT\_WRITE} un segmento di memoria cui si
      ha solo accesso in lettura.
    \item[\errcode{ENOMEM}] non è stato possibile allocare le risorse
      necessarie all'interno del kernel o si è specificato un indirizzo di
      memoria non valido del processo o non corrispondente a pagine mappate
      (negli ultimi due casi prima del kernel 2.4.19 veniva prodotto,
      erroneamente, \errcode{EFAULT}).
  \end{errlist}
}
\end{funcproto}

La funzione prende come argomenti un indirizzo di partenza in \param{addr},
allineato alle dimensioni delle pagine di memoria, ed una dimensione
\param{size}. La nuova protezione deve essere specificata in \param{prot} con
una combinazione dei valori di tab.~\ref{tab:file_mmap_prot}.  La nuova
protezione verrà applicata a tutte le pagine contenute, anche parzialmente,
dall'intervallo fra \param{addr} e \param{addr}+\param{size}-1.

Infine Linux supporta alcune operazioni specifiche non disponibili su altri
kernel unix-like per poter usare le quali occorre però dichiarare
\macro{\_GNU\_SOURCE} prima dell'inclusione di \texttt{sys/mman.h}. La prima
di queste è la possibilità di modificare un precedente \textit{memory
  mapping}, ad esempio per espanderlo o restringerlo.  Questo è realizzato
dalla funzione di sistema \funcd{mremap}, il cui prototipo è:

\begin{funcproto}{
\fhead{sys/mman.h} 
\fdecl{void * mremap(void *old\_address, size\_t old\_size , size\_t
    new\_size, unsigned long flags)}
\fdesc{Restringe o allarga una mappatura in memoria.} 
}

{La funzione ritorna l'indirizzo alla nuova area di memoria in caso di
  successo o il valore \const{MAP\_FAILED} (pari a \texttt{(void *) -1}), nel
  qual caso \var{errno} assumerà uno dei valori:
  \begin{errlist}
    \item[\errcode{EINVAL}] il valore di \param{old\_address} non è un
      puntatore valido.
    \item[\errcode{EFAULT}] ci sono indirizzi non validi nell'intervallo
      specificato da \param{old\_address} e \param{old\_size}, o ci sono altre
      mappature di tipo non corrispondente a quella richiesta.
    \item[\errcode{ENOMEM}] non c'è memoria sufficiente oppure l'area di
      memoria non può essere espansa all'indirizzo virtuale corrente, e non si
      è specificato \const{MREMAP\_MAYMOVE} nei flag.
    \item[\errcode{EAGAIN}] il segmento di memoria scelto è bloccato e non può
      essere rimappato.
  \end{errlist}
}
\end{funcproto}

La funzione richiede come argomenti \param{old\_address} (che deve essere
allineato alle dimensioni di una pagina di memoria) che specifica il
precedente indirizzo del \textit{memory mapping} e \param{old\_size}, che ne
indica la dimensione. Con \param{new\_size} si specifica invece la nuova
dimensione che si vuole ottenere. Infine l'argomento \param{flags} è una
maschera binaria per i flag che controllano il comportamento della funzione.
Il solo valore utilizzato è \const{MREMAP\_MAYMOVE} che consente di eseguire
l'espansione anche quando non è possibile utilizzare il precedente
indirizzo. Per questo motivo, se si è usato questo flag, la funzione può
restituire un indirizzo della nuova zona di memoria che non è detto coincida
con \param{old\_address}.

La funzione si appoggia al sistema della memoria virtuale per modificare
l'associazione fra gli indirizzi virtuali del processo e le pagine di memoria,
modificando i dati direttamente nella \textit{page table} del processo. Come
per \func{mprotect} la funzione può essere usata in generale, anche per pagine
di memoria non corrispondenti ad un \textit{memory mapping}, e consente così
di implementare la funzione \func{realloc} in maniera molto efficiente.

Una caratteristica comune a tutti i sistemi unix-like è che la mappatura in
memoria di un file viene eseguita in maniera lineare, cioè parti successive di
un file vengono mappate linearmente su indirizzi successivi in memoria.
Esistono però delle applicazioni (in particolare la tecnica è usata dai
database o dai programmi che realizzano macchine virtuali) in cui è utile
poter mappare sezioni diverse di un file su diverse zone di memoria.

Questo è ovviamente sempre possibile eseguendo ripetutamente la funzione
\func{mmap} per ciascuna delle diverse aree del file che si vogliono mappare
in sequenza non lineare (ed in effetti è quello che veniva fatto anche con
Linux prima che fossero introdotte queste estensioni) ma questo approccio ha
delle conseguenze molto pesanti in termini di prestazioni.  Infatti per
ciascuna mappatura in memoria deve essere definita nella \textit{page table}
del processo una nuova area di memoria virtuale, quella che nel gergo del
kernel viene chiamata VMA (\textit{virtual memory area}, che corrisponda alla
mappatura, in modo che questa diventi visibile nello spazio degli indirizzi
come illustrato in fig.~\ref{fig:file_mmap_layout}.

Quando un processo esegue un gran numero di mappature diverse (si può arrivare
anche a centinaia di migliaia) per realizzare a mano una mappatura non-lineare
esso vedrà un accrescimento eccessivo della sua \textit{page table}, e lo
stesso accadrà per tutti gli altri processi che utilizzano questa tecnica. In
situazioni in cui le applicazioni hanno queste esigenze si avranno delle
prestazioni ridotte, dato che il kernel dovrà impiegare molte risorse per
mantenere i dati relativi al \textit{memory mapping}, sia in termini di
memoria interna per i dati delle \textit{page table}, che di CPU per il loro
aggiornamento.

Per questo motivo con il kernel 2.5.46 è stato introdotto, ad opera di Ingo
Molnar, un meccanismo che consente la mappatura non-lineare. Anche questa è
una caratteristica specifica di Linux, non presente in altri sistemi
unix-like.  Diventa così possibile utilizzare una sola mappatura iniziale, e
quindi una sola \textit{virtual memory area} nella \textit{page table} del
processo, e poi rimappare a piacere all'interno di questa i dati del file. Ciò
è possibile grazie ad una nuova \textit{system call},
\funcd{remap\_file\_pages}, il cui prototipo è:

\begin{funcproto}{
\fhead{sys/mman.h} 
\fdecl{int remap\_file\_pages(void *start, size\_t size, int prot,
    ssize\_t pgoff, int flags)}
\fdesc{Rimappa non linearmente un \textit{memory mapping}.} 
}

{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{EINVAL}] si è usato un valore non valido per uno degli
      argomenti o \param{start} non fa riferimento ad un \textit{memory
        mapping} valido creato con \const{MAP\_SHARED}.
  \end{errlist}
  ed inoltre 
 nel loro significato generico.}
\end{funcproto}

Per poter utilizzare questa funzione occorre anzitutto effettuare
preliminarmente una chiamata a \func{mmap} con \const{MAP\_SHARED} per
definire l'area di memoria che poi sarà rimappata non linearmente. Poi si
chiamerà questa funzione per modificare le corrispondenze fra pagine di
memoria e pagine del file; si tenga presente che \func{remap\_file\_pages}
permette anche di mappare la stessa pagina di un file in più pagine della
regione mappata.

La funzione richiede che si identifichi la sezione del file che si vuole
riposizionare all'interno del \textit{memory mapping} con gli argomenti
\param{pgoff} e \param{size}; l'argomento \param{start} invece deve indicare
un indirizzo all'interno dell'area definita dall'\func{mmap} iniziale, a
partire dal quale la sezione di file indicata verrà rimappata. L'argomento
\param{prot} deve essere sempre nullo, mentre \param{flags} prende gli stessi
valori di \func{mmap} (quelli di tab.~\ref{tab:file_mmap_prot}) ma di tutti i
flag solo \const{MAP\_NONBLOCK} non viene ignorato.

\itindbeg{prefaulting} 

Insieme alla funzione \func{remap\_file\_pages} nel kernel 2.5.46 con sono
stati introdotti anche due nuovi flag per \func{mmap}: \const{MAP\_POPULATE} e
\const{MAP\_NONBLOCK}.  Il primo dei due consente di abilitare il meccanismo
del \textit{prefaulting}. Questo viene di nuovo in aiuto per migliorare le
prestazioni in certe condizioni di utilizzo del \textit{memory mapping}.

Il problema si pone tutte le volte che si vuole mappare in memoria un file di
grosse dimensioni. Il comportamento normale del sistema della memoria virtuale
è quello per cui la regione mappata viene aggiunta alla \textit{page table}
del processo, ma i dati verranno effettivamente utilizzati (si avrà cioè un
\textit{page fault} che li trasferisce dal disco alla memoria) soltanto in
corrispondenza dell'accesso a ciascuna delle pagine interessate dal
\textit{memory mapping}.

Questo vuol dire che il passaggio dei dati dal disco alla memoria avverrà una
pagina alla volta con un gran numero di \textit{page fault}, chiaramente se si
sa in anticipo che il file verrà utilizzato immediatamente, è molto più
efficiente eseguire un \textit{prefaulting} in cui tutte le pagine di memoria
interessate alla mappatura vengono ``\textsl{popolate}'' in una sola volta,
questo comportamento viene abilitato quando si usa con \func{mmap} il flag
\const{MAP\_POPULATE}.

Dato che l'uso di \const{MAP\_POPULATE} comporta dell'I/O su disco che può
rallentare l'esecuzione di \func{mmap} è stato introdotto anche un secondo
flag, \const{MAP\_NONBLOCK}, che esegue un \textit{prefaulting} più limitato
in cui vengono popolate solo le pagine della mappatura che già si trovano
nella cache del kernel.\footnote{questo può essere utile per il linker
  dinamico, in particolare quando viene effettuato il \textit{prelink} delle
  applicazioni.}

\itindend{prefaulting}

Per i vantaggi illustrati all'inizio del paragrafo l'interfaccia del
\textit{memory mapped I/O} viene usata da una grande varietà di programmi,
spesso con esigenze molto diverse fra di loro riguardo le modalità con cui
verranno eseguiti gli accessi ad un file; è ad esempio molto comune per i
database effettuare accessi ai dati in maniera pressoché casuale, mentre un
riproduttore audio o video eseguirà per lo più letture sequenziali.

\itindend{memory~mapping}

Per migliorare le prestazioni a seconda di queste modalità di accesso è
disponibile una apposita funzione, \funcd{madvise},\footnote{tratteremo in
  sez.~\ref{sec:file_fadvise} le funzioni che consentono di ottimizzare
  l'accesso ai file con l'interfaccia classica.} che consente di fornire al
kernel delle indicazioni su come un processo intende accedere ad un segmento
di memoria, anche al di là delle mappature dei file, così che possano essere
adottate le opportune strategie di ottimizzazione. Il suo prototipo è:

\begin{funcproto}{
\fhead{sys/mman.h}
\fdecl{int madvise(void *start, size\_t length, int advice)}
\fdesc{Fornisce indicazioni sull'uso previsto di un segmento di memoria.} 
}

{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}] la mappatura esiste ma non corrisponde ad un file.
    \item[\errcode{EINVAL}] \param{start} non è allineato alla dimensione di
      una pagina, \param{length} ha un valore negativo, o \param{advice} non è
      un valore valido, o si è richiesto il rilascio (con
      \const{MADV\_DONTNEED}) di pagine bloccate o condivise o si è usato
      \const{MADV\_MERGEABLE} o \const{MADV\_UNMERGEABLE} ma il kernel non è
      stato compilato per il relativo supporto.
    \item[\errcode{EIO}] la paginazione richiesta eccederebbe i limiti (vedi
      sez.~\ref{sec:sys_resource_limit}) sulle pagine residenti in memoria del
      processo (solo in caso di \const{MADV\_WILLNEED}).
    \item[\errcode{ENOMEM}] gli indirizzi specificati non sono mappati, o, in
      caso \const{MADV\_WILLNEED}, non c'è sufficiente memoria per soddisfare
      la richiesta.
  \end{errlist}
  ed inoltre \errval{EAGAIN} e \errval{ENOSYS} nel loro significato generico.}
\end{funcproto}

La sezione di memoria sulla quale si intendono fornire le indicazioni deve
essere indicata con l'indirizzo iniziale \param{start} e l'estensione
\param{length}, il valore di \param{start} deve essere allineato,
mentre \param{length} deve essere un numero positivo; la versione di Linux
consente anche un valore nullo per \param{length}, inoltre se una parte
dell'intervallo non è mappato in memoria l'indicazione viene comunque
applicata alle restanti parti, anche se la funzione ritorna un errore di
\errval{ENOMEM}.

L'indicazione viene espressa dall'argomento \param{advice} che deve essere
specificato con uno dei valori riportati in
tab.~\ref{tab:madvise_advice_values}; si tenga presente che i valori indicati
nella seconda parte della tabella sono specifici di Linux e non sono previsti
dallo standard POSIX.1b.  La funzione non ha, tranne il caso di
\const{MADV\_DONTFORK}, nessun effetto sul comportamento di un programma, ma
può influenzarne le prestazioni fornendo al kernel indicazioni sulle esigenze
dello stesso, così che sia possibile scegliere le opportune strategie per la
gestione del \textit{read-ahead} (vedi sez.~\ref{sec:file_fadvise}) e del
caching dei dati.

\begin{table}[!htb]
  \centering
  \footnotesize
  \begin{tabular}[c]{|l|p{10 cm}|}
    \hline
    \textbf{Valore} & \textbf{Significato} \\
    \hline
    \hline
    \const{MADV\_DONTNEED}& non ci si aspetta nessun accesso nell'immediato
                            futuro, pertanto le pagine possono essere
                            liberate dal kernel non appena necessario; l'area
                            di memoria resterà accessibile, ma un accesso
                            richiederà che i dati vengano ricaricati dal file
                            a cui la mappatura fa riferimento.\\
    \const{MADV\_NORMAL}  & nessuna indicazione specifica, questo è il valore
                            di default usato quando non si è chiamato
                            \func{madvise}.\\
    \const{MADV\_RANDOM}  & ci si aspetta un accesso casuale all'area
                            indicata, pertanto l'applicazione di una lettura
                            anticipata con il meccanismo del
                            \textit{read-ahead} (vedi 
                            sez.~\ref{sec:file_fadvise}) è di
                            scarsa utilità e verrà disabilitata.\\
    \const{MADV\_SEQUENTIAL}& ci si aspetta un accesso sequenziale al file,
                            quindi da una parte sarà opportuno eseguire una
                            lettura anticipata, e dall'altra si potranno
                            scartare immediatamente le pagine una volta che
                            queste siano state lette.\\
    \const{MADV\_WILLNEED}& ci si aspetta un accesso nell'immediato futuro,
                            pertanto l'applicazione del \textit{read-ahead}
                            deve essere incentivata.\\
    \hline
    \const{MADV\_DONTDUMP}& esclude da un \textit{core dump} (vedi
                            sez.~\ref{sec:sig_standard}) le pagine 
                            specificate, viene usato per evitare di scrivere
                            su disco dati relativi a zone di memoria che si sa
                            non essere utili in un \textit{core dump}.\\
    \const{MADV\_DODUMP}  & rimuove l'effetto della precedente
                            \const{MADV\_DONTDUMP} (dal kernel 3.4).\\ 
    \const{MADV\_DONTFORK}& impedisce che l'intervallo specificato venga
                            ereditato dal processo figlio dopo una
                            \func{fork}; questo consente di evitare che il
                            meccanismo del \textit{copy on write} effettui la
                            rilocazione delle pagine quando il padre scrive
                            sull'area di memoria dopo la \func{fork}, cosa che
                            può causare problemi per l'hardware che esegue
                            operazioni in DMA su quelle pagine (dal kernel
                            2.6.16).\\
    \const{MADV\_DOFORK}  & rimuove l'effetto della precedente
                            \const{MADV\_DONTFORK} (dal kernel 2.6.16).\\ 
    \const{MADV\_HUGEPAGE}& abilita il meccanismo delle \textit{Transparent
                              Huge Page} (vedi sez.~\ref{sec:huge_pages})
                            sulla regione indicata; se questa è allineata
                            alle relative dimensioni il kernel alloca
                            direttamente delle \textit{huge page}; è
                            utilizzabile solo con mappature anomime private
                            (dal kernel 2.6.38).\\
    \const{MADV\_NOHUGEPAGE}& impedisce che la regione indicata venga
                            collassata in eventuali \textit{huge page} (dal
                            kernel 2.6.38).\\
    \const{MADV\_HWPOISON} &opzione ad uso di debug per verificare codice
                            che debba gestire errori nella gestione della
                            memoria; richiede una apposita opzione di
                            compilazione del kernel, privilegi amministrativi
                            (la capacità \const{CAP\_SYS\_ADMIN}) e provoca
                            l'emissione di un segnale di \const{SIGBUS} dal
                            programma chiamante e rimozione della mappatura
                            (dal kernel 2.6.32).\\
    \const{MADV\_SOFT\_OFFLINE}&opzione utilizzata per il debug del
                            codice di verifica degli errori di gestione
                            memoria, richiede una apposita opzione di
                            compilazione (dal kernel 2.6.33).\\
    \const{MADV\_MERGEABLE}& marca la pagina come accorpabile, indicazione
                            principalmente ad uso dei sistemi di
                            virtualizzazione\footnotemark (dal kernel 2.6.32).\\
    \const{MADV\_REMOVE}  & libera un intervallo di pagine di memoria ed il
                            relativo supporto sottostante; è supportato
                            soltanto sui filesystem in RAM \textit{tmpfs} e
                            \textit{shmfs} se usato su altri tipi di
                            filesystem causa un errore di \errcode{ENOSYS}
                            (dal kernel 2.6.16).\\
    \const{MADV\_UNMERGEABLE}& rimuove l'effetto della precedente
                            \const{MADV\_MERGEABLE} (dal kernel 2.6.32). \\
     \hline
  \end{tabular}
  \caption{Valori dell'argomento \param{advice} di \func{madvise}.}
  \label{tab:madvise_advice_values}
\end{table}

\footnotetext{a partire dal kernel 2.6.32 è stato introdotto un meccanismo che
  identifica pagine di memoria identiche e le accorpa in una unica pagina
  (soggetta al \textit{copy-on-write} per successive modifiche); per evitare
  di controllare tutte le pagine solo quelle marcate con questo flag vengono
  prese in considerazione per l'accorpamento; in questo modo si possono
  migliorare le prestazioni nella gestione delle macchine virtuali diminuendo
  la loro occupazione di memoria, ma il meccanismo può essere usato anche in
  altre applicazioni in cui sian presenti numerosi processi che usano gli
  stessi dati; per maggiori dettagli si veda
  \href{http://kernelnewbies.org/Linux_2_6_32\#head-d3f32e41df508090810388a57efce73f52660ccb}{\texttt{http://kernelnewbies.org/Linux\_2\_6\_32}}
  e la documentazione nei sorgenti del kernel
  (\texttt{Documentation/vm/ksm.txt}).} 


A differenza da quanto specificato nello standard POSIX.1b, per il quale l'uso
di \func{madvise} è a scopo puramente indicativo, Linux considera queste
richieste come imperative, per cui ritorna un errore qualora non possa
soddisfarle; questo comportamento differisce da quanto specificato nello
standard.

Nello standard POSIX.1-2001 è prevista una ulteriore funzione
\funcd{posix\_madvise} che su Linux viene reimplementata utilizzando
\func{madvise}; il suo prototipo è:

\begin{funcproto}{
\fhead{sys/mman.h}
\fdecl{int posix\_madvise(void *start, size\_t lenght, int advice)}
\fdesc{Fornisce indicazioni sull'uso previsto di un segmento di memoria.} 
}

{La funzione ritorna $0$ in caso di successo ed un valore positivo per un
  errore, nel qual caso \var{errno} assumerà uno dei valori:
  \begin{errlist}
    \item[\errcode{EINVAL}] \param{start} non è allineato alla dimensione di
      una pagina, \param{length} ha un valore negativo, o \param{advice} non è
      un valore valido.
    \item[\errcode{ENOMEM}] gli indirizzi specificati non sono nello spazio di
      indirizzi del processo.
  \end{errlist}
}
\end{funcproto}

Gli argomenti \param{start} e \param{lenght} hanno lo stesso identico
significato degli analoghi di \func{madvise}, a cui si rimanda per la loro
descrizione ma a differenza di quanto indicato dallo standard per questa
funzione, su Linux un valore nullo di \param{len} è consentito.

\begin{table}[!htb]
  \centering
  \footnotesize
  \begin{tabular}[c]{|l|l|}
    \hline
    \textbf{Valore} & \textbf{Significato} \\
    \hline
    \hline
    \const{POSIX\_MADV\_DONTNEED}& analogo a \const{MADV\_DONTNEED}.\\
    \const{POSIX\_MADV\_NORMAL}  & identico a \const{MADV\_NORMAL}.\\
    \const{POSIX\_MADV\_RANDOM}  & identico a \const{MADV\_RANDOM}.\\
    \const{POSIX\_MADV\_SEQUENTIAL}& identico a \const{MADV\_SEQUENTIAL}.\\
    \const{POSIX\_MADV\_WILLNEED}& identico a \const{MADV\_WILLNEED}.\\
     \hline
  \end{tabular}
  \caption{Valori dell'argomento \param{advice} di \func{posix\_madvise}.}
  \label{tab:posix_madvise_advice_values}
\end{table}


L'argomento \param{advice} invece può assumere solo i valori indicati in
tab.~\ref{tab:posix_madvise_advice_values}, che riflettono gli analoghi di
\func{madvise}, con lo stesso effetto per tutti tranne
\const{POSIX\_MADV\_DONTNEED}.  Infatti a partire dalle \acr{glibc} 2.6
\const{POSIX\_MADV\_DONTNEED} viene ignorato, in quanto l'uso del
corrispondente \const{MADV\_DONTNEED} di \func{madvise} ha, per la semantica
imperativa, l'effetto immediato di far liberare le pagine da parte del kernel,
che viene considerato distruttivo.



\subsection{I/O vettorizzato: \func{readv} e \func{writev}}
\label{sec:file_multiple_io}

Una seconda modalità di I/O diversa da quella ordinaria è il cosiddetto
\textsl{I/O vettorizzato}, che nasce per rispondere al caso abbastanza comune
in cui ci si trova nell'esigenza di dover eseguire una serie multipla di
operazioni di I/O, come una serie di letture o scritture di vari buffer. Un
esempio tipico è quando i dati sono strutturati nei campi di una struttura ed
essi devono essere caricati o salvati su un file.  Benché l'operazione sia
facilmente eseguibile attraverso una serie multipla di chiamate a \func{read}
e \func{write}, ci sono casi in cui si vuole poter contare sulla atomicità
delle operazioni di lettura e scrittura rispetto all'esecuzione del programma.

Per questo motivo fino da BSD 4.2 vennero introdotte delle nuove
\textit{system call} che permettessero di effettuare con una sola chiamata una
serie di letture da, o scritture su, una serie di buffer, quello che poi venne
chiamato \textsl{I/O vettorizzato}. Queste funzioni di sistema sono
\funcd{readv} e \funcd{writev},\footnote{in Linux le due funzioni sono riprese
  da BSD4.4, esse sono previste anche dallo standard POSIX.1-2001.} ed i
relativi prototipi sono:


\begin{funcproto}{
\fhead{sys/uio.h}
\fdecl{int readv(int fd, const struct iovec *vector, int count)}
\fdecl{int writev(int fd, const struct iovec *vector, int count)}
\fdesc{Eseguono rispettivamente una lettura o una scrittura vettorizzata.} 
}

{Le funzioni ritornano il numero di byte letti o scritti 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 non valido per uno degli
    argomenti (ad esempio \param{count} è maggiore di \const{IOV\_MAX}).
  \end{errlist}
  più tutti i valori, con lo stesso significato, che possono risultare
  dalle condizioni di errore di \func{read} e \func{write}.
 }
\end{funcproto}


Entrambe le funzioni usano una struttura \struct{iovec}, la cui definizione è
riportata in fig.~\ref{fig:file_iovec}, che definisce dove i dati devono
essere letti o scritti ed in che quantità. Il primo campo della struttura,
\var{iov\_base}, contiene l'indirizzo del buffer ed il secondo,
\var{iov\_len}, la dimensione dello stesso.

\begin{figure}[!htb]
  \footnotesize \centering
  \begin{minipage}[c]{\textwidth}
    \includestruct{listati/iovec.h}
  \end{minipage} 
  \normalsize 
  \caption{La struttura \structd{iovec}, usata dalle operazioni di I/O
    vettorizzato.} 
  \label{fig:file_iovec}
\end{figure}

La lista dei buffer da utilizzare viene indicata attraverso l'argomento
\param{vector} che è un vettore di strutture \struct{iovec}, la cui lunghezza
è specificata dall'argomento \param{count}.\footnote{fino alle libc5, Linux
  usava \type{size\_t} come tipo dell'argomento \param{count}, una scelta
  logica, che però è stata dismessa per restare aderenti allo standard
  POSIX.1-2001.}  Ciascuna struttura dovrà essere inizializzata opportunamente
per indicare i vari buffer da e verso i quali verrà eseguito il trasferimento
dei dati. Essi verranno letti (o scritti) nell'ordine in cui li si sono
specificati nel vettore \param{vector}.

La standardizzazione delle due funzioni all'interno della revisione
POSIX.1-2001 prevede anche che sia possibile avere un limite al numero di
elementi del vettore \param{vector}. Qualora questo sussista, esso deve essere
indicato dal valore dalla costante \const{IOV\_MAX}, definita come le altre
costanti analoghe (vedi sez.~\ref{sec:sys_limits}) in \headfile{limits.h}; lo
stesso valore deve essere ottenibile in esecuzione tramite la funzione
\func{sysconf} richiedendo l'argomento \const{\_SC\_IOV\_MAX} (vedi
sez.~\ref{sec:sys_limits}).

Nel caso di Linux il limite di sistema è di 1024, però se si usano le
\acr{glibc} queste forniscono un \textit{wrapper} per le \textit{system call}
che si accorge se una operazione supererà il precedente limite, in tal caso i
dati verranno letti o scritti con le usuali \func{read} e \func{write} usando
un buffer di dimensioni sufficienti appositamente allocato e sufficiente a
contenere tutti i dati indicati da \param{vector}. L'operazione avrà successo
ma si perderà l'atomicità del trasferimento da e verso la destinazione finale.

Si tenga presente infine che queste funzioni operano sui file con
l'interfaccia dei file descriptor, e non è consigliabile mescolarle con
l'interfaccia classica dei \textit{file stream} di
sez.~\ref{sec:files_std_interface}; a causa delle bufferizzazioni interne di
quest'ultima infatti si potrebbero avere risultati indefiniti e non
corrispondenti a quanto aspettato.

Come per le normali operazioni di lettura e scrittura, anche per l'\textsl{I/O
  vettorizzato} si pone il problema di poter effettuare le operazioni in
maniera atomica a partire da un certa posizione sul file. Per questo motivo a
partire dal kernel 2.6.30 sono state introdotte anche per l'\textsl{I/O
  vettorizzato} le analoghe delle funzioni \func{pread} e \func{pwrite} (vedi
sez.~\ref{sec:file_read} e \ref{sec:file_write}); le due funzioni sono
\funcd{preadv} e \funcd{pwritev} ed i rispettivi prototipi sono:\footnote{le
  due funzioni sono analoghe alle omonime presenti in BSD; le \textit{system
    call} usate da Linux (introdotte a partire dalla versione 2.6.30)
  utilizzano degli argomenti diversi per problemi collegati al formato a 64
  bit dell'argomento \param{offset}, che varia a seconda delle architetture,
  ma queste differenze vengono gestite dalle funzioni di librerie di libreria
  che mantengono l'interfaccia delle analoghe tratte da BSD.}


\begin{funcproto}{
\fhead{sys/uio.h}
\fdecl{int preadv(int fd, const struct iovec *vector, int count, off\_t
    offset)}
\fdecl{int pwritev(int fd, const struct iovec *vector, int count, off\_t
    offset)}
\fdesc{Eseguono una lettura o una scrittura vettorizzata a partire da una data
  posizione sul file.} 
}

{ Le funzioni hanno gli stessi valori di ritorno delle corrispondenti
  \func{readv} e \func{writev} ed anche gli eventuali errori sono gli stessi,
  con in più quelli che si possono ottenere dalle possibili condizioni di
  errore di \func{lseek}.
}
\end{funcproto}

Le due funzioni eseguono rispettivamente una lettura o una scrittura
vettorizzata a partire dalla posizione \param{offset} sul file indicato
da \param{fd}, la posizione corrente sul file, come vista da eventuali altri
processi che vi facciano riferimento, non viene alterata. A parte la presenza
dell'ulteriore argomento il comportamento delle funzioni è identico alle
precedenti \func{readv} e \func{writev}. 

Con l'uso di queste funzioni si possono evitare eventuali
\itindex{race~condition} \textit{race condition} quando si deve eseguire la
una operazione di lettura e scrittura vettorizzata a partire da una certa
posizione su un file, mentre al contempo si possono avere in concorrenza
processi che utilizzano lo stesso file descriptor (si ricordi quanto visto in
sez.~\ref{sec:file_adv_func}) con delle chiamate a \func{lseek}.



\subsection{L'I/O diretto fra file descriptor: \func{sendfile} e
  \func{splice}} 
\label{sec:file_sendfile_splice}

Uno dei problemi che si presentano nella gestione dell'I/O è quello in cui si
devono trasferire grandi quantità di dati da un file descriptor ed un altro;
questo usualmente comporta la lettura dei dati dal primo file descriptor in un
buffer in memoria, da cui essi vengono poi scritti sul secondo.

Benché il kernel ottimizzi la gestione di questo processo quando si ha a che
fare con file normali, in generale quando i dati da trasferire sono molti si
pone il problema di effettuare trasferimenti di grandi quantità di dati da
\textit{kernel space} a \textit{user space} e all'indietro, quando in realtà
potrebbe essere più efficiente mantenere tutto in \textit{kernel
  space}. Tratteremo in questa sezione alcune funzioni specialistiche che
permettono di ottimizzare le prestazioni in questo tipo di situazioni.

La prima funzione che è stata ideata per ottimizzare il trasferimento dei dati
fra due file descriptor è \func{sendfile}.\footnote{la funzione è stata
  introdotta con i kernel della serie 2.2, e disponibile dalle \acr{glibc}
  2.1.} La funzione è presente in diverse versioni di Unix (la si ritrova ad
esempio in FreeBSD, HPUX ed altri Unix) ma non è presente né in POSIX.1-2001
né in altri standard (pertanto si eviti di utilizzarla se si devono scrivere
programmi portabili) per cui per essa vengono utilizzati prototipi e
semantiche differenti. Nel caso di Linux il prototipo di \funcd{sendfile} è:


\begin{funcproto}{
\fhead{sys/sendfile.h}
\fdecl{ssize\_t sendfile(int out\_fd, int in\_fd, off\_t *offset, size\_t
    count)}
\fdesc{Copia dei dati da un file descriptor ad un altro.} 
}

{La funzione ritorna il numero di byte trasferiti in caso di successo e $-1$
  per un errore, nel qual caso \var{errno} assumerà uno dei valori:
  \begin{errlist}
    \item[\errcode{EAGAIN}] si è impostata la modalità non bloccante su
      \param{out\_fd} e la scrittura si bloccherebbe.
    \item[\errcode{EINVAL}] i file descriptor non sono validi, o sono bloccati
      (vedi sez.~\ref{sec:file_locking}), o \func{mmap} non è disponibile per
      \param{in\_fd}.
    \item[\errcode{EIO}] si è avuto un errore di lettura da \param{in\_fd}.
    \item[\errcode{ENOMEM}] non c'è memoria sufficiente per la lettura da
      \param{in\_fd}.
  \end{errlist}
  ed inoltre \errcode{EBADF} e \errcode{EFAULT} nel loro significato
  generico.}
\end{funcproto}

La funzione copia direttamente \param{count} byte dal file descriptor
\param{in\_fd} al file descriptor \param{out\_fd}. In caso di successo la
funzione ritorna il numero di byte effettivamente copiati da \param{in\_fd} a
\param{out\_fd} e come per le ordinarie \func{read} e \func{write} questo
valore può essere inferiore a quanto richiesto con \param{count}.

Se il puntatore \param{offset} è nullo la funzione legge i dati a partire
dalla posizione corrente su \param{in\_fd}, altrimenti verrà usata la
posizione indicata dal valore puntato da \param{offset}; in questo caso detto
valore sarà aggiornato, come \textit{value result argument}, per indicare la
posizione del byte successivo all'ultimo che è stato letto, mentre la
posizione corrente sul file non sarà modificata. Se invece \param{offset} è
nullo la posizione corrente sul file sarà aggiornata tenendo conto dei byte
letti da \param{in\_fd}.

Fino ai kernel della serie 2.4 la funzione era utilizzabile su un qualunque
file descriptor, e permetteva di sostituire la invocazione successiva di una
\func{read} e una \func{write} (e l'allocazione del relativo buffer) con una
sola chiamata a \funcd{sendfile}. In questo modo si poteva diminuire il numero
di chiamate al sistema e risparmiare in trasferimenti di dati da
\textit{kernel space} a \textit{user space} e viceversa.  La massima utilità
della funzione si ottiene comunque per il trasferimento di dati da un file su
disco ad un socket di rete,\footnote{questo è il caso classico del lavoro
  eseguito da un server web, ed infatti Apache ha una opzione per il supporto
  esplicito di questa funzione.} dato che in questo caso diventa possibile
effettuare il trasferimento diretto via DMA dal controller del disco alla
scheda di rete, senza neanche allocare un buffer nel kernel (il meccanismo è
detto \textit{zerocopy} in quanto i dati non vengono mai copiati dal kernel,
che si limita a programmare solo le operazioni di lettura e scrittura via DMA)
ottenendo la massima efficienza possibile senza pesare neanche sul processore.

In seguito però ci si accorse che, fatta eccezione per il trasferimento
diretto da file a socket, non sempre \func{sendfile} comportava miglioramenti
significativi delle prestazioni rispetto all'uso in sequenza di \func{read} e
\func{write}. Nel caso generico infatti il kernel deve comunque allocare un
buffer ed effettuare la copia dei dati, e in tal caso spesso il guadagno
ottenibile nel ridurre il numero di chiamate al sistema non compensa le
ottimizzazioni che possono essere fatte da una applicazione in \textit{user
  space} che ha una conoscenza diretta su come questi sono strutturati, per
cui in certi casi si potevano avere anche dei peggioramenti.  Questo ha
portato, per i kernel della serie 2.6,\footnote{per alcune motivazioni di
  questa scelta si può fare riferimento a quanto illustrato da Linus Torvalds
  in \url{http://www.cs.helsinki.fi/linux/linux-kernel/2001-03/0200.html}.}
alla decisione di consentire l'uso della funzione soltanto quando il file da
cui si legge supporta le operazioni di \textit{memory mapping} (vale a dire
non è un socket) e quello su cui si scrive è un socket; in tutti gli altri
casi l'uso di \func{sendfile} da luogo ad un errore di \errcode{EINVAL}.

Nonostante ci possano essere casi in cui \func{sendfile} non migliora le
prestazioni, resta il dubbio se la scelta di disabilitarla sempre per il
trasferimento fra file di dati sia davvero corretta. Se ci sono peggioramenti
di prestazioni infatti si può sempre fare ricorso al metodo ordinario, ma
lasciare a disposizione la funzione consentirebbe se non altro di semplificare
la gestione della copia dei dati fra file, evitando di dover gestire
l'allocazione di un buffer temporaneo per il loro trasferimento. Comunque a
partire dal kernel 2.6.33 la restrizione su \param{out\_fd} è stata rimossa e
questo può essere un file qualunque, rimane però quella di non poter usare un
socket per \param{in\_fd}.

A partire dal kernel 2.6.17 come alternativa a \func{sendfile} è disponibile
la nuova \textit{system call} \func{splice}. Lo scopo di questa funzione è
quello di fornire un meccanismo generico per il trasferimento di dati da o
verso un file, utilizzando un buffer gestito internamente dal
kernel. Descritta in questi termini \func{splice} sembra semplicemente un
``\textsl{dimezzamento}'' di \func{sendfile}, nel senso che un trasferimento
di dati fra due file con \func{sendfile} non sarebbe altro che la lettura
degli stessi su un buffer seguita dalla relativa scrittura, cosa che in questo
caso si dovrebbe eseguire con due chiamate a \func{splice}.

In realtà le due \textit{system call} sono profondamente diverse nel loro
meccanismo di funzionamento;\footnote{questo fino al kernel 2.6.23, dove
  \func{sendfile} è stata reimplementata in termini di \func{splice}, pur
  mantenendo disponibile la stessa interfaccia verso l'\textit{user space}.}
\func{sendfile} infatti, come accennato, non necessita di avere a disposizione
un buffer interno, perché esegue un trasferimento diretto di dati; questo la
rende in generale più efficiente, ma anche limitata nelle sue applicazioni,
dato che questo tipo di trasferimento è possibile solo in casi specifici che
nel caso di Linux questi sono anche solo quelli in cui essa può essere
effettivamente utilizzata.

Il concetto che sta dietro a \func{splice} invece è diverso,\footnote{in
  realtà la proposta originale di Larry Mc Voy non differisce poi tanto negli
  scopi da \func{sendfile}, quello che rende \func{splice} davvero diversa è
  stata la reinterpretazione che ne è stata fatta nell'implementazione su
  Linux realizzata da Jens Anxboe, concetti che sono esposti sinteticamente
  dallo stesso Linus Torvalds in \url{http://kerneltrap.org/node/6505}.} si
tratta semplicemente di una funzione che consente di fare in maniera del tutto
generica delle operazioni di trasferimento di dati fra un file e un buffer
gestito interamente in \textit{kernel space}. In questo caso il cuore della
funzione (e delle affini \func{vmsplice} e \func{tee}, che tratteremo più
avanti) è appunto l'uso di un buffer in \textit{kernel space}, e questo è
anche quello che ne ha semplificato l'adozione, perché l'infrastruttura per la
gestione di un tale buffer è presente fin dagli albori di Unix per la
realizzazione delle \textit{pipe} (vedi sez.~\ref{sec:ipc_unix}). Dal punto di
vista concettuale allora \func{splice} non è altro che una diversa interfaccia
(rispetto alle \textit{pipe}) con cui utilizzare in \textit{user space}
l'oggetto ``\textsl{buffer in kernel space}''.

Così se per una \textit{pipe} o una \textit{fifo} il buffer viene utilizzato
come area di memoria (vedi fig.~\ref{fig:ipc_pipe_singular}) dove appoggiare i
dati che vengono trasferiti da un capo all'altro della stessa per creare un
meccanismo di comunicazione fra processi, nel caso di \func{splice} il buffer
viene usato o come fonte dei dati che saranno scritti su un file, o come
destinazione dei dati che vengono letti da un file. La funzione fornisce
quindi una interfaccia generica che consente di trasferire dati da un buffer
ad un file o viceversa; il prototipo di \funcd{splice}, accessibile solo dopo
aver definito la macro \macro{\_GNU\_SOURCE},\footnote{si ricordi che questa
  funzione non è contemplata da nessuno standard, è presente solo su Linux, e
  pertanto deve essere evitata se si vogliono scrivere programmi portabili.}
è il seguente:

\begin{funcproto}{
\fhead{fcntl.h} 
\fdecl{long splice(int fd\_in, off\_t *off\_in, int fd\_out, off\_t
    *off\_out, size\_t len, \\
\phantom{long splice(}unsigned int flags)}
\fdesc{Trasferisce dati da un file verso una \textit{pipe} o viceversa.} 
}

{La funzione ritorna il numero di byte trasferiti in caso di successo e $-1$
  per un errore, nel qual caso \var{errno} assumerà uno dei valori:
  \begin{errlist}
    \item[\errcode{EBADF}] uno o entrambi fra \param{fd\_in} e \param{fd\_out}
      non sono file descriptor validi o, rispettivamente, non sono stati
      aperti in lettura o scrittura.
    \item[\errcode{EINVAL}] il filesystem su cui si opera non supporta
      \func{splice}, oppure nessuno dei file descriptor è una \textit{pipe},
      oppure si 
      è dato un valore a \param{off\_in} o \param{off\_out} ma il
      corrispondente file è un dispositivo che non supporta la funzione
      \func{lseek}.
    \item[\errcode{ENOMEM}] non c'è memoria sufficiente per l'operazione
      richiesta.
    \item[\errcode{ESPIPE}] o \param{off\_in} o \param{off\_out} non sono
      \val{NULL} ma il corrispondente file descriptor è una \textit{pipe}.
  \end{errlist}
}
\end{funcproto}


La funzione esegue un trasferimento di \param{len} byte dal file descriptor
\param{fd\_in} al file descriptor \param{fd\_out}, uno dei quali deve essere
una \textit{pipe}; l'altro file descriptor può essere qualunque, questo
significa che può essere, oltre che un file di dati, anche un altra
\textit{pipe}, o un socket.  Come accennato una \textit{pipe} non è altro che
un buffer in \textit{kernel space}, per cui a seconda che essa sia usata
per \param{fd\_in} o \param{fd\_out} si avrà rispettivamente la copia dei dati
dal buffer al file o viceversa.

In caso di successo la funzione ritorna il numero di byte trasferiti, che può
essere, come per le normali funzioni di lettura e scrittura su file, inferiore
a quelli richiesti; un valore negativo indicherà un errore mentre un valore
nullo indicherà che non ci sono dati da trasferire (ad esempio si è giunti
alla fine del file in lettura). Si tenga presente che, a seconda del verso del
trasferimento dei dati, la funzione si comporta nei confronti del file
descriptor che fa riferimento al file ordinario, come \func{read} o
\func{write}, e pertanto potrà anche bloccarsi (a meno che non si sia aperto
il suddetto file in modalità non bloccante).

I due argomenti \param{off\_in} e \param{off\_out} consentono di specificare,
come per l'analogo \param{offset} di \func{sendfile}, la posizione all'interno
del file da cui partire per il trasferimento dei dati. Come per
\func{sendfile} un valore nullo indica di usare la posizione corrente sul
file, ed essa sarà aggiornata automaticamente secondo il numero di byte
trasferiti. Un valore non nullo invece deve essere un puntatore ad una
variabile intera che indica la posizione da usare; questa verrà aggiornata, al
ritorno della funzione, al byte successivo all'ultimo byte trasferito.
Ovviamente soltanto uno di questi due argomenti, e più precisamente quello che
fa riferimento al file descriptor non associato alla \textit{pipe}, può essere
specificato come valore non nullo.

Infine l'argomento \param{flags} consente di controllare alcune
caratteristiche del funzionamento della funzione; il contenuto è una maschera
binaria e deve essere specificato come OR aritmetico dei valori riportati in
tab.~\ref{tab:splice_flag}. Alcuni di questi valori vengono utilizzati anche
dalle funzioni \func{vmsplice} e \func{tee} per cui la tabella riporta le
descrizioni complete di tutti i valori possibili anche quando, come per
\const{SPLICE\_F\_GIFT}, questi non hanno effetto su \func{splice}.

\begin{table}[htb]
  \centering
  \footnotesize
  \begin{tabular}[c]{|l|p{10cm}|}
    \hline
    \textbf{Valore} & \textbf{Significato} \\
    \hline
    \hline
    \const{SPLICE\_F\_MOVE}    & Suggerisce al kernel di spostare le pagine
                                 di memoria contenenti i dati invece di
                                 copiarle: per una maggiore efficienza
                                 \func{splice} usa quando possibile i
                                 meccanismi della memoria virtuale per
                                 eseguire i trasferimenti di dati. In maniera
                                 analoga a \func{mmap}), qualora le pagine non
                                 possano essere spostate dalla \textit{pipe} o
                                 il buffer non corrisponda a pagine intere
                                 esse saranno comunque copiate. Viene usato
                                 soltanto da \func{splice}.\\ 
    \const{SPLICE\_F\_NONBLOCK}& Richiede di operare in modalità non
                                 bloccante; questo flag influisce solo sulle
                                 operazioni che riguardano l'I/O da e verso la
                                 \textit{pipe}. Nel caso di \func{splice}
                                 questo significa che la funzione potrà
                                 comunque bloccarsi nell'accesso agli altri
                                 file descriptor (a meno che anch'essi non
                                 siano stati aperti in modalità non
                                 bloccante).\\
    \const{SPLICE\_F\_MORE}    & Indica al kernel che ci sarà l'invio di
                                 ulteriori dati in una \func{splice}
                                 successiva, questo è un suggerimento utile
                                 che viene usato quando \param{fd\_out} è un
                                 socket. Questa opzione consente di utilizzare
                                 delle opzioni di gestione dei socket che
                                 permettono di ottimizzare le trasmissioni via
                                 rete (si veda la descrizione di
                                 \const{TCP\_CORK} in
                                 sez.~\ref{sec:sock_tcp_udp_options} e quella
                                 di \const{MSG\_MORE} in
                                 sez.~\ref{sec:net_sendmsg}).  Attualmente
                                 viene usato solo da \func{splice}, potrà essere
                                 implementato in futuro anche per
                                 \func{vmsplice} e \func{tee}.\\
    \const{SPLICE\_F\_GIFT}    & Le pagine di memoria utente sono
                                 ``\textsl{donate}'' al kernel; questo
                                 significa che la cache delle pagine e i dati
                                 su disco potranno differire, e che
                                 l'applicazione non potrà modificare
                                 quest'area di memoria. 
                                 Se impostato una seguente \func{splice} che
                                 usa \const{SPLICE\_F\_MOVE} potrà spostare le 
                                 pagine con successo, altrimenti esse dovranno
                                 essere copiate; per usare questa opzione i
                                 dati dovranno essere opportunamente allineati
                                 in posizione ed in dimensione alle pagine di
                                 memoria. Viene usato soltanto da
                                 \func{vmsplice}.\\
    \hline
  \end{tabular}
  \caption{Le costanti che identificano i bit della maschera binaria
    dell'argomento \param{flags} di \func{splice}, \func{vmsplice} e
    \func{tee}.} 
  \label{tab:splice_flag}
\end{table}


Per capire meglio il funzionamento di \func{splice} vediamo un esempio con un
semplice programma che usa questa funzione per effettuare la copia di un file
su un altro senza utilizzare buffer in \textit{user space}. Lo scopo del
programma è quello di eseguire la copia dei dati con \func{splice}, questo
significa che si dovrà usare la funzione due volte, prima per leggere i dati
dal file di ingresso e poi per scriverli su quello di uscita, appoggiandosi ad
una \textit{pipe}: lo schema del flusso dei dati è illustrato in
fig.~\ref{fig:splicecp_data_flux}.

\begin{figure}[htb]
  \centering
  \includegraphics[height=3.5cm]{img/splice_copy}
  \caption{Struttura del flusso di dati usato dal programma \texttt{splicecp}.}
  \label{fig:splicecp_data_flux}
\end{figure}

Il programma si chiama \texttt{splicecp.c} ed il codice completo è disponibile
coi sorgenti allegati alla guida, il corpo principale del programma, che non
contiene la sezione di gestione delle opzioni, le funzioni di ausilio, le
aperture dei file di ingresso e di uscita passati come argomenti e quella
della \textit{pipe} intermedia, è riportato in fig.~\ref{fig:splice_example}.

\begin{figure}[!htb]
  \footnotesize \centering
  \begin{minipage}[c]{\codesamplewidth}
    \includecodesample{listati/splicecp.c}
  \end{minipage}
  \normalsize
  \caption{Esempio di codice che usa \func{splice} per effettuare la copia di
    un file.}
  \label{fig:splice_example}
\end{figure}

Il ciclo principale (\texttt{\small 13-38}) inizia con la lettura dal file
sorgente tramite la prima \func{splice} (\texttt{\small 14-15}), in questo
caso si è usato come primo argomento il file descriptor del file sorgente e
come terzo quello del capo in scrittura della \textit{pipe}. Il funzionamento
delle \textit{pipe} e l'uso della coppia di file descriptor ad esse associati
è trattato in dettaglio in sez.~\ref{sec:ipc_unix}; non ne parleremo qui dato
che nell'ottica dell'uso di \func{splice} questa operazione corrisponde
semplicemente al trasferimento dei dati dal file al buffer in \textit{kernel
  space}.

La lettura viene eseguita in blocchi pari alla dimensione specificata
dall'opzione \texttt{-s} (il default è 4096); essendo in questo caso
\func{splice} equivalente ad una \func{read} sul file, se ne controlla il
valore di uscita in \var{nread} che indica quanti byte sono stati letti, se
detto valore è nullo (\texttt{\small 16}) questo significa che si è giunti
alla fine del file sorgente e pertanto l'operazione di copia è conclusa e si
può uscire dal ciclo arrivando alla conclusione del programma (\texttt{\small
  59}). In caso di valore negativo (\texttt{\small 17-24}) c'è stato un
errore ed allora si ripete la lettura (\texttt{\small 16}) se questo è dovuto
ad una interruzione, o altrimenti si esce con un messaggio di errore
(\texttt{\small 21-23}).

Una volta completata con successo la lettura si avvia il ciclo di scrittura
(\texttt{\small 25-37}); questo inizia (\texttt{\small 26-27}) con la
seconda \func{splice} che cerca di scrivere gli \var{nread} byte letti, si
noti come in questo caso il primo argomento faccia di nuovo riferimento alla
\textit{pipe} (in questo caso si usa il capo in lettura, per i dettagli si
veda al solito sez.~\ref{sec:ipc_unix}) mentre il terzo sia il file descriptor
del file di destinazione.

Di nuovo si controlla il numero di byte effettivamente scritti restituito in
\var{nwrite} e in caso di errore al solito si ripete la scrittura se questo è
dovuto a una interruzione o si esce con un messaggio negli altri casi
(\texttt{\small 28-35}). Infine si chiude il ciclo di scrittura sottraendo
(\texttt{\small 37}) il numero di byte scritti a quelli di cui è richiesta la
scrittura,\footnote{in questa parte del ciclo \var{nread}, il cui valore
  iniziale è dato dai byte letti dalla precedente chiamata a \func{splice},
  viene ad assumere il significato di byte da scrivere.} così che il ciclo di
scrittura venga ripetuto fintanto che il valore risultante sia maggiore di
zero, indice che la chiamata a \func{splice} non ha esaurito tutti i dati
presenti sul buffer.

Si noti come il programma sia concettualmente identico a quello che si sarebbe
scritto usando \func{read} al posto della prima \func{splice} e \func{write}
al posto della seconda, utilizzando un buffer in \textit{user space} per
eseguire la copia dei dati, solo che in questo caso non è stato necessario
allocare nessun buffer e non si è trasferito nessun dato in \textit{user
  space}.  Si noti anche come si sia usata la combinazione
\texttt{SPLICE\_F\_MOVE | SPLICE\_F\_MORE } per l'argomento \param{flags} di
\func{splice}, infatti anche se un valore nullo avrebbe dato gli stessi
risultati, l'uso di questi flag, che si ricordi servono solo a dare
suggerimenti al kernel, permette in genere di migliorare le prestazioni.

Come accennato con l'introduzione di \func{splice} sono state realizzate anche
altre due \textit{system call}, \func{vmsplice} e \func{tee}, che utilizzano
la stessa infrastruttura e si basano sullo stesso concetto di manipolazione e
trasferimento di dati attraverso un buffer in \textit{kernel space}; benché
queste non attengono strettamente ad operazioni di trasferimento dati fra file
descriptor, le tratteremo qui, essendo strettamente correlate fra loro.

La prima funzione, \funcd{vmsplice}, è la più simile a \func{splice} e come
indica il suo nome consente di trasferire i dati dalla memoria virtuale di un
processo (ad esempio per un file mappato in memoria) verso una \textit{pipe};
il suo prototipo è:

\begin{funcproto}{
\fhead{fcntl.h} 
\fhead{sys/uio.h}
\fdecl{long vmsplice(int fd, const struct iovec *iov, unsigned long nr\_segs,\\
\phantom{long vmsplice(}unsigned int flags)}
\fdesc{Trasferisce dati dalla memoria di un processo verso una \textit{pipe}.} 
}

{La funzione ritorna il numero di byte trasferiti in caso di successo e $-1$
  per un errore, nel qual caso \var{errno} assumerà uno dei valori:
  \begin{errlist}
    \item[\errcode{EBADF}] o \param{fd} non è un file descriptor valido o non
      fa riferimento ad una \textit{pipe}.
    \item[\errcode{EINVAL}] si è usato un valore nullo per \param{nr\_segs}
      oppure si è usato \const{SPLICE\_F\_GIFT} ma la memoria non è allineata.
    \item[\errcode{ENOMEM}] non c'è memoria sufficiente per l'operazione
      richiesta.
  \end{errlist}
}
\end{funcproto}

La \textit{pipe} indicata da \param{fd} dovrà essere specificata tramite il
file descriptor corrispondente al suo capo aperto in scrittura (di nuovo si
faccia riferimento a sez.~\ref{sec:ipc_unix}), mentre per indicare quali
segmenti della memoria del processo devono essere trasferiti verso di essa si
dovrà utilizzare un vettore di strutture \struct{iovec} (vedi
fig.~\ref{fig:file_iovec}), esattamente con gli stessi criteri con cui le si
usano per l'I/O vettorizzato, indicando gli indirizzi e le dimensioni di
ciascun segmento di memoria su cui si vuole operare; le dimensioni del
suddetto vettore devono essere passate nell'argomento \param{nr\_segs} che
indica il numero di segmenti di memoria da trasferire.  Sia per il vettore che
per il valore massimo di \param{nr\_segs} valgono le stesse limitazioni
illustrate in sez.~\ref{sec:file_multiple_io}.

In caso di successo la funzione ritorna il numero di byte trasferiti sulla
\textit{pipe}. In generale, se i dati una volta creati non devono essere
riutilizzati (se cioè l'applicazione che chiama \func{vmsplice} non
modificherà più la memoria trasferita), è opportuno utilizzare
per \param{flag} il valore \const{SPLICE\_F\_GIFT}; questo fa sì che il kernel
possa rimuovere le relative pagine dalla cache della memoria virtuale, così
che queste possono essere utilizzate immediatamente senza necessità di
eseguire una copia dei dati che contengono.

La seconda funzione aggiunta insieme a \func{splice} è \func{tee}, che deve il
suo nome all'omonimo comando in \textit{user space}, perché in analogia con
questo permette di duplicare i dati in ingresso su una \textit{pipe} su
un'altra \textit{pipe}. In sostanza, sempre nell'ottica della manipolazione
dei dati su dei buffer in \textit{kernel space}, la funzione consente di
eseguire una copia del contenuto del buffer stesso. Il prototipo di
\funcd{tee} è il seguente:

\begin{funcproto}{
\fhead{fcntl.h}
\fdecl{long tee(int fd\_in, int fd\_out, size\_t len, unsigned int
    flags)}
\fdesc{Duplica i dati da una \textit{pipe} ad un'altra.} 
}

{La funzione ritorna restituisce il numero di byte copiati in caso di successo
  e $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei valori:
  \begin{errlist}
    \item[\errcode{EINVAL}] o uno fra \param{fd\_in} e \param{fd\_out} non fa
      riferimento ad una \textit{pipe} o entrambi fanno riferimento alla
      stessa \textit{pipe}.
    \item[\errcode{ENOMEM}] non c'è memoria sufficiente per l'operazione
      richiesta.
  \end{errlist}
}
\end{funcproto}

La funzione copia \param{len} byte del contenuto di una \textit{pipe} su di
un'altra; \param{fd\_in} deve essere il capo in lettura della \textit{pipe}
sorgente e \param{fd\_out} il capo in scrittura della \textit{pipe}
destinazione; a differenza di quanto avviene con \func{read} i dati letti con
\func{tee} da \param{fd\_in} non vengono \textsl{consumati} e restano
disponibili sulla \textit{pipe} per una successiva lettura (di nuovo per il
comportamento delle \textit{pipe} si veda sez.~\ref{sec:ipc_unix}). Al
momento\footnote{quello della stesura di questo paragrafo, avvenuta il Gennaio
  2010, in futuro potrebbe essere implementato anche \const{SPLICE\_F\_MORE}.}
il solo valore utilizzabile per \param{flag}, fra quelli elencati in
tab.~\ref{tab:splice_flag}, è \const{SPLICE\_F\_NONBLOCK} che rende la
funzione non bloccante.

La funzione restituisce il numero di byte copiati da una \textit{pipe}
all'altra (o $-1$ in caso di errore), un valore nullo indica che non ci sono
byte disponibili da copiare e che il capo in scrittura della \textit{pipe} è
stato chiuso; si tenga presente però che questo non avviene se si è impostato
il flag \const{SPLICE\_F\_NONBLOCK}, in tal caso infatti si avrebbe un errore
di \errcode{EAGAIN}. Un esempio di realizzazione del comando \texttt{tee}
usando questa funzione, ripreso da quello fornito nella pagina di manuale e
dall'esempio allegato al patch originale, è riportato in
fig.~\ref{fig:tee_example}. Il programma consente di copiare il contenuto
dello \textit{standard input} sullo \textit{standard output} e su un file
specificato come argomento, il codice completo si trova nel file
\texttt{tee.c} dei sorgenti allegati alla guida.

\begin{figure}[!htb]
  \footnotesize \centering
  \begin{minipage}[c]{\codesamplewidth}
    \includecodesample{listati/tee.c}
  \end{minipage}
  \normalsize
  \caption{Esempio di codice che usa \func{tee} per copiare i dati dello
    standard input sullo standard output e su un file.}
  \label{fig:tee_example}
\end{figure}

La prima parte del programma, che si è omessa per brevità, si cura
semplicemente di controllare che sia stato fornito almeno un argomento (il
nome del file su cui scrivere), di aprirlo e che sia lo standard input che lo
standard output corrispondano ad una \textit{pipe}.

Il ciclo principale (\texttt{\small 11-32}) inizia con la chiamata a
\func{tee} che duplica il contenuto dello standard input sullo standard output
(\texttt{\small 13}), questa parte è del tutto analoga ad una lettura ed
infatti come nell'esempio di fig.~\ref{fig:splice_example} si controlla il
valore di ritorno della funzione in \var{len}; se questo è nullo significa che
non ci sono più dati da leggere e si chiude il ciclo (\texttt{\small 14}), se
è negativo c'è stato un errore, ed allora si ripete la chiamata se questo è
dovuto ad una interruzione (\texttt{\small 15-48}) o si stampa un messaggio
di errore e si esce negli altri casi (\texttt{\small 18-21}).

Una volta completata la copia dei dati sullo \textit{standard output} si
possono estrarre dallo \textit{standard input} e scrivere sul file, di nuovo
su usa un ciclo di scrittura (\texttt{\small 24-31}) in cui si ripete una
chiamata a \func{splice} (\texttt{\small 25}) fintanto che non si sono scritti
tutti i \var{len} byte copiati in precedenza con \func{tee} (il funzionamento
è identico all'analogo ciclo di scrittura del precedente esempio di
fig.~\ref{fig:splice_example}).

Infine una nota finale riguardo \func{splice}, \func{vmsplice} e \func{tee}:
occorre sottolineare che benché finora si sia parlato di trasferimenti o copie
di dati in realtà nella implementazione di queste \textit{system call} non è
affatto detto che i dati vengono effettivamente spostati o copiati, il kernel
infatti realizza le \textit{pipe} come un insieme di puntatori\footnote{per
  essere precisi si tratta di un semplice buffer circolare, un buon articolo
  sul tema si trova su \url{http://lwn.net/Articles/118750/}.}  alle pagine di
memoria interna che contengono i dati, per questo una volta che i dati sono
presenti nella memoria del kernel tutto quello che viene fatto è creare i
suddetti puntatori ed aumentare il numero di referenze; questo significa che
anche con \func{tee} non viene mai copiato nessun byte, vengono semplicemente
copiati i puntatori.

% TODO?? dal 2.6.25 splice ha ottenuto il supporto per la ricezione su rete


\subsection{Gestione avanzata dell'accesso ai dati dei file}
\label{sec:file_fadvise}

Nell'uso generico dell'interfaccia per l'accesso al contenuto dei file le
operazioni di lettura e scrittura non necessitano di nessun intervento di
supervisione da parte dei programmi, si eseguirà una \func{read} o una
\func{write}, i dati verranno passati al kernel che provvederà ad effettuare
tutte le operazioni (e a gestire il \textit{caching} dei dati) per portarle a
termine in quello che ritiene essere il modo più efficiente.

Il problema è che il concetto di migliore efficienza impiegato dal kernel è
relativo all'uso generico, mentre esistono molti casi in cui ci sono esigenze
specifiche dei singoli programmi, che avendo una conoscenza diretta di come
verranno usati i file, possono necessitare di effettuare delle ottimizzazioni
specifiche, relative alle proprie modalità di I/O sugli stessi. Tratteremo in
questa sezione una serie funzioni che consentono ai programmi di ottimizzare
il loro accesso ai dati dei file e controllare la gestione del relativo
\textit{caching}.

\itindbeg{read-ahead}

Una prima funzione che può essere utilizzata per modificare la gestione
ordinaria dell'I/O su un file è \funcd{readahead} (questa è una funzione
specifica di Linux, introdotta con il kernel 2.4.13, e non deve essere usata
se si vogliono scrivere programmi portabili), che consente di richiedere una
lettura anticipata del contenuto dello stesso in cache, così che le seguenti
operazioni di lettura non debbano subire il ritardo dovuto all'accesso al
disco; il suo prototipo è:

\begin{funcproto}{
\fhead{fcntl.h}
\fdecl{ssize\_t readahead(int fd, off64\_t *offset, size\_t count)}
\fdesc{Esegue una lettura preventiva del contenuto di un file in cache.} 
}

{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}] l'argomento \param{fd} non è un file descriptor
      valido o non è aperto in lettura.
    \item[\errcode{EINVAL}] l'argomento \param{fd} si riferisce ad un tipo di
      file che non supporta l'operazione (come una \textit{pipe} o un socket).
  \end{errlist}
}
\end{funcproto}

La funzione richiede che venga letto in anticipo il contenuto del file
\param{fd} a partire dalla posizione \param{offset} e per un ammontare di
\param{count} byte, in modo da portarlo in cache.  La funzione usa la memoria
virtuale ed il meccanismo della paginazione per cui la lettura viene eseguita
in blocchi corrispondenti alle dimensioni delle pagine di memoria, ed i valori
di \param{offset} e \param{count} vengono arrotondati di conseguenza.

La funzione estende quello che è un comportamento normale del kernel che,
quando si legge un file, aspettandosi che l'accesso prosegua, esegue sempre
una lettura preventiva di una certa quantità di dati; questo meccanismo di
lettura anticipata viene chiamato \textit{read-ahead}, da cui deriva il nome
della funzione. La funzione \func{readahead}, per ottimizzare gli accessi a
disco, effettua la lettura in cache della sezione richiesta e si blocca
fintanto che questa non viene completata.  La posizione corrente sul file non
viene modificata ed indipendentemente da quanto indicato con \param{count} la
lettura dei dati si interrompe una volta raggiunta la fine del file.

Si può utilizzare questa funzione per velocizzare le operazioni di lettura
all'interno di un programma tutte le volte che si conosce in anticipo quanti
dati saranno necessari nelle elaborazioni successive. Si potrà così
concentrare in un unico momento (ad esempio in fase di inizializzazione) la
lettura dei dati da disco, così da ottenere una migliore velocità di risposta
nelle operazioni successive.

\itindend{read-ahead}

Il concetto di \func{readahead} viene generalizzato nello standard
POSIX.1-2001 dalla funzione \func{posix\_fadvise} (anche se
l'argomento \param{len} è stato modificato da \type{size\_t} a \type{off\_t}
nella revisione POSIX.1-2003 TC1) che consente di ``\textsl{avvisare}'' il
kernel sulle modalità con cui si intende accedere nel futuro ad una certa
porzione di un file, così che esso possa provvedere le opportune
ottimizzazioni; il prototipo di \funcd{posix\_fadvise}\footnote{la funzione è
  stata introdotta su Linux solo a partire dal kernel 2.5.60, ed è disponibile
  soltanto se è stata definita la macro \macro{\_XOPEN\_SOURCE} ad valore di
  almeno \texttt{600} o la macro \macro{\_POSIX\_C\_SOURCE} ad valore di
  almeno \texttt{200112L}.} è:


\begin{funcproto}{
\fhead{fcntl.h}
\fdecl{int posix\_fadvise(int fd, off\_t offset, off\_t len, int advice)}
\fdesc{Dichiara al kernel le future modalità di accesso ad un file.}
}

{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}] l'argomento \param{fd} non è un file descriptor
      valido.
    \item[\errcode{EINVAL}] il valore di \param{advice} non è valido o
      \param{fd} si riferisce ad un tipo di file che non supporta l'operazione
      (come una \textit{pipe} o un socket).
    \item[\errcode{ESPIPE}] previsto dallo standard se \param{fd} è una
      \textit{pipe} o un socket (ma su Linux viene restituito
      \errcode{EINVAL}).
  \end{errlist}
}
\end{funcproto}

La funzione dichiara al kernel le modalità con cui intende accedere alla
regione del file indicato da \param{fd} che inizia alla posizione
\param{offset} e si estende per \param{len} byte. Se per \param{len} si usa un
valore nullo la regione coperta sarà da \param{offset} alla fine del file, ma
questo è vero solo per le versioni più recenti, fino al kernel 2.6.6 il valore
nullo veniva interpretato letteralmente. Le modalità sono indicate
dall'argomento \param{advice} che è una maschera binaria dei valori illustrati
in tab.~\ref{tab:posix_fadvise_flag}, che riprendono il significato degli
analoghi già visti in sez.~\ref{sec:file_memory_map} per
\func{madvise}.\footnote{dato che si tratta dello stesso tipo di funzionalità,
  in questo caso applicata direttamente al sistema ai contenuti di un file
  invece che alla sua mappatura in memoria.} Si tenga presente comunque che la
funzione dà soltanto un avvertimento, non esiste nessun vincolo per il kernel,
che utilizza semplicemente l'informazione.

\begin{table}[htb]
  \centering
  \footnotesize
  \begin{tabular}[c]{|l|p{10cm}|}
    \hline
    \textbf{Valore} & \textbf{Significato} \\
    \hline
    \hline
    \const{POSIX\_FADV\_NORMAL}  & Non ci sono avvisi specifici da fare
                                   riguardo le modalità di accesso, il
                                   comportamento sarà identico a quello che si
                                   avrebbe senza nessun avviso.\\ 
    \const{POSIX\_FADV\_SEQUENTIAL}& L'applicazione si aspetta di accedere di
                                   accedere ai dati specificati in maniera
                                   sequenziale, a partire dalle posizioni più
                                   basse.\\ 
    \const{POSIX\_FADV\_RANDOM}  & I dati saranno letti in maniera
                                   completamente causale.\\
    \const{POSIX\_FADV\_NOREUSE} & I dati saranno acceduti una sola volta.\\ 
    \const{POSIX\_FADV\_WILLNEED}& I dati saranno acceduti a breve.\\ 
    \const{POSIX\_FADV\_DONTNEED}& I dati non saranno acceduti a breve.\\ 
    \hline
  \end{tabular}
  \caption{Valori delle costanti usabili per l'argomento \param{advice} di
    \func{posix\_fadvise}, che indicano la modalità con cui si intende accedere
    ad un file.}
  \label{tab:posix_fadvise_flag}
\end{table}

Come \func{madvise} anche \func{posix\_fadvise} si appoggia al sistema della
memoria virtuale ed al meccanismo standard del \textit{read-ahead} utilizzato
dal kernel; in particolare utilizzando il valore
\const{POSIX\_FADV\_SEQUENTIAL} si raddoppia la dimensione dell'ammontare di
dati letti preventivamente rispetto al default, aspettandosi appunto una
lettura sequenziale che li utilizzerà, mentre con \const{POSIX\_FADV\_RANDOM}
si disabilita del tutto il suddetto meccanismo, dato che con un accesso del
tutto casuale è inutile mettersi a leggere i dati immediatamente successivi
gli attuali; infine l'uso di \const{POSIX\_FADV\_NORMAL} consente di
riportarsi al comportamento di default.

Le due modalità \const{POSIX\_FADV\_NOREUSE} e \const{POSIX\_FADV\_WILLNEED}
fino al kernel 2.6.18 erano equivalenti, a partire da questo kernel la prima
viene non ha più alcun effetto, mentre la seconda dà inizio ad una lettura in
cache della regione del file indicata.  La quantità di dati che verranno letti
è ovviamente limitata in base al carico che si viene a creare sul sistema
della memoria virtuale, ma in genere una lettura di qualche megabyte viene
sempre soddisfatta (ed un valore superiore è solo raramente di qualche
utilità). In particolare l'uso di \const{POSIX\_FADV\_WILLNEED} si può
considerare l'equivalente POSIX di \func{readahead}.

Infine con \const{POSIX\_FADV\_DONTNEED} si dice al kernel di liberare le
pagine di cache occupate dai dati presenti nella regione di file indicata.
Questa è una indicazione utile che permette di alleggerire il carico sulla
cache, ed un programma può utilizzare periodicamente questa funzione per
liberare pagine di memoria da dati che non sono più utilizzati per far posto a
nuovi dati utili; la pagina di manuale riporta l'esempio dello streaming di
file di grosse dimensioni, dove le pagine occupate dai dati già inviati
possono essere tranquillamente scartate.

Sia \func{posix\_fadvise} che \func{readahead} attengono alla ottimizzazione
dell'accesso in lettura; lo standard POSIX.1-2001 prevede anche una funzione
specifica per le operazioni di scrittura, \funcd{posix\_fallocate} (la
funzione è stata introdotta a partire dalle glibc 2.1.94), che consente di
preallocare dello spazio disco per assicurarsi che una seguente scrittura non
fallisca, il suo prototipo, anch'esso disponibile solo se si definisce la
macro \macro{\_XOPEN\_SOURCE} ad almeno 600, è:

\begin{funcproto}{
\fhead{fcntl.h}
\fdecl{int posix\_fallocate(int fd, off\_t offset, off\_t len)}
\fdesc{Richiede la allocazione di spazio disco per un file.} 
}

{La funzione ritorna $0$ in caso di successo e direttamente un codice di
  errore altrimenti, in tal caso \var{errno} non viene impostato, e si otterrà
  direttamente uno dei valori:
  \begin{errlist}
    \item[\errcode{EBADF}] l'argomento \param{fd} non è un file descriptor
      valido o non è aperto in scrittura.
    \item[\errcode{EINVAL}] o \param{offset} o \param{len} sono minori di
      zero.
    \item[\errcode{EFBIG}] il valore di (\param{offset} + \param{len}) eccede
      la dimensione massima consentita per un file.
    \item[\errcode{ENODEV}] l'argomento \param{fd} non fa riferimento ad un
      file regolare.
    \item[\errcode{ENOSPC}] non c'è sufficiente spazio disco per eseguire
      l'operazione. 
    \item[\errcode{ESPIPE}] l'argomento \param{fd} è una \textit{pipe}.
  \end{errlist}
}
\end{funcproto}

La funzione assicura che venga allocato sufficiente spazio disco perché sia
possibile scrivere sul file indicato dall'argomento \param{fd} nella regione
che inizia dalla posizione \param{offset} e si estende per \param{len} byte;
se questa regione si estende oltre la fine del file le dimensioni di
quest'ultimo saranno incrementate di conseguenza. Dopo aver eseguito con
successo la funzione è garantito che una successiva scrittura nella regione
indicata non fallirà per mancanza di spazio disco. La funzione non ha nessun
effetto né sul contenuto, né sulla posizione corrente del file.

Ci si può chiedere a cosa possa servire una funzione come
\func{posix\_fallocate} dato che è sempre possibile ottenere l'effetto voluto
eseguendo esplicitamente sul file la scrittura di una serie di zeri (usando
\funcd{pwrite} per evitare spostamenti della posizione corrente sul file) per
l'estensione di spazio necessaria qualora il file debba essere esteso o abbia
dei buchi.\footnote{si ricordi che occorre scrivere per avere l'allocazione e
  che l'uso di \func{truncate} per estendere un file creerebbe soltanto uno
  \textit{sparse file} (vedi sez.~\ref{sec:file_lseek}) senza una effettiva
  allocazione dello spazio disco.}  In realtà questa è la modalità con cui la
funzione veniva realizzata nella prima versione fornita dalle \acr{glibc}, per
cui la funzione costituiva in sostanza soltanto una standardizzazione delle
modalità di esecuzione di questo tipo di allocazioni.

Questo metodo, anche se funzionante, comporta però l'effettiva esecuzione una
scrittura su tutto lo spazio disco necessario, da fare al momento della
richiesta di allocazione, pagandone il conseguente prezzo in termini di
prestazioni; il tutto quando in realtà servirebbe solo poter riservare lo
spazio per poi andarci a scrivere, una sola volta, quando il contenuto finale
diventa effettivamente disponibile.  Per poter fare tutto questo è però
necessario il supporto da parte del kernel, e questo è divenuto disponibile
solo a partire dal kernel 2.6.23 in cui è stata introdotta la nuova
\textit{system call} \func{fallocate},\footnote{non è detto che la funzione
  sia disponibile per tutti i filesystem, ad esempio per XFS il supporto è
  stato introdotto solo a partire dal kernel 2.6.25.}  che consente di
realizzare direttamente all'interno del kernel l'allocazione dello spazio
disco così da poter realizzare una versione di \func{posix\_fallocate} con
prestazioni molto più elevate; nelle \acr{glibc} la nuova \textit{system call}
viene sfruttata per la realizzazione di \func{posix\_fallocate} a partire
dalla versione 2.10.

Trattandosi di una funzione di servizio, ed ovviamente disponibile
esclusivamente su Linux, inizialmente \funcd{fallocate} non era stata definita
come funzione di libreria,\footnote{pertanto poteva essere invocata soltanto
  in maniera indiretta con l'ausilio di \func{syscall}, vedi
  sez.~\ref{sec:proc_syscall}, come \code{long fallocate(int fd, int mode,
      loff\_t offset, loff\_t len)}.} ma a partire dalle \acr{glibc} 2.10 è
  stato fornito un supporto esplicito; il suo prototipo è:

\begin{funcproto}{
\fhead{fcntl.h} 
\fdecl{int fallocate(int fd, int mode, off\_t offset, off\_t len)}
\fdesc{Prealloca dello spazio disco per un file.} 
}

{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}] \param{fd} non fa riferimento ad un file descriptor
      valido aperto in scrittura.
    \item[\errcode{EFBIG}] la somma di \param{offset} e \param{len} eccede le
      dimensioni massime di un file. 
    \item[\errcode{EINVAL}] \param{offset} è minore di zero o \param{len} è
      minore o uguale a zero. 
    \item[\errcode{ENODEV}] \param{fd} non fa riferimento ad un file ordinario
      o a una directory. 
    \item[\errcode{EPERM}] il file è immutabile o \textit{append-only} (vedi
      sez.~\ref{sec:file_perm_overview}).
    \item[\errcode{ENOSYS}] il filesystem contenente il file associato
      a \param{fd} non supporta \func{fallocate}.
    \item[\errcode{EOPNOTSUPP}] il filesystem contenente il file associato
      a \param{fd} non supporta l'operazione \param{mode}.
  \end{errlist}
  ed inoltre \errval{EINTR}, \errval{EIO} e \errval{ENOSPC} nel loro significato
  generico.}
\end{funcproto}

La funzione prende gli stessi argomenti di \func{posix\_fallocate} con lo
stesso significato, a cui si aggiunge l'argomento \param{mode} che indica le
modalità di allocazione; se questo è nullo il comportamento è identico a
quello di \func{posix\_fallocate} e si può considerare \func{fallocate} come
l'implementazione ottimale della stessa a livello di kernel.

Inizialmente l'unico altro valore possibile per \param{mode} era
\const{FALLOC\_FL\_KEEP\_SIZE} che richiede che la dimensione del file (quella
ottenuta nel campo \var{st\_size} di una struttura \struct{stat} dopo una
chiamata a \texttt{fstat}) non venga modificata anche quando la somma
di \param{offset} e \param{len} eccede la dimensione corrente, che serve
quando si deve comunque preallocare dello spazio per scritture in append. In
seguito sono stati introdotti altri valori, riassunti in
tab.\ref{tab:fallocate_mode}, per compiere altre operazioni relative alla
allocazione dello spazio disco dei file.

\begin{table}[htb]
  \centering
  \footnotesize
  \begin{tabular}[c]{|l|p{10cm}|}
    \hline
    \textbf{Valore} & \textbf{Significato} \\
    \hline
    \hline
    \const{FALLOC\_FL\_INSERT}     & .\\
    \const{FALLOC\_FL\_COLLAPSE\_RANGE}& .\\ 
    \const{FALLOC\_FL\_KEEP\_SIZE} & Mantiene invariata la dimensione del
                                     file, pur allocando lo spazio disco anche
                                     oltre la dimensione corrente del file.\\
    \const{FALLOC\_FL\_PUNCH\_HOLE}& Crea un \textsl{buco} nel file (vedi
                                     sez.~\ref{sec:file_lseek}) rendendolo una
                                     \textit{sparse file} (dal kernel
                                     2.6.38).\\  
    \const{FALLOC\_FL\_ZERO\_RANGE}& .\\ 
    \hline
  \end{tabular}
  \caption{Valori delle costanti usabili per l'argomento \param{mode} di
    \func{fallocate}.}
  \label{tab:fallocate_mode}
\end{table}

In particolare con \const{FALLOC\_FL\_PUNCH\_HOLE} è possibile scartare il
contenuto della sezione di file indicata da \param{offser} e \param{len},
creando un \textsl{buco} (si ricordi quanto detto in
sez.~\ref{sec:file_lseek}); i blocchi del file interamente contenuti
nell'intervallo verranno disallocati, la parte di intervallo contenuta
parzialmente in altri blocchi verrà riempita con zeri e la lettura dal file
restituirà degli zeri per tutto l'intervallo indicato. In sostanza si rende il
file uno \textit{sparse file} a posteriori.

% vedi http://lwn.net/Articles/226710/ e http://lwn.net/Articles/240571/
% http://kernelnewbies.org/Linux_2_6_23

% TODO aggiungere FALLOC_FL_ZERO_RANGE e FALLOC_FL_COLLAPSE_RANGE, inseriti
% nel kernel 3.15 (sul secondo vedi http://lwn.net/Articles/589260/), vedi
% anche http://lwn.net/Articles/629965/

% TODO aggiungere FALLOC_FL_INSERT vedi  http://lwn.net/Articles/629965/


% TODO non so dove trattarli, ma dal 2.6.39 ci sono i file handle, vedi
% http://lwn.net/Articles/432757/ 


% LocalWords:  dell'I locking multiplexing cap sez system call socket BSD GID
% LocalWords:  descriptor client deadlock NONBLOCK EAGAIN polling select kernel
% LocalWords:  pselect like sys unistd int fd readfds writefds exceptfds struct
% LocalWords:  timeval errno EBADF EINTR EINVAL ENOMEM sleep tab signal void of
% LocalWords:  CLR ISSET SETSIZE POSIX read NULL nell'header l'header glibc fig
% LocalWords:  libc header psignal sigmask SOURCE XOPEN timespec sigset race DN
% LocalWords:  condition sigprocmask tut self trick oldmask poll XPG pollfd l'I
% LocalWords:  ufds unsigned nfds RLIMIT NOFILE EFAULT ndfs events revents hung
% LocalWords:  POLLIN POLLRDNORM POLLRDBAND POLLPRI POLLOUT POLLWRNORM POLLERR
% LocalWords:  POLLWRBAND POLLHUP POLLNVAL POLLMSG SysV stream ASYNC SETOWN FAQ
% LocalWords:  GETOWN fcntl SETFL SIGIO SETSIG Stevens driven siginfo sigaction
% LocalWords:  all'I nell'I Frequently Unanswered Question SIGHUP lease holder
% LocalWords:  breaker truncate write SETLEASE arg RDLCK WRLCK UNLCK GETLEASE
% LocalWords:  uid capabilities capability EWOULDBLOCK notify dall'OR ACCESS st
% LocalWords:  pread readv MODIFY pwrite writev ftruncate creat mknod mkdir buf
% LocalWords:  symlink rename DELETE unlink rmdir ATTRIB chown chmod utime lio
% LocalWords:  MULTISHOT thread linkando librt layer aiocb asyncronous control
% LocalWords:  block ASYNCHRONOUS lseek fildes nbytes reqprio PRIORITIZED sigev
% LocalWords:  PRIORITY SCHEDULING opcode listio sigevent signo value function
% LocalWords:  aiocbp ENOSYS append error const EINPROGRESS fsync return ssize
% LocalWords:  DSYNC fdatasync SYNC cancel ECANCELED ALLDONE CANCELED suspend
% LocalWords:  NOTCANCELED list nent timout sig NOP WAIT NOWAIT size count iov
% LocalWords:  iovec vector EOPNOTSUPP EISDIR len memory mapping mapped swap NB
% LocalWords:  mmap length prot flags off MAP FAILED ANONYMOUS EACCES SHARED SH
% LocalWords:  only ETXTBSY DENYWRITE ENODEV filesystem EPERM EXEC noexec table
% LocalWords:  ENFILE lenght segment violation SIGSEGV FIXED msync munmap copy
% LocalWords:  DoS Denial Service EXECUTABLE NORESERVE LOCKED swapping stack fs
% LocalWords:  GROWSDOWN ANON POPULATE prefaulting SIGBUS fifo VME fork old SFD
% LocalWords:  exec atime ctime mtime mprotect addr mremap address new Failed
% LocalWords:  long MAYMOVE realloc VMA virtual Ingo Molnar remap pages pgoff
% LocalWords:  dall' fault cache linker prelink advisory discrectionary lock fl
% LocalWords:  flock shared exclusive operation dup inode linked NFS cmd ENOLCK
% LocalWords:  EDEADLK whence SEEK CUR type pid GETLK SETLK SETLKW HP EACCESS
% LocalWords:  switch bsd lockf mandatory SVr sgid group root mount mand TRUNC
% LocalWords:  SVID UX Documentation sendfile dnotify inotify NdA ppoll fds add
% LocalWords:  init EMFILE FIONREAD ioctl watch char pathname uint mask ENOSPC
% LocalWords:  CLOSE NOWRITE MOVE MOVED FROM TO rm wd event page ctl acquired
% LocalWords:  attribute Universe epoll Solaris kqueue level triggered Jonathan
% LocalWords:  Lemon BSDCON edge Libenzi kevent backporting epfd EEXIST ENOENT
% LocalWords:  MOD wait EPOLLIN EPOLLOUT EPOLLRDHUP SOCK EPOLLPRI EPOLLERR one
% LocalWords:  EPOLLHUP EPOLLET EPOLLONESHOT shot maxevents ctlv ALL DONT HPUX
% LocalWords:  FOLLOW ONESHOT ONLYDIR FreeBSD EIO caching sysctl instances name
% LocalWords:  watches IGNORED ISDIR OVERFLOW overflow UNMOUNT queued cookie ls
% LocalWords:  NUL sizeof casting printevent nread limits sysconf SC wrapper Di
% LocalWords:  splice result argument DMA controller zerocopy Linus Larry Voy
% LocalWords:  Jens Anxboe vmsplice seek ESPIPE GIFT TCP CORK MSG splicecp nr
% LocalWords:  nwrite segs patch readahead posix fadvise TC advice FADV NORMAL
% LocalWords:  SEQUENTIAL NOREUSE WILLNEED DONTNEED streaming fallocate EFBIG
% LocalWords:  POLLRDHUP half close pwait Gb madvise MADV ahead REMOVE tmpfs it
% LocalWords:  DONTFORK DOFORK shmfs preadv pwritev syscall linux loff head XFS
% LocalWords:  MERGEABLE EOVERFLOW prealloca hole FALLOC KEEP stat fstat union
% LocalWords:  conditions sigwait CLOEXEC signalfd sizemask SIGKILL SIGSTOP ssi
% LocalWords:  sigwaitinfo FifoReporter Windows ptr sigqueue named timerfd TFD
% LocalWords:  clockid CLOCK MONOTONIC REALTIME itimerspec interval Resource
% LocalWords:  ABSTIME gettime temporarily unavailable SIGINT SIGQUIT SIGTERM
% LocalWords:  sigfd fifofd break siginf names starting echo Message from Got
% LocalWords:  message kill received means exit TLOCK ULOCK EPOLLWAKEUP


%%% Local Variables: 
%%% mode: latex
%%% TeX-master: "gapil"
%%% End: 

