Esempio di copia con {{{splice}}} e fine della trattazione della stessa.
authorSimone Piccardi <piccardi@gnulinux.it>
Fri, 17 Aug 2007 12:34:48 +0000 (12:34 +0000)
committerSimone Piccardi <piccardi@gnulinux.it>
Fri, 17 Aug 2007 12:34:48 +0000 (12:34 +0000)
fileadv.tex
listati/splicecp.c [new file with mode: 0644]
sockctrl.tex
sources/splicecp.c

index 14e10b8af985199c54470814e0862c08685d43b9..4761523154cd2904385bb04b0817431e1f6ab982 100644 (file)
@@ -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 (file)
index 0000000..28a6705
--- /dev/null
@@ -0,0 +1,61 @@
+#define _GNU_SOURCE
+#include <fcntl.h>       /* 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;
+}
index 7bce251253307e488a21803192642346e343a13c..fc4a614d999ef24884a7e91837d871705f2f79b5 100644 (file)
@@ -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
index c19066fad7de0ee16208e3897bda299773ce3331..cbaa4e89725c1746f4a542ecd6aa1bcf5fe4e088 100644 (file)
@@ -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;
 }