From 154622c3382b558d8c0b7984ee76d96d09d44c71 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Fri, 17 Aug 2007 12:34:48 +0000 Subject: [PATCH] Esempio di copia con {{{splice}}} e fine della trattazione della stessa. --- fileadv.tex | 146 +++++++++++++++++++++++++++++++++++++++++---- listati/splicecp.c | 61 +++++++++++++++++++ sockctrl.tex | 10 ++-- sources/splicecp.c | 19 +++--- 4 files changed, 210 insertions(+), 26 deletions(-) create mode 100644 listati/splicecp.c diff --git a/fileadv.tex b/fileadv.tex index 14e10b8..4761523 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -3010,7 +3010,7 @@ definito la macro \macro{\_GNU\_SOURCE},\footnote{si ricordi che questa } \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 @@ -3041,32 +3041,154 @@ Ovviamente soltanto uno di questi due argomenti, e pi 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 diff --git a/listati/splicecp.c b/listati/splicecp.c new file mode 100644 index 0000000..28a6705 --- /dev/null +++ b/listati/splicecp.c @@ -0,0 +1,61 @@ +#define _GNU_SOURCE +#include /* file control functions */ +... + +int main(int argc, char *argv[]) +{ + int size = 4096; + int pipefd[2]; + int in_fd, out_fd; + int nread, nwrite; + ... + /* Main body */ + if ((argc - optind) != 2) { /* There must two argument */ + printf("Wrong number of arguments %d\n", argc - optind); + usage(); + } + /* open pipe, input and output file */ + in_fd = open(argv[optind], O_RDONLY); + if (in_fd < 0) { + printf("Input error %s on %s\n", strerror(errno), argv[optind]); + exit(EXIT_FAILURE); + } + out_fd = open(argv[optind+1], O_CREAT|O_RDWR|O_TRUNC, 0644); + if (out_fd < 0) { + 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 */ + while (1) { + nread = splice(in_fd, NULL, pipefd[1], NULL, size, + SPLICE_F_MOVE|SPLICE_F_MORE); + if (nread == 0) break; + if (nread < 0) { + if (errno == EINTR) { + continue; + } else { + perror("read error"); + exit(EXIT_FAILURE); + } + } + do { + nwrite = splice(pipefd[0], NULL, out_fd, NULL, nread, + SPLICE_F_MOVE|SPLICE_F_MORE); + if (nwrite == 0) continue; + if (nwrite < 0) { + if (errno == EINTR) + continue; + else { + perror("write error"); + exit(EXIT_FAILURE); + } + } + nread -= nwrite; + } while (nread); + } + return EXIT_SUCCESS; +} diff --git a/sockctrl.tex b/sockctrl.tex index 7bce251..fc4a614 100644 --- a/sockctrl.tex +++ b/sockctrl.tex @@ -4065,9 +4065,9 @@ questi per % ottimizzazione per l'uso come router. \item[\procrelfile{/proc/sys/net/core}{somaxconn}] imposta la dimensione - massima del \textit{backlog} della funzione \func{listen} (vedi - sez.~\ref{sec:TCP_func_listen}), e corrisponde al valore della costante - \const{SOMAXCONN}; il suo valore di default è 128. + massima utilizzabile per il \textit{backlog} della funzione \func{listen} + (vedi sez.~\ref{sec:TCP_func_listen}), e corrisponde al valore della + costante \const{SOMAXCONN}; il suo valore di default è 128. \end{basedescript} @@ -4082,8 +4082,8 @@ vengono usati all'interno di quest'ultimo (come ICMP, TCP e UDP) o a fianco dello stesso (come ARP). I file che consentono di controllare le caratteristiche specifiche del -protocollo IP in quanto tale, descritti anche nella pagina di manuale -accessibile con \texttt{man 7 ip}, sono i seguenti: +protocollo IP in quanto tale, che sono descritti anche nella relativa pagina +di manuale accessibile con \texttt{man 7 ip}, sono i seguenti: \begin{basedescript}{\desclabelwidth{3.5cm}\desclabelstyle{\nextlinelabel}} \item[\procrelfile{/proc/sys/net/ipv4}{ip\_default\_ttl}] imposta il valore di diff --git a/sources/splicecp.c b/sources/splicecp.c index c19066f..cbaa4e8 100644 --- a/sources/splicecp.c +++ b/sources/splicecp.c @@ -89,10 +89,6 @@ int main(int argc, char *argv[]) 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]); @@ -103,10 +99,15 @@ int main(int argc, char *argv[]) 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) { @@ -117,10 +118,10 @@ int main(int argc, char *argv[]) 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; @@ -131,7 +132,7 @@ int main(int argc, char *argv[]) } nread -= nwrite; debug("left %d bytes\n", nread); - } while (nread); + } } return EXIT_SUCCESS; } -- 2.30.2