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
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
richiede che il valore di \param{timeout} non sia modificato.
Rispetto a \func{select} la nuova funzione prende un argomento
-aggiuntivo \param{sigmask}, un puntatore ad una \index{maschera~dei~segnali}
-maschera di segnali (si veda sez.~\ref{sec:sig_sigmask}). Nell'esecuzione la
-maschera dei segnali corrente viene sostituita da quella così indicata
-immediatamente prima di eseguire l'attesa, e viene poi ripristinata al ritorno
-della funzione. L'uso di \param{sigmask} è stato introdotto allo scopo di
-prevenire possibili \textit{race condition} \itindex{race~condition} quando
-oltre alla presenza di dati sui file descriptor come nella \func{select}
-ordinaria, ci si deve porre in attesa anche dell'arrivo di un segnale.
+aggiuntivo \param{sigmask}, un puntatore ad una maschera di segnali (si veda
+sez.~\ref{sec:sig_sigmask}). Nell'esecuzione la maschera dei segnali corrente
+viene sostituita da quella così indicata immediatamente prima di eseguire
+l'attesa, e viene poi ripristinata al ritorno della funzione. L'uso
+di \param{sigmask} è stato introdotto allo scopo di prevenire possibili
+\textit{race condition} \itindex{race~condition} quando oltre alla presenza di
+dati sui file descriptor come nella \func{select} ordinaria, ci si deve porre
+in attesa anche dell'arrivo di un segnale.
Come abbiamo visto in sez.~\ref{sec:sig_example} la tecnica classica per
rilevare l'arrivo di un segnale è quella di utilizzare il gestore per
\func{select}, che è una estensione tipica di BSD, è stata introdotta una
interfaccia completamente diversa, basata sulla funzione di sistema
\funcd{poll},\footnote{la funzione è prevista dallo standard XPG4, ed è stata
- introdotta in Linux come system call a partire dal kernel 2.1.23 ed inserita
- nelle \acr{libc} 5.4.28, originariamente l'argomento \param{nfds} era di
- tipo \ctyp{unsigned int}, la funzione è stata inserita nello standard
+ introdotta in Linux come \textit{system call} a partire dal kernel 2.1.23 ed
+ inserita nelle \acr{libc} 5.4.28, originariamente l'argomento \param{nfds}
+ era di tipo \ctyp{unsigned int}, la funzione è stata inserita nello standard
POSIX.1-2001 in cui è stato introdotto il tipo nativo \type{nfds\_t}.} il
cui prototipo è:
\end{funcproto}
La funzione ha lo stesso comportamento di \func{poll}, solo che si può
-specificare, con l'argomento \param{sigmask}, il puntatore ad una
-\index{maschera~dei~segnali} maschera di segnali; questa sarà la maschera
-utilizzata per tutto il tempo che la funzione resterà in attesa, all'uscita
-viene ripristinata la maschera originale. L'uso di questa funzione è cioè
-equivalente, come illustrato nella pagina di manuale, all'esecuzione atomica
-del seguente codice:
+specificare, con l'argomento \param{sigmask}, il puntatore ad una maschera di
+segnali; questa sarà la maschera utilizzata per tutto il tempo che la funzione
+resterà in attesa, all'uscita viene ripristinata la maschera originale. L'uso
+di questa funzione è cioè equivalente, come illustrato nella pagina di
+manuale, all'esecuzione atomica del seguente codice:
\includecodesnip{listati/ppoll_means.c}
Eccetto per \param{timeout}, che come per \func{pselect} deve essere un
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.
contemporaneamente. Valgono le osservazioni fatte in
sez.~\ref{sec:file_select}, e per poterlo fare di nuovo è necessaria una
variante della funzione di attesa che consenta di reimpostare all'uscita una
-\index{maschera~dei~segnali} maschera di segnali, analoga alle estensioni
-\func{pselect} e \func{ppoll} che abbiamo visto in precedenza per
-\func{select} e \func{poll}. In questo caso la funzione di sistema si chiama
-\funcd{epoll\_pwait}\footnote{la funzione è stata introdotta a partire dal
- kernel 2.6.19, ed è come tutta l'interfaccia di \textit{epoll}, specifica di
- Linux.} ed il suo prototipo è:
+maschera di segnali, analoga alle estensioni \func{pselect} e \func{ppoll} che
+abbiamo visto in precedenza per \func{select} e \func{poll}. In questo caso la
+funzione di sistema si chiama \funcd{epoll\_pwait}\footnote{la funzione è
+ stata introdotta a partire dal kernel 2.6.19, ed è, come tutta l'interfaccia
+ di \textit{epoll}, specifica di Linux.} ed il suo prototipo è:
\begin{funcproto}{
\fhead{sys/epoll.h}
\end{funcproto}
La funzione è del tutto analoga \funcd{epoll\_wait}, soltanto che alla sua
-uscita viene ripristinata la \index{maschera~dei~segnali} maschera di segnali
-originale, sostituita durante l'esecuzione da quella impostata con
-l'argomento \param{sigmask}; in sostanza la chiamata a questa funzione è
-equivalente al seguente codice, eseguito però in maniera atomica:
+uscita viene ripristinata la maschera di segnali originale, sostituita durante
+l'esecuzione da quella impostata con l'argomento \param{sigmask}; in sostanza
+la chiamata a questa funzione è equivalente al seguente codice, eseguito però
+in maniera atomica:
\includecodesnip{listati/epoll_pwait_means.c}
Si tenga presente che come le precedenti funzioni di \textit{I/O multiplexing}
versione, \funcm{signalfd4}, introdotta con il kernel 2.6.27 e che è quella
che viene sempre usata a partire dalle \acr{glibc} 2.9, che prende un
argomento aggiuntivo \code{size\_t sizemask} che indica la dimensione della
- \index{maschera~dei~segnali} maschera dei segnali, il cui valore viene
- impostato automaticamente dalle \acr{glibc}.} il cui prototipo è:
+ maschera dei segnali, il cui valore viene impostato automaticamente dalle
+ \acr{glibc}.} il cui prototipo è:
\begin{funcproto}{
\fhead{sys/signalfd.h}
L'elenco dei segnali che si vogliono gestire con \func{signalfd} deve essere
specificato tramite l'argomento \param{mask}. Questo deve essere passato come
-puntatore ad una \index{maschera~dei~segnali} maschera di segnali creata con
-l'uso delle apposite macro già illustrate in sez.~\ref{sec:sig_sigset}. La
-maschera deve indicare su quali segnali si intende operare con
-\func{signalfd}; l'elenco può essere modificato con una successiva chiamata a
-\func{signalfd}. Dato che \signal{SIGKILL} e \signal{SIGSTOP} non possono
-essere intercettati (e non prevedono neanche la possibilità di un gestore) un
-loro inserimento nella maschera verrà ignorato senza generare errori.
+puntatore ad una maschera di segnali creata con l'uso delle apposite macro già
+illustrate in sez.~\ref{sec:sig_sigset}. La maschera deve indicare su quali
+segnali si intende operare con \func{signalfd}; l'elenco può essere modificato
+con una successiva chiamata a \func{signalfd}. Dato che \signal{SIGKILL} e
+\signal{SIGSTOP} non possono essere intercettati (e non prevedono neanche la
+possibilità di un gestore) un loro inserimento nella maschera verrà ignorato
+senza generare errori.
L'argomento \param{flags} consente di impostare direttamente in fase di
creazione due flag per il file descriptor analoghi a quelli che si possono
quello che useremo per il controllo degli altri. É poi necessario
disabilitare la ricezione dei segnali (nel caso \signal{SIGINT},
\signal{SIGQUIT} e \signal{SIGTERM}) per i quali si vuole la notifica tramite
-file descriptor. Per questo prima li si inseriscono (\texttt{\small 22-25})
-in una \index{maschera~dei~segnali} maschera di segnali \texttt{sigmask} che
-useremo con (\texttt{\small 26}) \func{sigprocmask} per disabilitarli. Con la
-stessa maschera si potrà per passare all'uso (\texttt{\small 28-29}) di
-\func{signalfd} per abilitare la notifica sul file descriptor
-\var{sigfd}. Questo poi (\texttt{\small 30-33}) dovrà essere aggiunto con
-\func{epoll\_ctl} all'elenco di file descriptor controllati con \texttt{epfd}.
+file descriptor. Per questo prima li si inseriscono (\texttt{\small 22-25}) in
+una maschera di segnali \texttt{sigmask} che useremo con (\texttt{\small 26})
+\func{sigprocmask} per disabilitarli. Con la stessa maschera si potrà per
+passare all'uso (\texttt{\small 28-29}) di \func{signalfd} per abilitare la
+notifica sul file descriptor \var{sigfd}. Questo poi (\texttt{\small 30-33})
+dovrà essere aggiunto con \func{epoll\_ctl} all'elenco di file descriptor
+controllati con \texttt{epfd}.
Occorrerà infine (\texttt{\small 35-38}) creare la \textit{named fifo} se
questa non esiste ed aprirla per la lettura (\texttt{\small 39-40}); una
all'origine di Unix i soli programmi che potevano avere una tale esigenza
erano i demoni, attenendosi a uno dei criteri base della progettazione, che
era di far fare al kernel solo le operazioni strettamente necessarie e
-lasciare tutto il resto a processi in user space, non era stata prevista
-nessuna funzionalità di notifica.
+lasciare tutto il resto a processi in \textit{user space}, non era stata
+prevista nessuna funzionalità di notifica.
Visto però il crescente interesse nei confronti di una funzionalità di questo
tipo, che è molto richiesta specialmente nello sviluppo dei programmi ad
rispetto a quelle usate normalmente.
In generale questa interfaccia è completamente astratta e può essere
-implementata sia direttamente nel kernel che in user space attraverso l'uso di
-\itindex{thread} \textit{thread}. Per le versioni del kernel meno recenti
-esiste una implementazione di questa interfaccia fornita completamente delle
-\acr{glibc} a partire dalla versione 2.1, che è realizzata completamente in
-user space, ed è accessibile linkando i programmi con la libreria
+implementata sia direttamente nel kernel che in \textit{user space} attraverso
+l'uso di \itindex{thread} \textit{thread}. Per le versioni del kernel meno
+recenti esiste una implementazione di questa interfaccia fornita completamente
+delle \acr{glibc} a partire dalla versione 2.1, che è realizzata completamente
+in \textit{user space}, ed è accessibile linkando i programmi con la libreria
\file{librt}. A partire dalla versione 2.5.32 è stato introdotto nel kernel
una nuova infrastruttura per l'I/O asincrono, ma ancora il supporto è parziale
ed insufficiente ad implementare tutto l'AIO POSIX.
di caricare in memoria solo le parti del file che sono effettivamente usate ad
un dato istante.
-Infatti, dato che l'accesso è fatto direttamente attraverso la
-\index{memoria~virtuale} memoria virtuale, la sezione di memoria mappata su
-cui si opera sarà a sua volta letta o scritta sul file una pagina alla volta e
-solo per le parti effettivamente usate, il tutto in maniera completamente
-trasparente al processo; l'accesso alle pagine non ancora caricate avverrà
-allo stesso modo con cui vengono caricate in memoria le pagine che sono state
-salvate sullo \textit{swap}.
+Infatti, dato che l'accesso è fatto direttamente attraverso la memoria
+virtuale, la sezione di memoria mappata su cui si opera sarà a sua volta letta
+o scritta sul file una pagina alla volta e solo per le parti effettivamente
+usate, il tutto in maniera completamente trasparente al processo; l'accesso
+alle pagine non ancora caricate avverrà allo stesso modo con cui vengono
+caricate in memoria le pagine che sono state salvate sullo \textit{swap}.
Infine in situazioni in cui la memoria è scarsa, le pagine che mappano un file
vengono salvate automaticamente, così come le pagine dei programmi vengono
protezioni delle pagine di memoria; lo standard prevede che essa si applichi
solo ai \textit{memory mapping} creati con \func{mmap}, ma nel caso di Linux
la funzione può essere usata con qualunque pagina valida nella memoria
-virtuale. Questa funzione è \funcd{mprotect} ed il suo prototipo è:
-\begin{functions}
-% \headdecl{unistd.h}
- \headdecl{sys/mman.h}
+virtuale. Questa funzione di sistema è \funcd{mprotect} ed il suo prototipo è:
- \funcdecl{int mprotect(const void *addr, size\_t len, int prot)}
-
- Modifica le protezioni delle pagine di memoria comprese nell'intervallo
- specificato.
+\begin{funcproto}{
+\fhead{sys/mman.h}
+\fdecl{int mprotect(const void *addr, size\_t len, int prot)}
+\fdesc{Modifica le protezioni delle pagine di memoria.}
+}
- \bodydesc{La funzione restituisce 0 in caso di successo, e -1 in caso di
- errore nel qual caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
+{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
+ caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
\item[\errcode{EINVAL}] il valore di \param{addr} non è valido o non è un
multiplo di \const{PAGE\_SIZE}.
\item[\errcode{EACCES}] l'operazione non è consentita, ad esempio si è
cercato di marcare con \const{PROT\_WRITE} un segmento di memoria cui si
ha solo accesso in lettura.
-% \item[\errcode{ENOMEM}] non è stato possibile allocare le risorse
-% necessarie all'interno del kernel.
-% \item[\errcode{EFAULT}] si è specificato un indirizzo di memoria non
-% accessibile.
- \end{errlist}
- ed inoltre \errval{ENOMEM} ed \errval{EFAULT}.
- }
-\end{functions}
-
+ \item[\errcode{ENOMEM}] non è stato possibile allocare le risorse
+ necessarie all'interno del kernel o si è specificato un indirizzo di
+ memoria non valido del processo o non corrispondente a pagine mappate
+ (negli ultimi due casi prima del kernel 2.4.19 veniva prodotto,
+ erroneamente, \errcode{EFAULT}).
+ \end{errlist}
+}
+\end{funcproto}
La funzione prende come argomenti un indirizzo di partenza in \param{addr},
allineato alle dimensioni delle pagine di memoria, ed una dimensione
dall'intervallo fra \param{addr} e \param{addr}+\param{size}-1.
Infine Linux supporta alcune operazioni specifiche non disponibili su altri
-kernel unix-like. La prima di queste è la possibilità di modificare un
-precedente \textit{memory mapping}, ad esempio per espanderlo o restringerlo.
-Questo è realizzato dalla funzione \funcd{mremap}, il cui prototipo è:
-\begin{functions}
- \headdecl{unistd.h}
- \headdecl{sys/mman.h}
+kernel unix-like per poter usare le quali occorre però dichiarare
+\macro{\_GNU\_SOURCE} prima dell'inclusione di \texttt{sys/mman.h}. La prima
+di queste è la possibilità di modificare un precedente \textit{memory
+ mapping}, ad esempio per espanderlo o restringerlo. Questo è realizzato
+dalla funzione di sistema \funcd{mremap}, il cui prototipo è:
- \funcdecl{void * mremap(void *old\_address, size\_t old\_size , size\_t
+\begin{funcproto}{
+\fhead{sys/mman.h}
+\fdecl{void * mremap(void *old\_address, size\_t old\_size , size\_t
new\_size, unsigned long flags)}
-
- Restringe o allarga una mappatura in memoria di un file.
+\fdesc{Restringe o allarga una mappatura in memoria.}
+}
- \bodydesc{La funzione restituisce l'indirizzo alla nuova area di memoria in
- caso di successo od il valore \const{MAP\_FAILED} (pari a \texttt{(void *)
- -1}) in caso di errore, nel qual caso \var{errno} assumerà uno dei
- valori:
- \begin{errlist}
+{La funzione ritorna l'indirizzo alla nuova area di memoria in caso di
+ successo o il valore \const{MAP\_FAILED} (pari a \texttt{(void *) -1}), nel
+ qual caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
\item[\errcode{EINVAL}] il valore di \param{old\_address} non è un
puntatore valido.
\item[\errcode{EFAULT}] ci sono indirizzi non validi nell'intervallo
è specificato \const{MREMAP\_MAYMOVE} nei flag.
\item[\errcode{EAGAIN}] il segmento di memoria scelto è bloccato e non può
essere rimappato.
- \end{errlist}
- }
-\end{functions}
+ \end{errlist}
+}
+\end{funcproto}
La funzione richiede come argomenti \param{old\_address} (che deve essere
allineato alle dimensioni di una pagina di memoria) che specifica il
indica la dimensione. Con \param{new\_size} si specifica invece la nuova
dimensione che si vuole ottenere. Infine l'argomento \param{flags} è una
maschera binaria per i flag che controllano il comportamento della funzione.
-Il solo valore utilizzato è \const{MREMAP\_MAYMOVE}\footnote{per poter
- utilizzare questa costante occorre aver definito \macro{\_GNU\_SOURCE} prima
- di includere \headfile{sys/mman.h}.} che consente di eseguire l'espansione
-anche quando non è possibile utilizzare il precedente indirizzo. Per questo
-motivo, se si è usato questo flag, la funzione può restituire un indirizzo
-della nuova zona di memoria che non è detto coincida con \param{old\_address}.
-
-La funzione si appoggia al sistema della \index{memoria~virtuale} memoria
-virtuale per modificare l'associazione fra gli indirizzi virtuali del processo
-e le pagine di memoria, modificando i dati direttamente nella
-\itindex{page~table} \textit{page table} del processo. Come per
-\func{mprotect} la funzione può essere usata in generale, anche per pagine di
-memoria non corrispondenti ad un \textit{memory mapping}, e consente così di
-implementare la funzione \func{realloc} in maniera molto efficiente.
+Il solo valore utilizzato è \const{MREMAP\_MAYMOVE} che consente di eseguire
+l'espansione anche quando non è possibile utilizzare il precedente
+indirizzo. Per questo motivo, se si è usato questo flag, la funzione può
+restituire un indirizzo della nuova zona di memoria che non è detto coincida
+con \param{old\_address}.
+
+La funzione si appoggia al sistema della memoria virtuale per modificare
+l'associazione fra gli indirizzi virtuali del processo e le pagine di memoria,
+modificando i dati direttamente nella \textit{page table} del processo. Come
+per \func{mprotect} la funzione può essere usata in generale, anche per pagine
+di memoria non corrispondenti ad un \textit{memory mapping}, e consente così
+di implementare la funzione \func{realloc} in maniera molto efficiente.
Una caratteristica comune a tutti i sistemi unix-like è che la mappatura in
memoria di un file viene eseguita in maniera lineare, cioè parti successive di
un file vengono mappate linearmente su indirizzi successivi in memoria.
-Esistono però delle applicazioni\footnote{in particolare la tecnica è usata
- dai database o dai programmi che realizzano macchine virtuali.} in cui è
-utile poter mappare sezioni diverse di un file su diverse zone di memoria.
+Esistono però delle applicazioni (in particolare la tecnica è usata dai
+database o dai programmi che realizzano macchine virtuali) in cui è utile
+poter mappare sezioni diverse di un file su diverse zone di memoria.
Questo è ovviamente sempre possibile eseguendo ripetutamente la funzione
\func{mmap} per ciascuna delle diverse aree del file che si vogliono mappare
-in sequenza non lineare,\footnote{ed in effetti è quello che veniva fatto
- anche con Linux prima che fossero introdotte queste estensioni.} ma questo
-approccio ha delle conseguenze molto pesanti in termini di prestazioni.
-Infatti per ciascuna mappatura in memoria deve essere definita nella
-\itindex{page~table} \textit{page table} del processo una nuova area di
-memoria virtuale\footnote{quella che nel gergo del kernel viene chiamata VMA
- (\textit{virtual memory area}).} che corrisponda alla mappatura, in modo che
-questa diventi visibile nello spazio degli indirizzi come illustrato in
-fig.~\ref{fig:file_mmap_layout}.
-
-Quando un processo esegue un gran numero di mappature diverse\footnote{si può
- arrivare anche a centinaia di migliaia.} per realizzare a mano una mappatura
-non-lineare si avrà un accrescimento eccessivo della sua \itindex{page~table}
-\textit{page table}, e lo stesso accadrà per tutti gli altri processi che
-utilizzano questa tecnica. In situazioni in cui le applicazioni hanno queste
-esigenze si avranno delle prestazioni ridotte, dato che il kernel dovrà
-impiegare molte risorse\footnote{sia in termini di memoria interna per i dati
- delle \itindex{page~table} \textit{page table}, che di CPU per il loro
- aggiornamento.} solo per mantenere i dati di una gran quantità di
-\textit{memory mapping}.
+in sequenza non lineare (ed in effetti è quello che veniva fatto anche con
+Linux prima che fossero introdotte queste estensioni) ma questo approccio ha
+delle conseguenze molto pesanti in termini di prestazioni. Infatti per
+ciascuna mappatura in memoria deve essere definita nella \textit{page table}
+del processo una nuova area di memoria virtuale, quella che nel gergo del
+kernel viene chiamata VMA (\textit{virtual memory area}, che corrisponda alla
+mappatura, in modo che questa diventi visibile nello spazio degli indirizzi
+come illustrato in fig.~\ref{fig:file_mmap_layout}.
+
+Quando un processo esegue un gran numero di mappature diverse (si può arrivare
+anche a centinaia di migliaia) per realizzare a mano una mappatura non-lineare
+esso vedrà un accrescimento eccessivo della sua \textit{page table}, e lo
+stesso accadrà per tutti gli altri processi che utilizzano questa tecnica. In
+situazioni in cui le applicazioni hanno queste esigenze si avranno delle
+prestazioni ridotte, dato che il kernel dovrà impiegare molte risorse per
+mantenere i dati relativi al \textit{memory mapping}, sia in termini di
+memoria interna per i dati delle \textit{page table}, che di CPU per il loro
+aggiornamento.
Per questo motivo con il kernel 2.5.46 è stato introdotto, ad opera di Ingo
Molnar, un meccanismo che consente la mappatura non-lineare. Anche questa è
una caratteristica specifica di Linux, non presente in altri sistemi
-unix-like. Diventa così possibile utilizzare una sola mappatura
-iniziale\footnote{e quindi una sola \textit{virtual memory area} nella
- \itindex{page~table} \textit{page table} del processo.} e poi rimappare a
-piacere all'interno di questa i dati del file. Ciò è possibile grazie ad una
-nuova \textit{system call}, \funcd{remap\_file\_pages}, il cui prototipo è:
-\begin{functions}
- \headdecl{sys/mman.h}
+unix-like. Diventa così possibile utilizzare una sola mappatura iniziale, e
+quindi una sola \textit{virtual memory area} nella \textit{page table} del
+processo, e poi rimappare a piacere all'interno di questa i dati del file. Ciò
+è possibile grazie ad una nuova \textit{system call},
+\funcd{remap\_file\_pages}, il cui prototipo è:
- \funcdecl{int remap\_file\_pages(void *start, size\_t size, int prot,
+\begin{funcproto}{
+\fhead{sys/mman.h}
+\fdecl{int remap\_file\_pages(void *start, size\_t size, int prot,
ssize\_t pgoff, int flags)}
-
- Permette di rimappare non linearmente un precedente \textit{memory mapping}.
+\fdesc{Rimappa non linearmente un \textit{memory mapping}.}
+}
- \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di
- errore, nel qual caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
+{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
+ caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
\item[\errcode{EINVAL}] si è usato un valore non valido per uno degli
argomenti o \param{start} non fa riferimento ad un \textit{memory
mapping} valido creato con \const{MAP\_SHARED}.
- \end{errlist}
- }
-\end{functions}
+ \end{errlist}
+ ed inoltre
+ nel loro significato generico.}
+\end{funcproto}
Per poter utilizzare questa funzione occorre anzitutto effettuare
preliminarmente una chiamata a \func{mmap} con \const{MAP\_SHARED} per
-definire l'area di memoria che poi sarà rimappata non linearmente. Poi di
+definire l'area di memoria che poi sarà rimappata non linearmente. Poi si
chiamerà questa funzione per modificare le corrispondenze fra pagine di
memoria e pagine del file; si tenga presente che \func{remap\_file\_pages}
permette anche di mappare la stessa pagina di un file in più pagine della
\textit{memory mapping}.
Questo vuol dire che il passaggio dei dati dal disco alla memoria avverrà una
-pagina alla volta con un gran numero di \itindex{page~fault} \textit{page
- fault}, chiaramente se si sa in anticipo che il file verrà utilizzato
-immediatamente, è molto più efficiente eseguire un \textit{prefaulting} in cui
-tutte le pagine di memoria interessate alla mappatura vengono
-``\textsl{popolate}'' in una sola volta, questo comportamento viene abilitato
-quando si usa con \func{mmap} il flag \const{MAP\_POPULATE}.
+pagina alla volta con un gran numero di \textit{page fault}, chiaramente se si
+sa in anticipo che il file verrà utilizzato immediatamente, è molto più
+efficiente eseguire un \textit{prefaulting} in cui tutte le pagine di memoria
+interessate alla mappatura vengono ``\textsl{popolate}'' in una sola volta,
+questo comportamento viene abilitato quando si usa con \func{mmap} il flag
+\const{MAP\_POPULATE}.
Dato che l'uso di \const{MAP\_POPULATE} comporta dell'I/O su disco che può
rallentare l'esecuzione di \func{mmap} è stato introdotto anche un secondo
database effettuare accessi ai dati in maniera pressoché casuale, mentre un
riproduttore audio o video eseguirà per lo più letture sequenziali.
+\itindend{memory~mapping}
+
Per migliorare le prestazioni a seconda di queste modalità di accesso è
disponibile una apposita funzione, \funcd{madvise},\footnote{tratteremo in
sez.~\ref{sec:file_fadvise} le funzioni che consentono di ottimizzare
l'accesso ai file con l'interfaccia classica.} che consente di fornire al
-kernel delle indicazioni su dette modalità, così che possano essere adottate
-le opportune strategie di ottimizzazione. Il suo prototipo è:
-\begin{functions}
- \headdecl{sys/mman.h}
+kernel delle indicazioni su come un processo intende accedere ad un segmento
+di memoria, anche al di là delle mappature dei file, così che possano essere
+adottate le opportune strategie di ottimizzazione. Il suo prototipo è:
- \funcdecl{int madvise(void *start, size\_t length, int advice)}
-
- Fornisce indicazioni sull'uso previsto di un \textit{memory mapping}.
+\begin{funcproto}{
+\fhead{sys/mman.h}
+\fdecl{int madvise(void *start, size\_t length, int advice)}
+\fdesc{Fornisce indicazioni sull'uso previsto di un segmento di memoria.}
+}
- \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di
- errore, nel qual caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
+{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
+ caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
\item[\errcode{EBADF}] la mappatura esiste ma non corrisponde ad un file.
\item[\errcode{EINVAL}] \param{start} non è allineato alla dimensione di
una pagina, \param{length} ha un valore negativo, o \param{advice} non è
un valore valido, o si è richiesto il rilascio (con
- \const{MADV\_DONTNEED}) di pagine bloccate o condivise.
+ \const{MADV\_DONTNEED}) di pagine bloccate o condivise o si è usato
+ \const{MADV\_MERGEABLE} o \const{MADV\_UNMERGEABLE} ma il kernel non è
+ stato compilato per il relativo supporto.
\item[\errcode{EIO}] la paginazione richiesta eccederebbe i limiti (vedi
sez.~\ref{sec:sys_resource_limit}) sulle pagine residenti in memoria del
processo (solo in caso di \const{MADV\_WILLNEED}).
\item[\errcode{ENOMEM}] gli indirizzi specificati non sono mappati, o, in
caso \const{MADV\_WILLNEED}, non c'è sufficiente memoria per soddisfare
la richiesta.
- \end{errlist}
- ed inoltre \errval{EAGAIN} e \errval{ENOSYS}.
- }
-\end{functions}
+ \end{errlist}
+ ed inoltre \errval{EAGAIN} e \errval{ENOSYS} nel loro significato generico.}
+\end{funcproto}
La sezione di memoria sulla quale si intendono fornire le indicazioni deve
essere indicata con l'indirizzo iniziale \param{start} e l'estensione
\param{length}, il valore di \param{start} deve essere allineato,
-mentre \param{length} deve essere un numero positivo.\footnote{la versione di
- Linux consente anche un valore nullo per \param{length}, inoltre se una
- parte dell'intervallo non è mappato in memoria l'indicazione viene comunque
- applicata alle restanti parti, anche se la funzione ritorna un errore di
- \errval{ENOMEM}.} L'indicazione viene espressa dall'argomento \param{advice}
-che deve essere specificato con uno dei valori\footnote{si tenga presente che
- gli ultimi tre valori sono specifici di Linux (introdotti a partire dal
- kernel 2.6.16) e non previsti dallo standard POSIX.1b.} riportati in
-tab.~\ref{tab:madvise_advice_values}.
+mentre \param{length} deve essere un numero positivo; la versione di Linux
+consente anche un valore nullo per \param{length}, inoltre se una parte
+dell'intervallo non è mappato in memoria l'indicazione viene comunque
+applicata alle restanti parti, anche se la funzione ritorna un errore di
+\errval{ENOMEM}.
+
+L'indicazione viene espressa dall'argomento \param{advice} che deve essere
+specificato con uno dei valori riportati in
+tab.~\ref{tab:madvise_advice_values}; si tenga presente che i valori indicati
+nella seconda parte della tabella sono specifici di Linux e non sono previsti
+dallo standard POSIX.1b. La funzione non ha, tranne il caso di
+\const{MADV\_DONTFORK}, nessun effetto sul comportamento di un programma, ma
+può influenzarne le prestazioni fornendo al kernel indicazioni sulle esigenze
+dello stesso, così che sia possibile scegliere le opportune strategie per la
+gestione del \textit{read-ahead} (vedi sez.~\ref{sec:file_fadvise}) e del
+caching dei dati.
-\begin{table}[htb]
+\begin{table}[!htb]
\centering
\footnotesize
\begin{tabular}[c]{|l|p{10 cm}|}
\textbf{Valore} & \textbf{Significato} \\
\hline
\hline
+ \const{MADV\_DONTNEED}& non ci si aspetta nessun accesso nell'immediato
+ futuro, pertanto le pagine possono essere
+ liberate dal kernel non appena necessario; l'area
+ di memoria resterà accessibile, ma un accesso
+ richiederà che i dati vengano ricaricati dal file
+ a cui la mappatura fa riferimento.\\
\const{MADV\_NORMAL} & nessuna indicazione specifica, questo è il valore
di default usato quando non si è chiamato
\func{madvise}.\\
\const{MADV\_RANDOM} & ci si aspetta un accesso casuale all'area
indicata, pertanto l'applicazione di una lettura
anticipata con il meccanismo del
- \itindex{read-ahead} \textit{read-ahead} (vedi
+ \textit{read-ahead} (vedi
sez.~\ref{sec:file_fadvise}) è di
scarsa utilità e verrà disabilitata.\\
\const{MADV\_SEQUENTIAL}& ci si aspetta un accesso sequenziale al file,
\const{MADV\_WILLNEED}& ci si aspetta un accesso nell'immediato futuro,
pertanto l'applicazione del \textit{read-ahead}
deve essere incentivata.\\
- \const{MADV\_DONTNEED}& non ci si aspetta nessun accesso nell'immediato
- futuro, pertanto le pagine possono essere
- liberate dal kernel non appena necessario; l'area
- di memoria resterà accessibile, ma un accesso
- richiederà che i dati vengano ricaricati dal file
- a cui la mappatura fa riferimento.\\
\hline
- \const{MADV\_REMOVE} & libera un intervallo di pagine di memoria ed il
- relativo supporto sottostante; è supportato
- soltanto sui filesystem in RAM \textit{tmpfs} e
- \textit{shmfs}.\footnotemark\\
+ \const{MADV\_DONTDUMP}& esclude da un \textit{core dump} (vedi
+ sez.~\ref{sec:sig_standard}) le pagine
+ specificate, viene usato per evitare di scrivere
+ su disco dati relativi a zone di memoria che si sa
+ non essere utili in un \textit{core dump}.\\
+ \const{MADV\_DODUMP} & rimuove l'effetto della precedente
+ \const{MADV\_DONTDUMP} (dal kernel 3.4).\\
\const{MADV\_DONTFORK}& impedisce che l'intervallo specificato venga
ereditato dal processo figlio dopo una
\func{fork}; questo consente di evitare che il
- meccanismo del \itindex{copy~on~write}
- \textit{copy on write} effettui la rilocazione
- delle pagine quando il padre scrive sull'area
- di memoria dopo la \func{fork}, cosa che può
- causare problemi per l'hardware che esegue
- operazioni in DMA su quelle pagine.\\
+ meccanismo del \textit{copy on write} effettui la
+ rilocazione delle pagine quando il padre scrive
+ sull'area di memoria dopo la \func{fork}, cosa che
+ può causare problemi per l'hardware che esegue
+ operazioni in DMA su quelle pagine (dal kernel
+ 2.6.16).\\
\const{MADV\_DOFORK} & rimuove l'effetto della precedente
- \const{MADV\_DONTFORK}.\\
- \const{MADV\_MERGEABLE}& marca la pagina come accorpabile (indicazione
+ \const{MADV\_DONTFORK} (dal kernel 2.6.16).\\
+ \const{MADV\_HUGEPAGE}& abilita il meccanismo delle \textit{Transparent
+ Huge Page} (vedi sez.~\ref{sec:huge_pages})
+ sulla regione indicata; se questa è allineata
+ alle relative dimensioni il kernel alloca
+ direttamente delle \textit{huge page}; è
+ utilizzabile solo con mappature anomime private
+ (dal kernel 2.6.38).\\
+ \const{MADV\_NOHUGEPAGE}& impedisce che la regione indicata venga
+ collassata in eventuali \textit{huge page} (dal
+ kernel 2.6.38).\\
+ \const{MADV\_HWPOISON} &opzione ad uso di debug per verificare codice
+ che debba gestire errori nella gestione della
+ memoria; richiede una apposita opzione di
+ compilazione del kernel, privilegi amministrativi
+ (la capacità \const{CAP\_SYS\_ADMIN}) e provoca
+ l'emissione di un segnale di \const{SIGBUS} dal
+ programma chiamante e rimozione della mappatura
+ (dal kernel 2.6.32).\\
+ \const{MADV\_SOFT\_OFFLINE}&opzione utilizzata per il debug del
+ codice di verifica degli errori di gestione
+ memoria, richiede una apposita opzione di
+ compilazione (dal kernel 2.6.33).\\
+ \const{MADV\_MERGEABLE}& marca la pagina come accorpabile, indicazione
principalmente ad uso dei sistemi di
- virtualizzazione).\footnotemark\\
- \hline
+ virtualizzazione\footnotemark (dal kernel 2.6.32).\\
+ \const{MADV\_REMOVE} & libera un intervallo di pagine di memoria ed il
+ relativo supporto sottostante; è supportato
+ soltanto sui filesystem in RAM \textit{tmpfs} e
+ \textit{shmfs} se usato su altri tipi di
+ filesystem causa un errore di \errcode{ENOSYS}
+ (dal kernel 2.6.16).\\
+ \const{MADV\_UNMERGEABLE}& rimuove l'effetto della precedente
+ \const{MADV\_MERGEABLE} (dal kernel 2.6.32). \\
+ \hline
\end{tabular}
\caption{Valori dell'argomento \param{advice} di \func{madvise}.}
\label{tab:madvise_advice_values}
\end{table}
-\footnotetext{se usato su altri tipi di filesystem causa un errore di
- \errcode{ENOSYS}.}
-
\footnotetext{a partire dal kernel 2.6.32 è stato introdotto un meccanismo che
identifica pagine di memoria identiche e le accorpa in una unica pagina
(soggetta al \textit{copy-on-write} per successive modifiche); per evitare
la loro occupazione di memoria, ma il meccanismo può essere usato anche in
altre applicazioni in cui sian presenti numerosi processi che usano gli
stessi dati; per maggiori dettagli si veda
- \href{http://kernelnewbies.org/Linux_2_6_32\#head-d3f32e41df508090810388a57efce73f52660ccb}{\texttt{http://kernelnewbies.org/Linux\_2\_6\_32}}.}
-
-La funzione non ha, tranne il caso di \const{MADV\_DONTFORK}, nessun effetto
-sul comportamento di un programma, ma può influenzarne le prestazioni fornendo
-al kernel indicazioni sulle esigenze dello stesso, così che sia possibile
-scegliere le opportune strategie per la gestione del \itindex{read-ahead}
-\textit{read-ahead} e del caching dei dati. A differenza da quanto specificato
-nello standard POSIX.1b, per il quale l'uso di \func{madvise} è a scopo
-puramente indicativo, Linux considera queste richieste come imperative, per
-cui ritorna un errore qualora non possa soddisfarle.\footnote{questo
- comportamento differisce da quanto specificato nello standard.}
+ \href{http://kernelnewbies.org/Linux_2_6_32\#head-d3f32e41df508090810388a57efce73f52660ccb}{\texttt{http://kernelnewbies.org/Linux\_2\_6\_32}}
+ e la documentazione nei sorgenti del kernel
+ (\texttt{Documentation/vm/ksm.txt}).}
+
+
+A differenza da quanto specificato nello standard POSIX.1b, per il quale l'uso
+di \func{madvise} è a scopo puramente indicativo, Linux considera queste
+richieste come imperative, per cui ritorna un errore qualora non possa
+soddisfarle; questo comportamento differisce da quanto specificato nello
+standard.
+
+Nello standard POSIX.1-2001 è prevista una ulteriore funzione
+\funcd{posix\_madvise} che su Linux viene reimplementata utilizzando
+\func{madvise}; il suo prototipo è:
+
+\begin{funcproto}{
+\fhead{sys/mman.h}
+\fdecl{int posix\_madvise(void *start, size\_t lenght, int advice)}
+\fdesc{Fornisce indicazioni sull'uso previsto di un segmento di memoria.}
+}
+
+{La funzione ritorna $0$ in caso di successo ed un valore positivo per un
+ errore, nel qual caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
+ \item[\errcode{EINVAL}] \param{start} non è allineato alla dimensione di
+ una pagina, \param{length} ha un valore negativo, o \param{advice} non è
+ un valore valido.
+ \item[\errcode{ENOMEM}] gli indirizzi specificati non sono nello spazio di
+ indirizzi del processo.
+ \end{errlist}
+}
+\end{funcproto}
+
+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
+ \footnotesize
+ \begin{tabular}[c]{|l|l|}
+ \hline
+ \textbf{Valore} & \textbf{Significato} \\
+ \hline
+ \hline
+ \const{POSIX\_MADV\_DONTNEED}& analogo a \const{MADV\_DONTNEED}.\\
+ \const{POSIX\_MADV\_NORMAL} & identico a \const{MADV\_NORMAL}.\\
+ \const{POSIX\_MADV\_RANDOM} & identico a \const{MADV\_RANDOM}.\\
+ \const{POSIX\_MADV\_SEQUENTIAL}& identico a \const{MADV\_SEQUENTIAL}.\\
+ \const{POSIX\_MADV\_WILLNEED}& identico a \const{MADV\_WILLNEED}.\\
+ \hline
+ \end{tabular}
+ \caption{Valori dell'argomento \param{advice} di \func{posix\_madvise}.}
+ \label{tab:posix_madvise_advice_values}
+\end{table}
+
+
+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,
+che viene considerato distruttivo.
-\itindend{memory~mapping}
\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
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
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
\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
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
-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.}
+sola chiamata a \funcd{sendfile}. In questo modo si poteva diminuire il numero
+di chiamate al sistema e risparmiare in trasferimenti di dati da
+\textit{kernel space} a \textit{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 (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
+\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 \textit{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} darà luogo ad un errore di \errcode{EINVAL}.
+casi l'uso di \func{sendfile} da 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
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.}
+l'allocazione di un buffer temporaneo per il loro trasferimento. Comunque a
+partire dal kernel 2.6.33 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'\textit{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
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
-\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
- kernel space}''.
+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 \textit{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 \textit{user space}
+l'oggetto ``\textsl{buffer in kernel space}''.
Così se per una \textit{pipe} o una \textit{fifo} il buffer viene utilizzato
come area di memoria (vedi fig.~\ref{fig:ipc_pipe_singular}) dove appoggiare i
dati che vengono trasferiti da un capo all'altro della stessa per creare un
meccanismo di comunicazione fra processi, nel caso di \func{splice} il buffer
viene usato o come fonte dei dati che saranno scritti su un file, o come
-destinazione dei dati che vengono letti da un file. La funzione \funcd{splice}
-fornisce quindi una interfaccia generica che consente di trasferire dati da un
-buffer ad un file o viceversa; il suo prototipo, accessibile solo dopo aver
-definito la macro \macro{\_GNU\_SOURCE},\footnote{si ricordi che questa
+destinazione dei dati che vengono letti da un file. La funzione fornisce
+quindi una interfaccia generica che consente di trasferire dati da un buffer
+ad un file o viceversa; il prototipo di \funcd{splice}, accessibile solo dopo
+aver 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
- *off\_out, size\_t len, unsigned int flags)}
-
- Trasferisce dati da un file verso una \textit{pipe} o viceversa.
+\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, \\
+\phantom{long splice(}unsigned int flags)}
+\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.
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
-una \textit{pipe}; l'altro file descriptor può essere
-qualunque.\footnote{questo significa che può essere, oltre che un file di
- dati, anche un altra \textit{pipe}, o un socket.} Come accennato una
-\textit{pipe} non è altro che un buffer in kernel space, per cui a seconda che
-essa sia usata per \param{fd\_in} o \param{fd\_out} si avrà rispettivamente la
-copia dei dati dal buffer al file o viceversa.
+una \textit{pipe}; l'altro file descriptor può essere qualunque, questo
+significa che può essere, oltre che un file di dati, anche un altra
+\textit{pipe}, o un socket. Come accennato una \textit{pipe} non è altro che
+un buffer in \textit{kernel space}, per cui a seconda che essa sia usata
+per \param{fd\_in} o \param{fd\_out} si avrà rispettivamente la copia dei dati
+dal buffer al file o viceversa.
In caso di successo la funzione ritorna il numero di byte trasferiti, che può
essere, come per le normali funzioni di lettura e scrittura su file, inferiore
\hline
\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}.\\
+ copiarle: 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 \textit{pipe} o
+ il buffer non corrisponda a pagine intere
+ esse saranno comunque copiate. 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
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
+ socket. 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}). Attualmente
+ viene usato solo da \func{splice}, potrà essere
implementato in futuro anche per
\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
+ ``\textsl{donate}'' al kernel; 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.
+ 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
\label{tab:splice_flag}
\end{table}
-\footnotetext[120]{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 \textit{pipe} o il buffer non corrisponda a pagine
- intere esse saranno comunque copiate.}
-
-\footnotetext[121]{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}.
+su un altro senza utilizzare buffer in \textit{user space}. Lo scopo del
+programma è quello di eseguire la copia dei dati con \func{splice}, questo
+significa che si dovrà usare la funzione due volte, prima per leggere i dati
+dal file di ingresso e poi per scriverli su quello di uscita, appoggiandosi 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}
+ \includegraphics[height=3.5cm]{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.
+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, le funzioni di ausilio, le
+aperture dei file di ingresso e di uscita passati come argomenti e quella
+della \textit{pipe} intermedia, è riportato in fig.~\ref{fig:splice_example}.
-\begin{figure}[!htbp]
+\begin{figure}[!htb]
\footnotesize \centering
\begin{minipage}[c]{\codesamplewidth}
\includecodesample{listati/splicecp.c}
\label{fig:splice_example}
\end{figure}
-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
+Il ciclo principale (\texttt{\small 13-38}) inizia con la lettura dal file
+sorgente tramite la prima \func{splice} (\texttt{\small 14-15}), 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
+come terzo quello del capo in scrittura della \textit{pipe}. Il funzionamento
delle \textit{pipe} e l'uso della coppia di file descriptor ad esse associati
è trattato in dettaglio in sez.~\ref{sec:ipc_unix}; non ne parleremo qui dato
che nell'ottica dell'uso di \func{splice} questa operazione corrisponde
-semplicemente al trasferimento dei dati dal file al buffer).
+semplicemente al trasferimento dei dati dal file al buffer in \textit{kernel
+ space}.
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
-detto valore è nullo (\texttt{\small 36}) questo significa che si è giunti
+detto valore è nullo (\texttt{\small 16}) 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
+ 59}). In caso di valore negativo (\texttt{\small 17-24}) c'è stato un
+errore ed allora si ripete la lettura (\texttt{\small 16}) se questo è dovuto
ad una interruzione, o altrimenti si esce con un messaggio di errore
-(\texttt{\small 41-43}).
+(\texttt{\small 21-23}).
Una volta completata con successo la lettura si avvia il ciclo di scrittura
-(\texttt{\small 45-57}); questo inizia (\texttt{\small 46-47}) con la
+(\texttt{\small 25-37}); questo inizia (\texttt{\small 26-27}) 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
Di nuovo si controlla il numero di byte effettivamente scritti restituito in
\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
+(\texttt{\small 28-35}). Infine si chiude il ciclo di scrittura sottraendo
+(\texttt{\small 37}) 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
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 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.
+al posto della seconda, utilizzando un buffer in \textit{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 \textit{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 anche
altre due \textit{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 trasferimento dati fra file
+trasferimento di dati attraverso un buffer in \textit{kernel space}; benché
+queste non attengono strettamente ad operazioni di trasferimento dati fra file
descriptor, le tratteremo qui, essendo strettamente correlate fra loro.
La prima funzione, \funcd{vmsplice}, è la più simile a \func{splice} e come
indica il suo nome consente di trasferire i dati dalla memoria virtuale di un
processo (ad esempio per un file mappato in memoria) 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}.
+\begin{funcproto}{
+\fhead{fcntl.h}
+\fhead{sys/uio.h}
+\fdecl{long vmsplice(int fd, const struct iovec *iov, unsigned long nr\_segs,\\
+\phantom{long vmsplice(}unsigned int flags)}
+\fdesc{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}
+{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}] 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}
+ \end{errlist}
+}
+\end{funcproto}
La \textit{pipe} indicata da \param{fd} dovrà essere specificata tramite il
file descriptor corrispondente al suo capo aperto in scrittura (di nuovo si
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}
+suo nome all'omonimo comando in \textit{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 \textit{kernel space}, la funzione consente di
+eseguire una copia del contenuto del buffer stesso. Il prototipo di
+\funcd{tee} è il seguente:
- \funcdecl{long tee(int fd\_in, int fd\_out, size\_t len, unsigned int
+\begin{funcproto}{
+\fhead{fcntl.h}
+\fdecl{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.
+\fdesc{Duplica i dati 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}
+{La funzione ritorna restituisce il numero di byte copiati in caso di successo
+ e $-1$ per un 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}
+ \end{errlist}
+}
+\end{funcproto}
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}
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 e che il capo in scrittura della \textit{pipe} è
-stato chiuso.\footnote{si tenga presente però che questo non avviene se si è
- impostato il flag \const{SPLICE\_F\_NONBLOCK}, in tal caso infatti si
- avrebbe un errore di \errcode{EAGAIN}.} Un esempio di realizzazione del
-comando \texttt{tee} usando questa funzione, ripreso da quello fornito nella
-pagina di manuale e dall'esempio allegato al patch originale, è riportato in
+stato chiuso; si tenga presente però che questo non avviene se si è impostato
+il flag \const{SPLICE\_F\_NONBLOCK}, in tal caso infatti si avrebbe un errore
+di \errcode{EAGAIN}. Un esempio di realizzazione del comando \texttt{tee}
+usando questa funzione, ripreso da quello fornito nella pagina di manuale e
+dall'esempio allegato al patch originale, è riportato in
fig.~\ref{fig:tee_example}. Il programma consente di copiare il contenuto
-dello standard input sullo standard output e su un file specificato come
-argomento, il codice completo si trova nel file \texttt{tee.c} dei sorgenti
-allegati alla guida.
+dello \textit{standard input} sullo \textit{standard output} e su un file
+specificato come argomento, il codice completo si trova nel file
+\texttt{tee.c} dei sorgenti allegati alla guida.
-\begin{figure}[!htbp]
+\begin{figure}[!htb]
\footnotesize \centering
\begin{minipage}[c]{\codesamplewidth}
\includecodesample{listati/tee.c}
\label{fig:tee_example}
\end{figure}
-La prima parte del programma (\texttt{\small 10-35}) si cura semplicemente di
-controllare (\texttt{\small 11-14}) che sia stato fornito almeno un argomento
-(il nome del file su cui scrivere), di aprirlo ({\small 15-19}) e che sia lo
-standard input (\texttt{\small 20-27}) che lo standard output (\texttt{\small
- 28-35}) corrispondano ad una \textit{pipe}.
+La prima parte del programma, che si è omessa per brevità, si cura
+semplicemente di controllare che sia stato fornito almeno un argomento (il
+nome del file su cui scrivere), di aprirlo e che sia lo standard input che lo
+standard output corrispondano ad una \textit{pipe}.
-Il ciclo principale (\texttt{\small 37-58}) inizia con la chiamata a
+Il ciclo principale (\texttt{\small 11-32}) inizia con la chiamata a
\func{tee} che duplica il contenuto dello standard input sullo standard output
-(\texttt{\small 39}), questa parte è del tutto analoga ad una lettura ed
+(\texttt{\small 13}), questa parte è del tutto analoga ad una lettura ed
infatti come nell'esempio di fig.~\ref{fig:splice_example} si controlla il
valore di ritorno della funzione in \var{len}; se questo è nullo significa che
-non ci sono più dati da leggere e si chiude il ciclo (\texttt{\small 40}), se
+non ci sono più dati da leggere e si chiude il ciclo (\texttt{\small 14}), se
è negativo c'è stato un errore, ed allora si ripete la chiamata se questo è
-dovuto ad una interruzione (\texttt{\small 42-44}) o si stampa un messaggio
-di errore e si esce negli altri casi (\texttt{\small 44-47}).
-
-Una volta completata la copia dei dati sullo standard output si possono
-estrarre dalla standard input e scrivere sul file, di nuovo su usa un ciclo di
-scrittura (\texttt{\small 50-58}) in cui si ripete una chiamata a
-\func{splice} (\texttt{\small 51}) fintanto che non si sono scritti tutti i
-\var{len} byte copiati in precedenza con \func{tee} (il funzionamento è
-identico all'analogo ciclo di scrittura del precedente esempio di
+dovuto ad una interruzione (\texttt{\small 15-48}) o si stampa un messaggio
+di errore e si esce negli altri casi (\texttt{\small 18-21}).
+
+Una volta completata la copia dei dati sullo \textit{standard output} si
+possono estrarre dallo \textit{standard input} e scrivere sul file, di nuovo
+su usa un ciclo di scrittura (\texttt{\small 24-31}) in cui si ripete una
+chiamata a \func{splice} (\texttt{\small 25}) fintanto che non si sono scritti
+tutti i \var{len} byte copiati in precedenza con \func{tee} (il funzionamento
+è identico all'analogo ciclo di scrittura del precedente esempio di
fig.~\ref{fig:splice_example}).
Infine una nota finale riguardo \func{splice}, \func{vmsplice} e \func{tee}:
\itindbeg{read-ahead}
Una prima funzione che può essere utilizzata per modificare la gestione
-ordinaria dell'I/O su un file è \funcd{readahead},\footnote{questa è una
- funzione specifica di Linux, introdotta con il kernel 2.4.13, e non deve
- essere usata se si vogliono scrivere programmi portabili.} che consente di
-richiedere una lettura anticipata del contenuto dello stesso in cache, così
-che le seguenti operazioni di lettura non debbano subire il ritardo dovuto
-all'accesso al disco; il suo prototipo è:
-\begin{functions}
- \headdecl{fcntl.h}
+ordinaria dell'I/O su un file è \funcd{readahead} (questa è una funzione
+specifica di Linux, introdotta con il kernel 2.4.13, e non deve essere usata
+se si vogliono scrivere programmi portabili), che consente di richiedere una
+lettura anticipata del contenuto dello stesso in cache, così che le seguenti
+operazioni di lettura non debbano subire il ritardo dovuto all'accesso al
+disco; il suo prototipo è:
- \funcdecl{ssize\_t readahead(int fd, off64\_t *offset, size\_t count)}
-
- Esegue una lettura preventiva del contenuto di un file in cache.
+\begin{funcproto}{
+\fhead{fcntl.h}
+\fdecl{ssize\_t readahead(int fd, off64\_t *offset, size\_t count)}
+\fdesc{Esegue una lettura preventiva del contenuto di un file in cache.}
+}
- \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di
- errore, nel qual caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
+{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
+ caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
\item[\errcode{EBADF}] l'argomento \param{fd} non è un file descriptor
valido o non è aperto in lettura.
\item[\errcode{EINVAL}] l'argomento \param{fd} si riferisce ad un tipo di
file che non supporta l'operazione (come una \textit{pipe} o un socket).
- \end{errlist}
- }
-\end{functions}
+ \end{errlist}
+}
+\end{funcproto}
La funzione richiede che venga letto in anticipo il contenuto del file
\param{fd} a partire dalla posizione \param{offset} e per un ammontare di
in blocchi corrispondenti alle dimensioni delle pagine di memoria, ed i valori
di \param{offset} e \param{count} vengono arrotondati di conseguenza.
-La funzione estende quello che è un comportamento normale del kernel che
+La funzione estende quello che è un comportamento normale del kernel che,
quando si legge un file, aspettandosi che l'accesso prosegua, esegue sempre
una lettura preventiva di una certa quantità di dati; questo meccanismo di
lettura anticipata viene chiamato \textit{read-ahead}, da cui deriva il nome
\itindend{read-ahead}
Il concetto di \func{readahead} viene generalizzato nello standard
-POSIX.1-2001 dalla funzione \func{posix\_fadvise},\footnote{anche se
- l'argomento \param{len} è stato modificato da \type{size\_t} a \type{off\_t}
- nella revisione POSIX.1-2003 TC5.} che consente di ``\textsl{avvisare}'' il
+POSIX.1-2001 dalla funzione \func{posix\_fadvise} (anche se
+l'argomento \param{len} è stato modificato da \type{size\_t} a \type{off\_t}
+nella revisione POSIX.1-2003 TC5) che consente di ``\textsl{avvisare}'' il
kernel sulle modalità con cui si intende accedere nel futuro ad una certa
-porzione di un file,\footnote{la funzione però è stata introdotta su Linux
- solo a partire dal kernel 2.5.60.} così che esso possa provvedere le
-opportune ottimizzazioni; il prototipo di \funcd{posix\_fadvise}, che è
-disponibile soltanto se è stata definita la macro \macro{\_XOPEN\_SOURCE} ad
-valore di almeno 600, è:
-\begin{functions}
- \headdecl{fcntl.h}
+porzione di un file, così che esso possa provvedere le opportune
+ottimizzazioni; il prototipo di \funcd{posix\_fadvise}\footnote{la funzione è
+ stata introdotta su Linux solo a partire dal kernel 2.5.60, ed è disponibile
+ soltanto se è stata definita la macro \macro{\_XOPEN\_SOURCE} ad valore di
+ almeno \texttt{600} o la macro \macro{\_POSIX\_C\_SOURCE} ad valore di
+ almeno \texttt{200112L}.} è:
- \funcdecl{int posix\_fadvise(int fd, off\_t offset, off\_t len, int advice)}
-
- Dichiara al kernel le future modalità di accesso ad un file.
- \bodydesc{La funzione restituisce 0 in caso di successo e $-1$ in caso di
- errore, nel qual caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
+\begin{funcproto}{
+\fhead{fcntl.h}
+\fdecl{int posix\_fadvise(int fd, off\_t offset, off\_t len, int advice)}
+\fdesc{Dichiara al kernel le future modalità di accesso ad un file.}
+}
+
+{La funzione ritorna $0$ in caso di successo e $-1$ per un errore, nel qual
+ caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
\item[\errcode{EBADF}] l'argomento \param{fd} non è un file descriptor
valido.
\item[\errcode{EINVAL}] il valore di \param{advice} non è valido o
\param{fd} si riferisce ad un tipo di file che non supporta l'operazione
(come una \textit{pipe} o un socket).
- \item[\errcode{ESPIPE}] previsto dallo standard se \param{fd} è una \textit{pipe} o
- un socket (ma su Linux viene restituito \errcode{EINVAL}).
- \end{errlist}
- }
-\end{functions}
+ \item[\errcode{ESPIPE}] previsto dallo standard se \param{fd} è una
+ \textit{pipe} o un socket (ma su Linux viene restituito
+ \errcode{EINVAL}).
+ \end{errlist}
+}
+\end{funcproto}
La funzione dichiara al kernel le modalità con cui intende accedere alla
regione del file indicato da \param{fd} che inizia alla posizione
% nel kernel 3.15 (sul secondo vedi http://lwn.net/Articles/589260/), vedi
% anche http://lwn.net/Articles/629965/
+% TODO aggiungere FALLOC_FL_INSERT vedi http://lwn.net/Articles/629965/
+
+
% TODO non so dove trattarli, ma dal 2.6.39 ci sono i file handle, vedi
% http://lwn.net/Articles/432757/
% LocalWords: sigwaitinfo FifoReporter Windows ptr sigqueue named timerfd TFD
% 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 TLOCK ULOCK EPOLLWAKEUP
%%% Local Variables:
%%% mode: latex
%%% TeX-master: "gapil"
%%% End:
-% LocalWords: sigfd fifofd break siginf names starting echo Message from Got
-% LocalWords: message kill received means exit
+