Trattate anche {{{vmslice}}} e {{{tee}}}, e scritto esempio di
authorSimone Piccardi <piccardi@gnulinux.it>
Fri, 17 Aug 2007 23:25:13 +0000 (23:25 +0000)
committerSimone Piccardi <piccardi@gnulinux.it>
Fri, 17 Aug 2007 23:25:13 +0000 (23:25 +0000)
programma che usa {{{tee}}}.

fileadv.tex
listati/splicecp.c

index 404e946a4cd2859a5a8247abc6d869d0e10633a3..797ecfbf9e4eccfeddc55ff2c59c4798b9121466 100644 (file)
@@ -3045,7 +3045,7 @@ 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
+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}.
 
@@ -3077,7 +3077,7 @@ descrizioni complete di tutti i valori possibili anche quando, come per
                                  socket.\footnotemark Attualmente viene usato
                                  solo da \func{splice}, potrà essere
                                  implementato in futuro anche per
-                                 \func{vmslice} e \func{tee}.\\
+                                 \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
@@ -3091,7 +3091,8 @@ descrizioni complete di tutti i valori possibili anche quando, come per
     \hline
   \end{tabular}
   \caption{Le costanti che identificano i bit della maschera binaria
-    dell'argomento \param{flags} di \func{slice}, \func{vmslice} e \func{tee}.}
+    dell'argomento \param{flags} di \func{splice}, \func{vmsplice} e
+    \func{tee}.} 
   \label{tab:splice_flag}
 \end{table}
 
@@ -3149,7 +3150,7 @@ infine (\texttt{\small 28--31}) la \textit{pipe} che verr
   \label{fig:splice_example}
 \end{figure}
 
-Il ciclo principale (\texttt{\small 33--59}) inizia con la lettura dal file
+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
@@ -3162,14 +3163,16 @@ 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}).
+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--58}); questo inizia (\texttt{\small 46--47}) con la
+(\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
@@ -3177,26 +3180,140 @@ 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 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.
+\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 file, solo che in questo caso non è stato necessario allocare nessun
+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 trasferiemento 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}
+  }
+\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 sudetto 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 indifizzi 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}
+
+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 (la funzione in questo caso non si blocca, a
+differenza di quanto avverrebbe per una normale lettura). Un esempio di
+realizzazione del comando \texttt{tee} usando questa funzione, ripreso da
+quello fornito nella pagina di manuale, è riportato in fig..
+
+
+
+Infine come nota finale riguardo \func{splice}, \func{vmsplice} e \func{tee}
+occorre sottolineare che benché si sia parlato finora di trasferimenti o copie
+di dati in realtà nella loro implementazione non è affatto detto che questi
+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/}
+  {\texttt{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, pertanto anche con \func{tee} non viene
+mai copiato nessun byte, vengono semplicemente copiati i puntatori.
+
 
 % TODO documentare le funzioni tee e splice
 % http://kerneltrap.org/node/6505 e http://lwn.net/Articles/178199/ e 
-% http://lwn.net/Articles/179492/
+% http://lwn.net/Articles/179492/ e http://lwn.net/Articles/181169
 % e http://en.wikipedia.org/wiki/Splice_(system_call)
 
 
@@ -4135,7 +4252,8 @@ possibilit
 % 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:  splice result argument DMA controller zerocopy Linus Larry Voy
+% LocalWords:  Jens Anxboe vmsplice seek ESPIPE GIFT TCP CORK MSG splicecp
 
 
 %%% Local Variables: 
index 28a6705df7122c82d9f163ff69d2283cf883eb44..0da067bc9c953e5258deed07475e496b6b4dd50c 100644 (file)
@@ -42,10 +42,9 @@ int main(int argc, char *argv[])
                exit(EXIT_FAILURE);
            } 
        }
-       do {
+       while (nread > 0) {
            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;
@@ -55,7 +54,7 @@ int main(int argc, char *argv[])
                }
            }
            nread -= nwrite;
-       } while (nread);
+       }
     }
     return EXIT_SUCCESS;
 }