}
\end{functions}
-La funzione esegue un trasferimento di \param{count} byte dal file descriptor
+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
fa riferimento al file descriptor non associato alla \textit{pipe}, può essere
specificato come valore non nullo.
-Infine l'argomento \param{flag} 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}.
-
+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{vmslice} 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{8cm}|}
+ \begin{tabular}[c]{|l|p{10cm}|}
\hline
\textbf{Valore} & \textbf{Significato} \\
\hline
\hline
- \const{SPLICE\_F\_MOVE} & .\\
- \const{SPLICE\_F\_NONBLOCK}& .\\
- \const{SPLICE\_F\_MORE} & .\\
- \const{SPLICE\_F\_GIFT} & .\\
+ \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{vmslice} 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{flag} di \func{slice}, \func{vmslice} e \func{tee}.}
+ dell'argomento \param{flags} di \func{slice}, \func{vmslice} 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 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}[!htbp]
+ \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--59}) 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} è trattato in dettaglio in sez.~\ref{sec:ipc_unix},
+nell'ottica dell'uso di \func{splice} questa operazione corrisponde
+semplicemente alla copia 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
+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. In caso di valore negativo c'è stato un errore ed allora si ripete la
+lettura se questo è dovuto ad una interruzione, o si esce altrimenti
+(\texttt{\small 37--44}).
+
+Una volta completata con successo la lettura si avvia il ciclo di scrittura
+(\texttt{\small 45--58}); 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 sia il capo in lettura della
+\textit{pipe} ed il terzo il file descriptor del file di destinazione.
+
+Di nuovo si controlla il numero di byte effettivamente scritti restituito in
+\var{nwrite} e se nullo (\texttt{\small 48}) si ripete la scrittura (in realtà
+questo non avverrà mai), in caso di errore (\texttt{\small 49--56}) al solito
+si ripete la scrittura se questo è dovuto a una interruzione o si esce con un
+messaggio negli altri casi.
+
+Infine si chiude il ciclo di scrittura sottraendo (\texttt{\small 57}) il
+numero di byte scritti a quelli letti, così che il ciclo di scrittura venga
+ripetuto (\texttt{\small 58}) qualora la chiamata a \func{splice} non abbia
+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 file, solo che in questo caso non è stato necessario allocare nessun
+buffer e non si è trasferito nessun dato in user space.
% TODO documentare le funzioni tee e splice
usage();
}
/* open pipe, input and output file */
- if (pipe(pipefd) == -1) {
- perror("Cannot create buffer pipe");
- exit(EXIT_FAILURE);
- }
in_fd = open(argv[optind], O_RDONLY);
if (in_fd < 0) {
printf("Input error %s on %s\n", strerror(errno), argv[optind]);
printf("Cannot open %s, error %s\n", argv[optind+1], strerror(errno));
exit(EXIT_FAILURE);
}
+ if (pipe(pipefd) == -1) {
+ perror("Cannot create buffer pipe");
+ exit(EXIT_FAILURE);
+ }
/* copy loop */
debug("Size %d\n", size);
while (1) {
- nread = splice(in_fd, NULL, pipefd[1], NULL, size, 0);
+ nread = splice(in_fd, NULL, pipefd[1], NULL, size,
+ SPLICE_F_MOVE|SPLICE_F_MORE);
debug("read %d bytes\n", nread);
if (nread == 0) break;
if (nread < 0) {
exit(EXIT_FAILURE);
}
}
- do {
- nwrite = splice(pipefd[0], NULL, out_fd, NULL, nread, 0);
+ while (nread > 0) {
+ nwrite = splice(pipefd[0], NULL, out_fd, NULL, nread,
+ SPLICE_F_MOVE|SPLICE_F_MORE);
debug("write %d bytes\n", nwrite);
- if (nwrite == 0) continue;
if (nwrite < 0) {
if (errno == EINTR)
continue;
}
nread -= nwrite;
debug("left %d bytes\n", nread);
- } while (nread);
+ }
}
return EXIT_SUCCESS;
}