From: Simone Piccardi Date: Fri, 18 Sep 2015 18:59:59 +0000 (+0000) Subject: Roba sendfile e splice X-Git-Url: https://gapil.gnulinux.it/gitweb/?a=commitdiff_plain;h=ff82508f36245b117cc70a41204264942cca9288;p=gapil.git Roba sendfile e splice --- diff --git a/fileadv.tex b/fileadv.tex index a255851..9437fdb 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -940,7 +940,7 @@ I/O. Abbiamo visto in sez.~\ref{sec:sig_gen_beha}, affrontando la suddivisione fra \textit{fast} e \textit{slow} \textit{system call},\index{system~call~lente} -che in certi casi le funzioni di I/O eseguite su un file descritor possono +che in certi casi le funzioni di I/O eseguite su un file descriptor possono bloccarsi indefinitamente. Questo non avviene mai per i file normali, per i quali le funzioni di lettura e scrittura ritornano sempre subito, ma può avvenire per alcuni \index{file!di~dispositivo} file di dispositivo, come ad @@ -1127,7 +1127,7 @@ funzione invece i \textit{file descriptor set} non vengono modificati anche in caso di errore. Si tenga presente infine che su Linux, in caso di programmazione -\textit{multithread} se un file descriptor viene chiuso in un altro +\textit{multi-thread} se un file descriptor viene chiuso in un altro \textit{thread} rispetto a quello in cui si sta usando \func{select}, questa non subisce nessun effetto. In altre varianti di sistemi unix-like invece \func{select} ritorna indicando che il file descriptor è pronto, con @@ -1498,7 +1498,7 @@ questo comportamento non modificando mai il valore di \param{timeout} anche se in questo caso non esiste nessuno standard che richieda questo comportamento. Infine anche per \func{poll} e \func{ppoll} valgono le considerazioni relative -alla possibilità di avere delle notificazione spurie della disponibilita di +alla possibilità di avere delle notificazione spurie della disponibilità di accesso ai file descriptor illustrate per \func{select} in sez.~\ref{sec:file_select}, che non staremo a ripetere qui. @@ -4734,10 +4734,10 @@ Nello standard POSIX.1-2001 è prevista una ulteriore funzione } \end{funcproto} -Gli argomenti \param{addr} e \param{len} hanno lo stesso significato degli -analoghi di \func{madvise}, mentre \param{advice} può assumere solo i valori -indicati in tab.~\ref{tab:posix_madvise_advice_values}, che riflettono gli -analoghi di \func{madvise}. +Gli argomenti \param{start} e \param{lenght} hanno lo stesso identico +significato degli analoghi di \func{madvise}, a cui si rimanda per la loro +descrizione ma a differenza di quanto indicato dallo standard per questa +funzione, su Linux un valore nullo di \param{len} è consentito. \begin{table}[!htb] \centering @@ -4758,8 +4758,11 @@ analoghi di \func{madvise}. \label{tab:posix_madvise_advice_values} \end{table} -A differenza di quanto indicato dallo standard su Linux un valore nullo -di \param{len} è consentito. Inoltre a partire dalle \acr{glibc} 2.6 + +L'argomento \param{advice} invece può assumere solo i valori indicati in +tab.~\ref{tab:posix_madvise_advice_values}, che riflettono gli analoghi di +\func{madvise}, con lo stesso effetto per tutti tranne +\const{POSIX\_MADV\_DONTNEED}. Infatti a partire dalle \acr{glibc} 2.6 \const{POSIX\_MADV\_DONTNEED} viene ignorato, in quanto l'uso del corrispondente \const{MADV\_DONTNEED} di \func{madvise} ha, per la semantica imperativa, l'effetto immediato di far liberare le pagine da parte del kernel, @@ -4770,46 +4773,43 @@ che viene considerato distruttivo. \subsection{I/O vettorizzato: \func{readv} e \func{writev}} \label{sec:file_multiple_io} -Un caso abbastanza comune è quello in cui ci si trova a dover eseguire una -serie multipla di operazioni di I/O, come una serie di letture o scritture di -vari buffer. Un esempio tipico è quando i dati sono strutturati nei campi di -una struttura ed essi devono essere caricati o salvati su un file. Benché -l'operazione sia facilmente eseguibile attraverso una serie multipla di -chiamate a \func{read} e \func{write}, ci sono casi in cui si vuole poter -contare sulla atomicità delle operazioni. +Una seconda modalità di I/O diversa da quella ordinaria è il cosiddetto +\textsl{I/O vettorizzato}, che nasce per rispondere al caso abbastanza comune +in cui ci si trova nell'esigenza di dover eseguire una serie multipla di +operazioni di I/O, come una serie di letture o scritture di vari buffer. Un +esempio tipico è quando i dati sono strutturati nei campi di una struttura ed +essi devono essere caricati o salvati su un file. Benché l'operazione sia +facilmente eseguibile attraverso una serie multipla di chiamate a \func{read} +e \func{write}, ci sono casi in cui si vuole poter contare sulla atomicità +delle operazioni di lettura e scrittura rispetto all'esecuzione del programma. Per questo motivo fino da BSD 4.2 vennero introdotte delle nuove \textit{system call} che permettessero di effettuare con una sola chiamata una -serie di letture o scritture su una serie di buffer, con quello che viene -normalmente chiamato \textsl{I/O vettorizzato}. Queste funzioni sono +serie di letture da, o scritture su, una serie di buffer, quello che poi venne +chiamato \textsl{I/O vettorizzato}. Queste funzioni di sistema sono \funcd{readv} e \funcd{writev},\footnote{in Linux le due funzioni sono riprese da BSD4.4, esse sono previste anche dallo standard POSIX.1-2001.} ed i relativi prototipi sono: -\begin{functions} - \headdecl{sys/uio.h} - - \funcdecl{int readv(int fd, const struct iovec *vector, int count)} - \funcdecl{int writev(int fd, const struct iovec *vector, int count)} - Eseguono rispettivamente una lettura o una scrittura vettorizzata. - - \bodydesc{Le funzioni restituiscono il numero di byte letti o scritti in - caso di successo, e -1 in caso di errore, nel qual caso \var{errno} - assumerà uno dei valori: + +\begin{funcproto}{ +\fhead{sys/uio.h} +\fdecl{int readv(int fd, const struct iovec *vector, int count)} +\fdecl{int writev(int fd, const struct iovec *vector, int count)} +\fdesc{Eseguono rispettivamente una lettura o una scrittura vettorizzata.} +} + +{Le funzioni ritornano il numero di byte letti o scritti in caso di successo e + $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} - \item[\errcode{EINVAL}] si è specificato un valore non valido per uno degli + \item[\errcode{EINVAL}] si è specificato un valore non valido per uno degli argomenti (ad esempio \param{count} è maggiore di \const{IOV\_MAX}). - \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale prima di - di avere eseguito una qualunque lettura o scrittura. - \item[\errcode{EAGAIN}] \param{fd} è stato aperto in modalità non bloccante e - non ci sono dati in lettura. - \item[\errcode{EOPNOTSUPP}] la coda delle richieste è momentaneamente piena. \end{errlist} - ed anche \errval{EISDIR}, \errval{EBADF}, \errval{ENOMEM}, \errval{EFAULT} - (se non sono stati allocati correttamente i buffer specificati nei campi - \var{iov\_base}), più gli eventuali errori delle funzioni di lettura e - scrittura eseguite su \param{fd}.} -\end{functions} + più tutti i valori, con lo stesso significato, che possono risultare + dalle condizioni di errore di \func{read} e \func{write}. + } +\end{funcproto} + Entrambe le funzioni usano una struttura \struct{iovec}, la cui definizione è riportata in fig.~\ref{fig:file_iovec}, che definisce dove i dati devono @@ -4875,29 +4875,24 @@ sez.~\ref{sec:file_read} e \ref{sec:file_write}); le due funzioni sono bit dell'argomento \param{offset}, che varia a seconda delle architetture, ma queste differenze vengono gestite dalle funzioni di librerie di libreria che mantengono l'interfaccia delle analoghe tratte da BSD.} -\begin{functions} - \headdecl{sys/uio.h} - - \funcdecl{int preadv(int fd, const struct iovec *vector, int count, off\_t + + +\begin{funcproto}{ +\fhead{sys/uio.h} +\fdecl{int preadv(int fd, const struct iovec *vector, int count, off\_t offset)} - \funcdecl{int pwritev(int fd, const struct iovec *vector, int count, off\_t +\fdecl{int pwritev(int fd, const struct iovec *vector, int count, off\_t offset)} +\fdesc{Eseguono una lettura o una scrittura vettorizzata a partire da una data + posizione sul file.} +} - Eseguono una lettura o una scrittura vettorizzata a partire da una data - posizione sul file. - - \bodydesc{Le funzioni hanno gli stessi valori di ritorno delle - corrispondenti \func{readv} e \func{writev}; anche gli eventuali errori - sono gli stessi già visti in precedenza, ma ad essi si possono aggiungere - per \var{errno} anche i valori: - \begin{errlist} - \item[\errcode{EOVERFLOW}] \param{offset} ha un valore che non può essere - usato come \type{off\_t}. - \item[\errcode{ESPIPE}] \param{fd} è associato ad un socket o una - \textit{pipe}. - \end{errlist} +{ Le funzioni hanno gli stessi valori di ritorno delle corrispondenti + \func{readv} e \func{writev} ed anche gli eventuali errori sono gli stessi, + con in più quelli che si possono ottenere dalle possibili condizioni di + errore di \func{lseek}. } -\end{functions} +\end{funcproto} Le due funzioni eseguono rispettivamente una lettura o una scrittura vettorizzata a partire dalla posizione \param{offset} sul file indicato @@ -4927,32 +4922,31 @@ buffer in memoria, da cui essi vengono poi scritti sul secondo. Benché il kernel ottimizzi la gestione di questo processo quando si ha a che fare con file normali, in generale quando i dati da trasferire sono molti si pone il problema di effettuare trasferimenti di grandi quantità di dati da -kernel space a user space e all'indietro, quando in realtà potrebbe essere più -efficiente mantenere tutto in kernel space. Tratteremo in questa sezione -alcune funzioni specialistiche che permettono di ottimizzare le prestazioni in -questo tipo di situazioni. +\textit{kernel space} a \textit{user space} e all'indietro, quando in realtà +potrebbe essere più efficiente mantenere tutto in \textit{kernel + space}. Tratteremo in questa sezione alcune funzioni specialistiche che +permettono di ottimizzare le prestazioni in questo tipo di situazioni. La prima funzione che è stata ideata per ottimizzare il trasferimento dei dati -fra due file descriptor è \func{sendfile};\footnote{la funzione è stata +fra due file descriptor è \func{sendfile}.\footnote{la funzione è stata introdotta con i kernel della serie 2.2, e disponibile dalle \acr{glibc} - 2.1.} la funzione è presente in diverse versioni di Unix,\footnote{la si - ritrova ad esempio in FreeBSD, HPUX ed altri Unix.} ma non è presente né in -POSIX.1-2001 né in altri standard,\footnote{pertanto si eviti di utilizzarla - se si devono scrivere programmi portabili.} per cui per essa vengono -utilizzati prototipi e semantiche differenti; nel caso di Linux il prototipo -di \funcd{sendfile} è: -\begin{functions} - \headdecl{sys/sendfile.h} + 2.1.} La funzione è presente in diverse versioni di Unix (la si ritrova ad +esempio in FreeBSD, HPUX ed altri Unix) ma non è presente né in POSIX.1-2001 +né in altri standard (pertanto si eviti di utilizzarla se si devono scrivere +programmi portabili) per cui per essa vengono utilizzati prototipi e +semantiche differenti. Nel caso di Linux il prototipo di \funcd{sendfile} è: - \funcdecl{ssize\_t sendfile(int out\_fd, int in\_fd, off\_t *offset, size\_t - count)} - - Copia dei dati da un file descriptor ad un altro. - \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} +\begin{funcproto}{ +\fhead{sys/sendfile.h} +\fdecl{ssize\_t sendfile(int out\_fd, int in\_fd, off\_t *offset, size\_t + count)} +\fdesc{Copia dei dati da un file descriptor ad un altro.} +} + +{La funzione ritorna il numero di byte trasferiti in caso di successo e $-1$ + per un errore, nel qual caso \var{errno} assumerà uno dei valori: + \begin{errlist} \item[\errcode{EAGAIN}] si è impostata la modalità non bloccante su \param{out\_fd} e la scrittura si bloccherebbe. \item[\errcode{EINVAL}] i file descriptor non sono validi, o sono bloccati @@ -4961,17 +4955,16 @@ di \funcd{sendfile} è: \item[\errcode{EIO}] si è avuto un errore di lettura da \param{in\_fd}. \item[\errcode{ENOMEM}] non c'è memoria sufficiente per la lettura da \param{in\_fd}. - \end{errlist} - ed inoltre \errcode{EBADF} e \errcode{EFAULT}. - } -\end{functions} + \end{errlist} + ed inoltre \errcode{EBADF} e \errcode{EFAULT} nel loro significato + generico.} +\end{funcproto} La funzione copia direttamente \param{count} byte dal file descriptor -\param{in\_fd} al file descriptor \param{out\_fd}; in caso di successo +\param{in\_fd} al file descriptor \param{out\_fd}. In caso di successo la funzione ritorna il numero di byte effettivamente copiati da \param{in\_fd} a -\param{out\_fd} o $-1$ in caso di errore; come le ordinarie \func{read} e -\func{write} questo valore può essere inferiore a quanto richiesto con -\param{count}. +\param{out\_fd} e come per le ordinarie \func{read} e \func{write} questo +valore può essere inferiore a quanto richiesto con \param{count}. Se il puntatore \param{offset} è nullo la funzione legge i dati a partire dalla posizione corrente su \param{in\_fd}, altrimenti verrà usata la @@ -4982,66 +4975,62 @@ posizione corrente sul file non sarà modificata. Se invece \param{offset} è nullo la posizione corrente sul file sarà aggiornata tenendo conto dei byte letti da \param{in\_fd}. -Fino ai kernel della serie 2.4 la funzione è utilizzabile su un qualunque file -descriptor, e permette di sostituire la invocazione successiva di una +Fino ai kernel della serie 2.4 la funzione era utilizzabile su un qualunque +file descriptor, e permetteva di sostituire la invocazione successiva di una \func{read} e una \func{write} (e l'allocazione del relativo buffer) con una -sola chiamata a \funcd{sendfile}. In questo modo si può diminuire il numero di -chiamate al sistema e risparmiare in trasferimenti di dati da kernel space a -user space e viceversa. La massima utilità della funzione si ha comunque per -il trasferimento di dati da un file su disco ad un socket di +sola chiamata a \funcd{sendfile}. In questo modo si poteva diminuire il numero +di chiamate al sistema e risparmiare in trasferimenti di dati da kernel space +a user space e viceversa. La massima utilità della funzione si ottiene +comunque per il trasferimento di dati da un file su disco ad un socket di rete,\footnote{questo è il caso classico del lavoro eseguito da un server web, ed infatti Apache ha una opzione per il supporto esplicito di questa funzione.} dato che in questo caso diventa possibile effettuare il trasferimento diretto via DMA dal controller del disco alla scheda di rete, -senza neanche allocare un buffer nel kernel,\footnote{il meccanismo è detto - \textit{zerocopy} in quanto i dati non vengono mai copiati dal kernel, che - si limita a programmare solo le operazioni di lettura e scrittura via DMA.} +senza neanche allocare un buffer nel kernel (il meccanismo è detto +\textit{zerocopy} in quanto i dati non vengono mai copiati dal kernel, che si +limita a programmare solo le operazioni di lettura e scrittura via DMA) ottenendo la massima efficienza possibile senza pesare neanche sul processore. -In seguito però ci si è accorti che, fatta eccezione per il trasferimento +In seguito però ci si accorse che, fatta eccezione per il trasferimento diretto da file a socket, non sempre \func{sendfile} comportava miglioramenti significativi delle prestazioni rispetto all'uso in sequenza di \func{read} e -\func{write},\footnote{nel caso generico infatti il kernel deve comunque - allocare un buffer ed effettuare la copia dei dati, e in tal caso spesso il - guadagno ottenibile nel ridurre il numero di chiamate al sistema non - compensa le ottimizzazioni che possono essere fatte da una applicazione in - user space che ha una conoscenza diretta su come questi sono strutturati.} e -che anzi in certi casi si potevano avere anche dei peggioramenti. Questo ha -portato, per i kernel della serie 2.6,\footnote{per alcune motivazioni di - questa scelta si può fare riferimento a quanto illustrato da Linus Torvalds - in \url{http://www.cs.helsinki.fi/linux/linux-kernel/2001-03/0200.html}.} -alla decisione di consentire l'uso della funzione soltanto quando il file da -cui si legge supporta le operazioni di \textit{memory mapping} (vale a dire -non è un socket) e quello su cui si scrive è un socket; in tutti gli altri -casi l'uso di \func{sendfile} darà luogo ad un errore di \errcode{EINVAL}. - -Nonostante ci possano essere casi in cui \func{sendfile} non migliora le -prestazioni, resta il dubbio se la scelta di disabilitarla sempre per il -trasferimento fra file di dati sia davvero corretta. Se ci sono peggioramenti -di prestazioni infatti si può sempre fare ricorso al metodo ordinario, ma -lasciare a disposizione la funzione consentirebbe se non altro di semplificare -la gestione della copia dei dati fra file, evitando di dover gestire -l'allocazione di un buffer temporaneo per il loro trasferimento. - -Questo dubbio si può comunque ritenere superato con l'introduzione, avvenuta a -partire dal kernel 2.6.17, della nuova \textit{system call} \func{splice}. Lo -scopo di questa funzione è quello di fornire un meccanismo generico per il -trasferimento di dati da o verso un file utilizzando un buffer gestito -internamente dal kernel. Descritta in questi termini \func{splice} sembra -semplicemente un ``\textsl{dimezzamento}'' di \func{sendfile}.\footnote{nel - senso che un trasferimento di dati fra due file con \func{sendfile} non - sarebbe altro che la lettura degli stessi su un buffer seguita dalla - relativa scrittura, cosa che in questo caso si dovrebbe eseguire con due - chiamate a \func{splice}.} In realtà le due \textit{system call} sono -profondamente diverse nel loro meccanismo di funzionamento;\footnote{questo - fino al kernel 2.6.23, dove \func{sendfile} è stata reimplementata in - termini di \func{splice}, pur mantenendo disponibile la stessa interfaccia - verso l'user space.} \func{sendfile} infatti, come accennato, non necessita -di avere a disposizione un buffer interno, perché esegue un trasferimento -diretto di dati; questo la rende in generale più efficiente, ma anche limitata -nelle sue applicazioni, dato che questo tipo di trasferimento è possibile solo -in casi specifici.\footnote{e nel caso di Linux questi sono anche solo quelli - in cui essa può essere effettivamente utilizzata.} +\func{write}. Nel caso generico infatti il kernel deve comunque allocare un +buffer ed effettuare la copia dei dati, e in tal caso spesso il guadagno +ottenibile nel ridurre il numero di chiamate al sistema non compensa le +ottimizzazioni che possono essere fatte da una applicazione in user space che +ha una conoscenza diretta su come questi sono strutturati, per cui in certi +casi si potevano avere anche dei peggioramenti. Questo ha portato, per i +kernel della serie 2.6,\footnote{per alcune motivazioni di questa scelta si + può fare riferimento a quanto illustrato da Linus Torvalds in + \url{http://www.cs.helsinki.fi/linux/linux-kernel/2001-03/0200.html}.} alla +decisione di consentire l'uso della funzione soltanto quando il file da cui si +legge supporta le operazioni di \textit{memory mapping} (vale a dire non è un +socket) e quello su cui si scrive è un socket; in tutti gli altri casi l'uso +di \func{sendfile} da luogo ad un errore di \errcode{EINVAL}. A partire dal +kernel 2.6.33 però la restrizione su \param{out\_fd} è stata rimossa e questo +può essere un file qualunque, rimane però quella di non poter usare un socket +per \param{in\_fd}. + +A partire dal kernel 2.6.17 come alternativa a \func{sendfile} è disponibile +la nuova \textit{system call} \func{splice}. Lo scopo di questa funzione è +quello di fornire un meccanismo generico per il trasferimento di dati da o +verso un file, utilizzando un buffer gestito internamente dal +kernel. Descritta in questi termini \func{splice} sembra semplicemente un +``\textsl{dimezzamento}'' di \func{sendfile}, nel senso che un trasferimento +di dati fra due file con \func{sendfile} non sarebbe altro che la lettura +degli stessi su un buffer seguita dalla relativa scrittura, cosa che in questo +caso si dovrebbe eseguire con due chiamate a \func{splice}. + +In realtà le due \textit{system call} sono profondamente diverse nel loro +meccanismo di funzionamento;\footnote{questo fino al kernel 2.6.23, dove + \func{sendfile} è stata reimplementata in termini di \func{splice}, pur + mantenendo disponibile la stessa interfaccia verso l'user space.} +\func{sendfile} infatti, come accennato, non necessita di avere a disposizione +un buffer interno, perché esegue un trasferimento diretto di dati; questo la +rende in generale più efficiente, ma anche limitata nelle sue applicazioni, +dato che questo tipo di trasferimento è possibile solo in casi specifici che +nel caso di Linux questi sono anche solo quelli in cui essa può essere +effettivamente utilizzata. Il concetto che sta dietro a \func{splice} invece è diverso,\footnote{in realtà la proposta originale di Larry Mc Voy non differisce poi tanto negli @@ -5051,11 +5040,11 @@ Il concetto che sta dietro a \func{splice} invece è diverso,\footnote{in dallo stesso Linus Torvalds in \url{http://kerneltrap.org/node/6505}.} si tratta semplicemente di una funzione che consente di fare in maniera del tutto generica delle operazioni di trasferimento di dati fra un file e un buffer -gestito interamente in kernel space. In questo caso il cuore della funzione (e -delle affini \func{vmsplice} e \func{tee}, che tratteremo più avanti) è -appunto l'uso di un buffer in kernel space, e questo è anche quello che ne ha -semplificato l'adozione, perché l'infrastruttura per la gestione di un tale -buffer è presente fin dagli albori di Unix per la realizzazione delle +gestito interamente in \textit{kernel space}. In questo caso il cuore della +funzione (e delle affini \func{vmsplice} e \func{tee}, che tratteremo più +avanti) è appunto l'uso di un buffer in kernel space, e questo è anche quello +che ne ha semplificato l'adozione, perché l'infrastruttura per la gestione di +un tale buffer è presente fin dagli albori di Unix per la realizzazione delle \textit{pipe} (vedi sez.~\ref{sec:ipc_unix}). Dal punto di vista concettuale allora \func{splice} non è altro che una diversa interfaccia (rispetto alle \textit{pipe}) con cui utilizzare in user space l'oggetto ``\textsl{buffer in @@ -5073,18 +5062,18 @@ definito la macro \macro{\_GNU\_SOURCE},\footnote{si ricordi che questa funzione non è contemplata da nessuno standard, è presente solo su Linux, e pertanto deve essere evitata se si vogliono scrivere programmi portabili.} è il seguente: -\begin{functions} - \headdecl{fcntl.h} - \funcdecl{long splice(int fd\_in, off\_t *off\_in, int fd\_out, off\_t + +\begin{funcproto}{ +\fhead{fcntl.h} +\fdecl{long splice(int fd\_in, off\_t *off\_in, int fd\_out, off\_t *off\_out, size\_t len, unsigned int flags)} - - Trasferisce dati da un file verso una \textit{pipe} o viceversa. +\fdesc{Trasferisce dati da un file verso una \textit{pipe} o viceversa.} +} - \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} +{La funzione ritorna il numero di byte trasferiti in caso di successo e $-1$ + per un errore, nel qual caso \var{errno} assumerà uno dei valori: + \begin{errlist} \item[\errcode{EBADF}] uno o entrambi fra \param{fd\_in} e \param{fd\_out} non sono file descriptor validi o, rispettivamente, non sono stati aperti in lettura o scrittura. @@ -5098,9 +5087,10 @@ definito la macro \macro{\_GNU\_SOURCE},\footnote{si ricordi che questa richiesta. \item[\errcode{ESPIPE}] o \param{off\_in} o \param{off\_out} non sono \val{NULL} ma il corrispondente file descriptor è una \textit{pipe}. - \end{errlist} - } -\end{functions} + \end{errlist} +} +\end{funcproto} + 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 @@ -5827,10 +5817,11 @@ livello di kernel. % LocalWords: clockid CLOCK MONOTONIC REALTIME itimerspec interval Resource % LocalWords: ABSTIME gettime temporarily unavailable SIGINT SIGQUIT SIGTERM % LocalWords: sigfd fifofd break siginf names starting echo Message from Got -% LocalWords: message kill received means exit +% LocalWords: message kill received means exit TLOCK ULOCK EPOLLWAKEUP %%% Local Variables: %%% mode: latex %%% TeX-master: "gapil" %%% End: +