descriptor (anche nullo) che sono attivi, e -1 in caso di errore, nel qual
caso \var{errno} assumerà uno dei valori:
\begin{errlist}
- \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno
+ \item[\errcode{EBADF}] si è specificato un file descriptor sbagliato in uno
degli insiemi.
- \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale.
- \item[\errcode{EINVAL}] Si è specificato per \param{ndfs} un valore negativo
+ \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale.
+ \item[\errcode{EINVAL}] si è specificato per \param{ndfs} un valore negativo
o un valore non valido per \param{timeout}.
\end{errlist}
ed inoltre \errval{ENOMEM}.
descriptor (anche nullo) che sono attivi, e -1 in caso di errore, nel qual
caso \var{errno} assumerà uno dei valori:
\begin{errlist}
- \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno
+ \item[\errcode{EBADF}] si è specificato un file descriptor sbagliato in uno
degli insiemi.
- \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale.
- \item[\errcode{EINVAL}] Si è specificato per \param{ndfs} un valore negativo
+ \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale.
+ \item[\errcode{EINVAL}] si è specificato per \param{ndfs} un valore negativo
o un valore non valido per \param{timeout}.
\end{errlist}
ed inoltre \errval{ENOMEM}.}
in caso di successo, o 0 se c'è stato un timeout e -1 in caso di errore,
ed in quest'ultimo caso \var{errno} assumerà uno dei valori:
\begin{errlist}
- \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno
+ \item[\errcode{EBADF}] si è specificato un file descriptor sbagliato in uno
degli insiemi.
- \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale.
- \item[\errcode{EINVAL}] Il valore di \param{nfds} eccede il limite
+ \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale.
+ \item[\errcode{EINVAL}] il valore di \param{nfds} eccede il limite
\macro{RLIMIT\_NOFILE}.
\end{errlist}
ed inoltre \errval{EFAULT} e \errval{ENOMEM}.}
in caso di successo, o 0 se c'è stato un timeout e -1 in caso di errore,
ed in quest'ultimo caso \var{errno} assumerà uno dei valori:
\begin{errlist}
- \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno
+ \item[\errcode{EBADF}] si è specificato un file descriptor sbagliato in uno
degli insiemi.
- \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale.
- \item[\errcode{EINVAL}] Il valore di \param{nfds} eccede il limite
+ \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale.
+ \item[\errcode{EINVAL}] il valore di \param{nfds} eccede il limite
\macro{RLIMIT\_NOFILE}.
\end{errlist}
ed inoltre \errval{EFAULT} e \errval{ENOMEM}.}
sotto controllo. L'argomento viene ignorato con l'operazione
\const{EPOLL\_CTL\_DEL}.\footnote{fino al kernel 2.6.9 era comunque richiesto
che questo fosse un puntatore valido, anche se poi veniva ignorato, a
- partire dal 2.6.9 si può specificare anche anche un valore \texttt{NULL}.}
+ partire dal 2.6.9 si può specificare anche un valore \texttt{NULL}.}
(analogo di \const{POLLIN}).\\
\const{EPOLLOUT} & Il file è pronto per le operazioni di scrittura
(analogo di \const{POLLOUT}).\\
- \const{EPOLLRDHUP} & l'altro capo di un socket di tipo
+ \const{EPOLLRDHUP} & L'altro capo di un socket di tipo
\const{SOCK\_STREAM} (vedi sez.~\ref{sec:sock_type})
ha chiuso la connessione o il capo in scrittura
della stessa (vedi sez.~\ref{sec:TCP_shutdown}).\\
\subsection{Il \textit{Signal driven I/O}}
\label{sec:file_asyncronous_operation}
+\itindbeg{signal~driven~I/O}
+
Abbiamo accennato in sez.~\ref{sec:file_open} che è possibile, attraverso
l'uso del flag \const{O\_ASYNC},\footnote{l'uso del flag di \const{O\_ASYNC} e
dei comandi \const{F\_SETOWN} e \const{F\_GETOWN} per \func{fcntl} è
% TODO fare esempio che usa O_ASYNC
+\itindend{signal~driven~I/O}
+
+
\subsection{I meccanismi di notifica asincrona.}
\label{sec:file_asyncronous_lease}
\textit{lease breaker}, cerca di eseguire una \func{open} o una
\func{truncate} sul file del quale l'\textit{holder} detiene il
\textit{lease}.
-
La notifica avviene in maniera analoga a come illustrato in precedenza per
l'uso di \const{O\_ASYNC}: di default viene inviato al \textit{lease holder}
il segnale \const{SIGIO}, ma questo segnale può essere modificato usando il
(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_control}. Per tutta questa serie di motivi in generale
+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.
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}; 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
+con l'interfaccia di \textit{epoll};\footnote{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} trattato in
+ sez.~\ref{sec:file_asyncronous_operation}.} 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,\footnote{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
\footnotesize
\begin{tabular}[c]{|l|c|p{10cm}|}
\hline
- \textbf{Flag} & & \textbf{Significato} \\
+ \textbf{Valore} & & \textbf{Significato} \\
\hline
\hline
\const{IN\_ACCESS} &$\bullet$& C'è stato accesso al file in
possibili.\\
\hline
\end{tabular}
- \caption{Le costanti che identificano i valori per la maschera binaria
+ \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}
\footnotesize
\begin{tabular}[c]{|l|p{10cm}|}
\hline
- \textbf{Flag} & \textbf{Significato} \\
+ \textbf{Valore} & \textbf{Significato} \\
\hline
\hline
\const{IN\_DONT\_FOLLOW}& Non dereferenzia \param{pathname} se questo è un
quelli per i file che contiene.\\
\hline
\end{tabular}
- \caption{Le costanti che identificano i valori per la maschera binaria
+ \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}
\footnotesize
\begin{tabular}[c]{|l|p{10cm}|}
\hline
- \textbf{Flag} & \textbf{Significato} \\
+ \textbf{Valore} & \textbf{Significato} \\
\hline
\hline
\const{IN\_IGNORED} & L'osservatore è stato rimosso, sia in maniera
osservazione è stato smontato.\\
\hline
\end{tabular}
- \caption{Le costanti che identificano i flag aggiuntivi usati nella maschera
+ \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}
\index{file!inotify|)}
-% TODO inserire anche eventfd (vedi http://lwn.net/Articles/233462/)
-% e le restanti signalfd e timerfd introdotte con il 2.6.22
-% o trovargli un posto migliore
-
-
\subsection{L'interfaccia POSIX per l'I/O asincrono}
\label{sec:file_asyncronous_io}
\bodydesc{Le funzioni restituiscono 0 in caso di successo, e -1 in caso di
errore, nel qual caso \var{errno} assumerà uno dei valori:
\begin{errlist}
- \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato.
- \item[\errcode{ENOSYS}] La funzione non è implementata.
- \item[\errcode{EINVAL}] Si è specificato un valore non valido per i campi
+ \item[\errcode{EBADF}] si è specificato un file descriptor sbagliato.
+ \item[\errcode{ENOSYS}] la funzione non è implementata.
+ \item[\errcode{EINVAL}] si è specificato un valore non valido per i campi
\var{aio\_offset} o \var{aio\_reqprio} di \param{aiocbp}.
- \item[\errcode{EAGAIN}] La coda delle richieste è momentaneamente piena.
+ \item[\errcode{EAGAIN}] la coda delle richieste è momentaneamente piena.
\end{errlist}
}
\end{functions}
completate, e -1 in caso di errore nel qual caso \var{errno} assumerà uno
dei valori:
\begin{errlist}
- \item[\errcode{EAGAIN}] Nessuna operazione è stata completata entro
+ \item[\errcode{EAGAIN}] nessuna operazione è stata completata entro
\param{timeout}.
- \item[\errcode{ENOSYS}] La funzione non è implementata.
- \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale.
+ \item[\errcode{ENOSYS}] la funzione non è implementata.
+ \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale.
\end{errlist}
}
\end{prototype}
\bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di
errore, nel qual caso \var{errno} assumerà uno dei valori:
\begin{errlist}
- \item[\errcode{EAGAIN}] Nessuna operazione è stata completata entro
+ \item[\errcode{EAGAIN}] nessuna operazione è stata completata entro
\param{timeout}.
- \item[\errcode{EINVAL}] Si è passato un valore di \param{mode} non valido
+ \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.
- \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale.
+ \item[\errcode{ENOSYS}] la funzione non è implementata.
+ \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale.
\end{errlist}
}
\end{prototype}
in caso di successo, e \const{MAP\_FAILED} (-1) in caso di errore, nel
qual caso \var{errno} assumerà uno dei valori:
\begin{errlist}
- \item[\errcode{EBADF}] Il file descriptor non è valido, e non si è usato
+ \item[\errcode{EBADF}] il file descriptor non è valido, e non si è usato
\const{MAP\_ANONYMOUS}.
\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{EINVAL}] I valori di \param{start}, \param{length} o
+ \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).
- \item[\errcode{ETXTBSY}] Si è impostato \const{MAP\_DENYWRITE} ma
+ \item[\errcode{ETXTBSY}] si è impostato \const{MAP\_DENYWRITE} ma
\param{fd} è aperto in scrittura.
- \item[\errcode{EAGAIN}] Il file è bloccato, o si è bloccata troppa memoria
+ \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{ENOMEM}] Non c'è memoria o si è superato il limite sul
+ \item[\errcode{ENOMEM}] non c'è memoria o si è superato il limite sul
numero di mappature possibili.
- \item[\errcode{ENODEV}] Il filesystem di \param{fd} non supporta il memory
+ \item[\errcode{ENODEV}] il filesystem di \param{fd} non supporta il memory
mapping.
- \item[\errcode{EPERM}] L'argomento \param{prot} ha richiesto
+ \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{ENFILE}] Si è superato il limite del sistema sul numero di
+ \item[\errcode{ENFILE}] si è superato il limite del sistema sul numero di
file aperti (vedi sez.~\ref{sec:sys_resource_limit}).
\end{errlist}
}
\label{tab:file_mmap_prot}
\end{table}
-
Il valore dell'argomento \param{prot} indica la protezione\footnote{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
che si chiama una \textit{segment violation}, e la relativa emissione del
segnale \const{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_flag}; il valore specificato deve essere
+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.
L'argomento \param{flags} specifica infine qual è il tipo di oggetto mappato,
pagina, ed in generale queste potranno non corrispondere alle dimensioni
effettive del file o della sezione che si vuole mappare.
-\footnotetext[20]{Dato che tutti faranno riferimento alle stesse pagine di
+\footnotetext[68]{dato che tutti faranno riferimento alle stesse pagine di
memoria.}
-\footnotetext[21]{L'uso di questo flag con \const{MAP\_SHARED} è stato
+\footnotetext[69]{l'uso di questo flag con \const{MAP\_SHARED} è stato
implementato in Linux a partire dai kernel della serie 2.4.x; esso consente
di creare segmenti di memoria condivisa e torneremo sul suo utilizzo in
sez.~\ref{sec:ipc_mmap_anonymous}.}
\begin{figure}[!htb]
\centering
- \includegraphics[width=13cm]{img/mmap_boundary}
+ \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}
\begin{figure}[htb]
\centering
- \includegraphics[width=13cm]{img/mmap_exceed}
+ \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}
\bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di
errore nel qual caso \var{errno} assumerà uno dei valori:
\begin{errlist}
- \item[\errcode{EINVAL}] O \param{start} non è multiplo di
+ \item[\errcode{EINVAL}] o \param{start} non è multiplo di
\const{PAGE\_SIZE}, o si è specificato un valore non valido per
\param{flags}.
- \item[\errcode{EFAULT}] L'intervallo specificato non ricade in una zona
+ \item[\errcode{EFAULT}] l'intervallo specificato non ricade in una zona
precedentemente mappata.
\end{errlist}
}
siano invalidate.\\
\hline
\end{tabular}
- \caption{Valori dell'argomento \param{flag} di \func{msync}.}
+ \caption{Le costanti che identificano i bit per la maschera binaria
+ dell'argomento \param{flag} di \func{msync}.}
\label{tab:file_mmap_rsync}
\end{table}
\bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di
errore nel qual caso \var{errno} assumerà uno dei valori:
\begin{errlist}
- \item[\errcode{EINVAL}] L'intervallo specificato non ricade in una zona
+ \item[\errcode{EINVAL}] l'intervallo specificato non ricade in una zona
precedentemente mappata.
\end{errlist}
}
\bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di
errore, nel qual caso \var{errno} assumerà uno dei valori:
\begin{errlist}
- \item[\errcode{EINVAL}] Si è usato un valore non valido per uno degli
+ \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}
\itindend{memory~mapping}
-
-
\subsection{I/O vettorizzato: \func{readv} e \func{writev}}
\label{sec:file_multiple_io}
La prima funzione che si pone l'obiettivo di ottimizzare il trasferimento dei
dati fra due file descriptor è \funcd{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,\footnote{la si
+ 2.1.} la funzione è presente in diverse versioni di Unix,\footnote{la si
ritrova ad esempio in FreeBSD, HPUX ed altri Unix.} ma non è presente né in
POSIX.1-2001 né in altri standard,\footnote{pertanto si eviti di utilizzarla
se si devono scrivere programmi portabili.} per cui per essa vengono
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
+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} è
+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 è utilizzabile su un qualunque file
descriptor, e permette di sostituire la invocazione successiva di una
-\func{read} ed una \func{write} (e l'allocazione del relativo buffer) con una
+\func{read} e una \func{write} (e l'allocazione del relativo buffer) con una
sola chiamata a \funcd{sendfile}. In questo modo si può diminuire il numero di
chiamate al sistema e risparmiare in trasferimenti di dati da kernel space a
user space e viceversa. La massima utilità della funzione si ha comunque per
-il trasferimento di dati da un file su disco ad socket di rete,\footnote{il
- caso classico del lavoro 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.\footnote{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.}
-
-Con i kernel della serie 2.6 ci si è accorti però che, a parte quest'ultimo
-caso, l'uso di \func{sendfile} non sempre portava significativi miglioramenti
-delle prestazioni rispetto all'uso in sequenza di \func{read} e \func{write},
-\footnote{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 user space che ha una
- maggiore conoscenza su come questi sono strutturati.} e che anzi in certi
-casi si avevano dei peggioramenti, questo ha portato alla
-decisione\footnote{per alcune motivazioni di questa scelta si può fare
- riferimento a quanto illustrato da Linus Torvalds in
- \href{http://www.cs.helsinki.fi/linux/linux-kernel/2001-03/0200.html}
- {\texttt{http://www.cs.helsinki.fi/linux/linux-kernel/2001-03/0200.html}}.}
-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 si avrà
-un errore di \errcode{EINVAL}.
-
-Nonostante i limiti illustrati resta comunque il dubbio se la scelta di
-disabilitare \func{sendfile} per il trasferimento di dati fra file di dati sia
-davvero corretta; la funzione infatti se non altro consentirebbe di
-semplificare l'interfaccia per la copia dei dati, evitando di dover gestire
-l'allocazione di un buffer temporaneo per il loro trasferimento in tutti quei
-casi in cui non c'è necessità di fare controlli sugli stessi. Inoltre essa
-avrebbe comunque il vantaggio di evitare trasferimenti di dati da e verso
-l'user space.
-
-Il dubbio è stato rimosso con l'introduzione della system call
-\func{splice},\footnote{avvenuto a partire dal kernel 2.6.17.} il cui scopo è
-appunto quello di fornire un meccanismo generico per il trasferimento di dati
-da o verso un file utilizzando un buffer intermedio gestito direttamente dal
-kernel. Lo scopo della funzione può sembrare lo stesso di \func{sendfile}, ma
-in realtà esse sono profondamente diverse nel loro meccanismo di
-funzionamento; \func{sendfile} infatti, come accennato, non necessita affatto
-(anzi nel caso di Linux viene sostanzialmente usata solo in questo caso) di
-avere a disposizione un buffer interno, perché esegue un trasferimento diretto
-di dati; questo la rende in generale molto più efficiente, ma anche limitata
-nelle sue applicazioni.
+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,\footnote{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 è accorti 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},\footnote{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
+ user space che ha una conoscenza diretta su come questi sono strutturati.} e
+che anzi 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 \href{http://www.cs.helsinki.fi/linux/linux-kernel/2001-03/0200.html}
+ {\textsf{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} darà luogo ad un errore di \errcode{EINVAL}.
+
+Nonostante ci possano essere casi in cui \func{sendfile} non migliora le
+prestazioni, le motivazioni addotte non convincono del tutto e resta il dubbio
+se la scelta di disabilitarla sempre per il trasferimento di dati fra file di
+dati sia davvero corretta. Se ci sono peggioramenti di prestazioni infatti si
+può sempre fare ricorso all'uso successivo di, ma lasciare a disposizione la
+funzione consentirebbe se non altro, anche in assenza di guadagni di
+prestazioni, di semplificare la gestione della copia dei dati fra file,
+evitando di dover gestire l'allocazione di un buffer temporaneo per il loro
+trasferimento; inoltre si avrebbe comunque il vantaggio di evitare inutili
+trasferimenti di dati da kernel space a user space e viceversa.
+
+Questo dubbio si può comunque ritenere superato con l'introduzione, avvenuto a
+partire dal kernel 2.6.17, della nuova 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}.\footnote{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 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'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.\footnote{e 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 ne differisce poi tanto,
- quello che la rende davvero diversa è stata la reinterpretazione che ne è
- stata fatta nell'implementazione su Linux realizzata da Jens Anxboe, di cui
- si può trovare un buon riassunto in \href{http://kerneltrap.org/node/6505}
- {\texttt{http://kerneltrap.org/node/6505}}.} si tratta semplicemente di una
-funzione che consente di fare delle operazioni di trasferimento dati da e
-verso un buffer interamente gestito in kernel space, in maniera del tutto
-generica. In questo caso il cuore della funzione (e delle affini
-\func{vmsplice} e \func{tee}, che tratteremo più avanti) è appunto il buffer
-in kernel space; questo è anche quello che ne ha semplificato
-l'adozione,\footnote{la funzione infatti non è definita in nessuno standard,
- e, allo stato attuale è disponibile soltanto su Linux.} perché
-l'infrastruttura per la gestione di un buffer in kernel space è presente fin
-dagli albori di Unix per la realizzazione delle \textit{pipe} (tratteremo
-l'argomento in sez.~\ref{sec:ipc_unix}). Dal punto di vista concettuale allora
-\func{splice} non è che un'altra interfaccia con cui esporre in userspace
-l'oggetto \textsl{buffer in kernel space}.
+ 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 \href{http://kerneltrap.org/node/6505}
+ {\textsf{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 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
+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 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 dove appoggiare i dati che vengono trasferiti da un capo
-all'altro della stessa (vedi fig.~\ref{fig:ipc_pipe_singular}) per creare un
-meccanismo di comunicazione fra processi, nel caso di \funcd{splice} il buffer
+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 infatti è una
-interfaccia generica che consente di trasferire dati da un buffer ad un file o
-viceversa; il suo prototipo, accessibile solo avendo definito
-\macro{\_GNU\_SOURCE},\footnote{ovviamente, essendo come detto la funzione
- totalmente specifica di Linux, essa non è prevista da nessuno standard e
- deve essere evitata se si vogliono scrivere programmi portabili.} è:
+destinazione dei dati che vengono letti da un file. La funzione \funcd{splice}
+fornisce quindi una interfaccia generica che consente di trasferire dati da un
+buffer ad un file o viceversa; il suo prototipo, 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{functions}
\headdecl{fcntl.h}
- \funcdecl{}
+ \funcdecl{long splice(int fd\_in, off\_t *off\_in, int fd\_out, off\_t
+ *off\_out, size\_t len, unsigned int flags)}
Trasferisce dati da un file verso una pipe o viceversa.
successo e $-1$ in caso di 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}.
+ \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 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{seek}.
+ \item[\errcode{ENOMEM}] non c'è memoria sufficiente per l'operazione
+ richiesta.
+ \item[\errcode{ESPIPE}] o \param{off\_in} o \param{off\_out} non sono
+ \const{NULL} ma il corrispondente file descriptor è una \textit{pipe}.
+ \end{errlist}
+ }
+\end{functions}
+
+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.\footnote{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 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;\footnotemark 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.\footnotemark 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;\footnotemark
+ 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}
+
+\footnotetext{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 pipe o il buffer non corrisponda a pagine intere esse saranno
+ comunque copiate.}
+
+\footnotetext{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}.}
+
+\footnotetext{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.}
+
+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 user space. 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 e le funzioni di ausilio è riportato in
+fig.~\ref{fig:splice_example}.
+
+Lo scopo del programma è quello di eseguire la copia dei con \func{splice},
+questo significa che si dovrà usare la funzione due volte, prima per leggere i
+dati e poi per scriverli, appoggiandosi ad un buffer in kernel space (vale a
+dire ad una \textit{pipe}); lo schema del flusso dei dati è illustrato in
+fig.~\ref{fig:splicecp_data_flux}.
+
+\begin{figure}[htb]
+ \centering
+ \includegraphics[height=6cm]{img/splice_copy}
+ \caption{Struttura del flusso di dati usato dal programma \texttt{splicecp}.}
+ \label{fig:splicecp_data_flux}
+\end{figure}
+
+Una volta trattate le opzioni il programma verifica che restino
+(\texttt{\small 13--16}) i due argomenti che indicano il file sorgente ed il
+file destinazione. Il passo successivo è aprire il file sorgente
+(\texttt{\small 18--22}), quello di destinazione (\texttt{\small 23--27}) ed
+infine (\texttt{\small 28--31}) la \textit{pipe} che verrà usata come buffer.
+
+\begin{figure}[!phtb]
+ \footnotesize \centering
+ \begin{minipage}[c]{15cm}
+ \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 33--58}) inizia con la lettura dal file
+sorgente tramite la prima \func{splice} (\texttt{\small 34--35}), 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).
+
+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 36}) 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 37--44}) c'è stato un
+errore ed allora si ripete la lettura (\texttt{\small 36}) se questo è dovuto
+ad una interruzione, o altrimenti si esce con un messaggio di errore
+(\texttt{\small 41--43}).
+
+Una volta completata con successo la lettura si avvia il ciclo di scrittura
+(\texttt{\small 45--57}); questo inizia (\texttt{\small 46--47}) 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 48--55}). Infine si chiude il ciclo di scrittura sottraendo
+(\texttt{\small 57}) 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 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 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 altre
+due 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 kernel space; benché queste non
+attengono strettamente ad operazioni di trasferimento dati fra file
+descriptor, le tratteremo qui.
+
+La prima funzione, \funcd{vmsplice}, è la più simile a \func{splice} e come
+indica il suo nome consente di trasferire i dati dalla memoria di un processo
+verso una \textit{pipe}, il suo prototipo è:
+\begin{functions}
+ \headdecl{fcntl.h}
+ \headdecl{sys/uio.h}
+
+ \funcdecl{long vmsplice(int fd, const struct iovec *iov, unsigned long
+ nr\_segs, unsigned int flags)}
+
+ Trasferisce dati dalla memoria di un processo verso una \textit{pipe}.
+
+ \bodydesc{La funzione restituisce il numero di byte trasferiti in caso di
+ successo e $-1$ in caso di 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}
- ed inoltre \errcode{EBADF} e \errcode{EFAULT}.
}
\end{functions}
+La \textit{pipe} 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 zone di memoria devono
+essere trasferita si deve utilizzare un vettore di strutture \struct{iovec}
+(vedi fig.~\ref{fig:file_iovec}), con le stesse con cui le si usano per l'I/O
+vettorizzato; 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
+pipe, in generale (se i dati una volta creati non devono essere riutilizzati)
+è opportuno utilizzare il flag \const{SPLICE\_F\_GIFT}; questo fa si che il
+kernel possa rimuovere le relative pagine dallo spazio degli indirizzi del
+processo, e scaricarle nella cache, 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 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 kernel space, la funzione consente di eseguire una copia del
+contenuto del buffer stesso. Il prototipo di \funcd{tee} è il seguente:
+\begin{functions}
+ \headdecl{fcntl.h}
+
+ \funcdecl{long tee(int fd\_in, int fd\_out, size\_t len, unsigned int
+ flags)}
+
+ Duplica \param{len} byte da una \textit{pipe} ad un'altra.
+ \bodydesc{La funzione restituisce il numero di byte copiati in caso di
+ successo e $-1$ in caso di 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{functions}
-% TODO documentare le funzioni tee e splice
-% http://kerneltrap.org/node/6505 e http://lwn.net/Articles/178199/ e
-% http://lwn.net/Articles/179492/
-% e http://en.wikipedia.org/wiki/Splice_(system_call)
+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 \func{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}).
+
+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 pipe è stato
+chiuso.\footnote{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 standard input sullo 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}[!htbp]
+ \footnotesize \centering
+ \begin{minipage}[c]{15cm}
+ \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 (\texttt{\small 10--35}) si cura semplicemente di
+controllare (\texttt{\small 11--14}) che sia stato fornito almeno un argomento
+(il nome del file su cui scrivere), di aprirlo ({\small 15--19}) e che sia lo
+standard input (\texttt{\small 20--27}) che lo standard output (\texttt{\small
+ 28--35}) corrispondano ad una \textit{pipe}.
+
+Il ciclo principale (\texttt{\small 37--58}) inizia con la chiamata a
+\func{tee} che duplica il contenuto dello standard input sullo standard output
+(\texttt{\small 39}), 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 40}), se
+è negativo c'è stato un errore, ed allora si ripete la chiamata se questo è
+dovuto ad una interruzione (\texttt{\small 42--44}) o si stampa un messaggio
+di errore e si esce negli altri casi (\texttt{\small 44--47}).
+
+Una volta completata la copia dei dati sullo standard output si possono
+estrarre dalla standard input e scrivere sul file, di nuovo su usa un ciclo di
+scrittura (\texttt{\small 50--58}) in cui si ripete una chiamata a
+\func{splice} (\texttt{\small 51}) 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 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 \href{http://lwn.net/Articles/118750/}
+ {\textsf{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}
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.
+il loro accesso ai dati dei file e controllare la gestione del relativo
+\textit{caching}.
+
+Una prima funzione che può essere utilizzata per modificare la gestione
+ordinaria dell'I/O su un file è \funcd{readahead},\footnote{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{functions}
+ \headdecl{fcntl.h}
+
+ \funcdecl{ssize\_t readahead(int fd, off64\_t *offset, size\_t count)}
+
+ Esegue una lettura preventiva del contenuto di un file in cache.
+
+ \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di
+ errore, nel qual caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
+ \item[\errcode{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 pipe o un socket).
+ \end{errlist}
+ }
+\end{functions}
+
+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
+\index{memoria~virtuale} memoria virtuale ed il meccanismo della
+\index{paginazione} 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} arrotondati di conseguenza.
+
+La funzione estende quello che è un comportamento normale del
+kernel\footnote{per ottimizzare gli accessi al disco il kernel quando si legge
+ un file, aspettandosi che l'accesso prosegua, esegue sempre una lettura
+ anticipata di una certa quantità di dati; questo meccanismo viene chiamato
+ \textit{readahead}, da cui deriva il nome della funzione.} effettuando la
+lettura in cache della sezione richiesta e bloccandosi 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 del programma tutte le volte che si conosce in anticipo quanti
+dati saranno necessari in seguito. Si potrà così concentrare in un unico
+momento (ad esempio in fase di inizializzazione) la lettura, così da ottenere
+una migliore risposta nelle operazioni successive.
+
+Il concetto di \func{readahead} viene generalizzato nello standard
+POSIX.1-2001 dalla funzione \funcd{posix\_fadvise},\footnote{anche se
+ l'argomento \param{len} è stato modificato da \ctyp{size\_t} a \ctyp{off\_t}
+ nella revisione POSIX.1-2003 TC5.} che consente di ``\textsl{avvisare}'' il
+kernel sulle modalità con cui si intende accedere nel futuro ad una certa
+porzione di un file,\footnote{la funzione però è stata introdotta su Linux
+ solo a partire dal kernel 2.5.60.} così che esso possa provvedere le
+opportune ottimizzazioni; il suo prototipo, che può è disponibile solo se si
+definisce la macro \macro{\_XOPEN\_SOURCE} ad almeno 600, è:
+\begin{functions}
+ \headdecl{fcntl.h}
+
+ \funcdecl{int posix\_fadvise(int fd, off\_t offset, off\_t len, int advice)}
+
+ Dichiara al kernel le future modalità di accesso ad un file.
+
+ \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di
+ errore, nel qual caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
+ \item[\errcode{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 pipe o un socket).
+ \item[\errcode{ESPIPE}] previsto dallo standard se \param{fd} è una pipe o
+ un socket (ma su Linux viene restituito \errcode{EINVAL}).
+ \end{errlist}
+ }
+\end{functions}
+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.\footnote{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}. Si tenga presente comunque
+che la funzione dà soltanto un avvertimento, non esiste nessun vincolo per il
+kernel, che utilizza semplicemente l'informazione.
-% TODO documentare \func{madvise}
-% TODO documentare \func{mincore}
+\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 dei bit dell'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}
+
+Anche \func{posix\_fadvise} si appoggia al sistema della memoria virtuale ed
+al meccanismo standard del \textit{readahead} utilizzato dal kernel; in
+particolare con \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}
+danno invece 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.\footnote{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, \func{posix\_fallocate},\footnote{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{functions}
+ \headdecl{fcntl.h}
+
+ \funcdecl{int posix\_fallocate(int fd, off\_t offset, off\_t len)}
+
+ Richiede la allocazione di spazio disco per un file.
+
+ \bodydesc{La funzione restituisce 0 in caso di successo e direttamente un
+ codice di errore, in caso di fallimento, in questo caso \var{errno} non
+ viene impostata, ma sarà restituito 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 pipe.
+ \end{errlist}
+ }
+\end{functions}
+
+La funzione si 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 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 scrittura nella regione indicata non fallirà per
+mancanza di spazio disco.
+
+
+
% TODO documentare \func{posix\_fadvise}
% vedi http://insights.oetiker.ch/linux/fadvise.html
% questo tread? http://www.ussg.iu.edu/hypermail/linux/kernel/0703.1/0032.html
% TODO documentare \func{fallocate}, introdotta con il 2.6.23
% vedi http://lwn.net/Articles/226710/ e http://lwn.net/Articles/240571/
+% http://kernelnewbies.org/Linux_2_6_23
+% \func{fallocate} con il 2.6.25 supporta pure XFS
%\subsection{L'utilizzo delle porte di I/O}
output sul file.
In tutti questi casi il \textit{file locking} è la tecnica che permette di
-evitare le \textit{race condition} \itindex{race~condition}, attraverso una
+evitare le \itindex{race~condition} \textit{race condition}, attraverso una
serie di funzioni che permettono di bloccare l'accesso al file da parte di
altri processi, così da evitare le sovrapposizioni, e garantire la atomicità
delle operazioni di scrittura.
\bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di
errore, nel qual caso \var{errno} assumerà uno dei valori:
\begin{errlist}
- \item[\errcode{EWOULDBLOCK}] Il file ha già un blocco attivo, e si è
+ \item[\errcode{EWOULDBLOCK}] il file ha già un blocco attivo, e si è
specificato \const{LOCK\_NB}.
\end{errlist}
}
\bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di
errore, nel qual caso \var{errno} assumerà uno dei valori:
\begin{errlist}
- \item[\errcode{EACCES}] L'operazione è proibita per la presenza di
+ \item[\errcode{EACCES}] l'operazione è proibita per la presenza di
\textit{file lock} da parte di altri processi.
- \item[\errcode{ENOLCK}] Il sistema non ha le risorse per il locking: ci
+ \item[\errcode{ENOLCK}] il sistema non ha le risorse per il locking: ci
sono troppi segmenti di lock aperti, si è esaurita la tabella dei lock,
o il protocollo per il locking remoto è fallito.
- \item[\errcode{EDEADLK}] Si è richiesto un lock su una regione bloccata da
+ \item[\errcode{EDEADLK}] si è richiesto un lock su una regione bloccata da
un altro processo che è a sua volta in attesa dello sblocco di un lock
mantenuto dal processo corrente; si avrebbe pertanto un
\itindex{deadlock} \textit{deadlock}. Non è garantito che il sistema
riconosca sempre questa situazione.
- \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale prima
+ \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale prima
di poter acquisire un lock.
\end{errlist}
ed inoltre \errval{EBADF}, \errval{EFAULT}.
\bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di
errore, nel qual caso \var{errno} assumerà uno dei valori:
\begin{errlist}
- \item[\errcode{EWOULDBLOCK}] Non è possibile acquisire il lock, e si è
+ \item[\errcode{EWOULDBLOCK}] non è possibile acquisire il lock, e si è
selezionato \const{LOCK\_NB}, oppure l'operazione è proibita perché il
file è mappato in memoria.
- \item[\errcode{ENOLCK}] Il sistema non ha le risorse per il locking: ci
+ \item[\errcode{ENOLCK}] il sistema non ha le risorse per il locking: ci
sono troppi segmenti di lock aperti, si è esaurita la tabella dei lock.
\end{errlist}
ed inoltre \errval{EBADF}, \errval{EINVAL}.
% 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
-% LocalWords: splice result argument DMA controller zerocopy Linus
+% 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
%%% Local Variables:
%%% mode: latex
%%% TeX-master: "gapil"
%%% End:
+% LocalWords: SEQUENTIAL NOREUSE WILLNEED DONTNEED streaming fallocate EFBIG