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
 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}.
 
 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
                                  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
     \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
     \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}
 
   \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}
 
   \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
 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
 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
 
 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
 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
 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
 
 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.
 
 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 
 
 % 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)
 
 
 % 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:  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: 
 
 
 %%% Local Variables: 
index 28a6705df7122c82d9f163ff69d2283cf883eb44..0da067bc9c953e5258deed07475e496b6b4dd50c 100644 (file)
@@ -42,10 +42,9 @@ int main(int argc, char *argv[])
                exit(EXIT_FAILURE);
            } 
        }
                exit(EXIT_FAILURE);
            } 
        }
-       do {
+       while (nread > 0) {
            nwrite = splice(pipefd[0], NULL, out_fd, NULL, nread, 
                            SPLICE_F_MOVE|SPLICE_F_MORE);
            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;
            if (nwrite < 0) {
                if (errno == EINTR)
                    continue;
@@ -55,7 +54,7 @@ int main(int argc, char *argv[])
                }
            }
            nread -= nwrite;
                }
            }
            nread -= nwrite;
-       } while (nread);
+       }
     }
     return EXIT_SUCCESS;
 }
     }
     return EXIT_SUCCESS;
 }