From 8ed116559b281bf39527afbdd7a172572698c1c1 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Fri, 17 Aug 2007 23:25:13 +0000 Subject: [PATCH] Trattate anche {{{vmslice}}} e {{{tee}}}, e scritto esempio di programma che usa {{{tee}}}. --- fileadv.tex | 166 ++++++++++++++++++++++++++++++++++++++------- listati/splicecp.c | 5 +- 2 files changed, 144 insertions(+), 27 deletions(-) diff --git a/fileadv.tex b/fileadv.tex index 404e946..797ecfb 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -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: diff --git a/listati/splicecp.c b/listati/splicecp.c index 28a6705..0da067b 100644 --- a/listati/splicecp.c +++ b/listati/splicecp.c @@ -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; } -- 2.30.2