X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=fileadv.tex;h=87e7bc144e4a2bbc5429780b4f2c0101e1b201cf;hp=58aced40516a754aac668f9960ab69c826ddec85;hb=18598db8d7f2ea072ae8f77ffdbefad1384cbcae;hpb=5d09b56fe89f540ac47d6690b66ac85facf6d757 diff --git a/fileadv.tex b/fileadv.tex index 58aced4..87e7bc1 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -170,8 +170,7 @@ effettuare una lettura,\footnote{per essere precisi la funzione ritorner accadere che \func{select} riporti il relativo file descriptor come leggibile, ma una successiva \func{read} si blocchi.} il secondo, \param{writefds}, per verificare la possibilità effettuare una scrittura ed il -terzo, -\param{exceptfds}, per verificare l'esistenza di eccezioni (come i dati +terzo, \param{exceptfds}, per verificare l'esistenza di eccezioni (come i dati urgenti \itindex{out-of-band} su un socket, vedi sez.~\ref{sec:TCP_urgent_data}). @@ -305,7 +304,7 @@ contestualmente all'esecuzione della funzione,\footnote{in Linux per kernel 2.6.16, non era presente la relativa system call, e la funzione era implementata nelle \acr{glibc} attraverso \func{select} (vedi \texttt{man select\_tut}) per cui la possibilità di \itindex{race~condition} - \textit{race condition} permaneva; in tale situzione si può ricorrere ad una + \textit{race condition} permaneva; in tale situazione si può ricorrere ad una soluzione alternativa, chiamata \itindex{self-pipe trick} \textit{self-pipe trick}, che consiste nell'aprire una pipe (vedi sez.~\ref{sec:ipc_pipes}) ed usare \func{select} sul capo in lettura della stessa; si può indicare @@ -357,17 +356,6 @@ indica un'attesa indefinita, mentre un valore nullo comporta il ritorno immediato (e può essere utilizzato per impiegare \func{poll} in modalità \textsl{non-bloccante}). -\begin{figure}[!htb] - \footnotesize \centering - \begin{minipage}[c]{15cm} - \includestruct{listati/pollfd.h} - \end{minipage} - \normalsize - \caption{La struttura \structd{pollfd}, utilizzata per specificare le - modalità di controllo di un file descriptor alla funzione \func{poll}.} - \label{fig:file_pollfd} -\end{figure} - Per ciascun file da controllare deve essere inizializzata una struttura \struct{pollfd} nel vettore indicato dall'argomento \param{ufds}. La struttura, la cui definizione è riportata in fig.~\ref{fig:file_pollfd}, @@ -381,6 +369,17 @@ tutto indipendenti da quelli in uscita (che vengono restituiti in \var{revents}) non è necessario reinizializzare tutte le volte il valore delle strutture \struct{pollfd} a meno di non voler cambiare qualche condizione. +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includestruct{listati/pollfd.h} + \end{minipage} + \normalsize + \caption{La struttura \structd{pollfd}, utilizzata per specificare le + modalità di controllo di un file descriptor alla funzione \func{poll}.} + \label{fig:file_pollfd} +\end{figure} + Le costanti che definiscono i valori relativi ai bit usati nelle maschere binarie dei campi \var{events} e \var{revents} sono riportati in tab.~\ref{tab:file_pollfd_flags}, insieme al loro significato. Le si sono @@ -444,11 +443,30 @@ valore nullo indica che si indica un errore nella chiamata, il cui codice viene riportato al solito tramite \var{errno}. +L'uso di \func{poll} consente di superare alcuni dei problemi illustrati in +precedenza per \func{select}; anzitutto, dato che in questo caso si usa un +vettore di strutture \struct{pollfd} di dimensione arbitraria, non esiste il +limite introdotto dalle dimesioni massime di un \itindex{file~descriptor~set} +\textit{file descriptor set} e la dimensione dei dati passati al kernel +dipende solo dal numero dei file descriptor che si vogliono controllare, non +dal loro valore.\footnote{anche se usando dei bit un \textit{file descriptor + set} può essere più efficiente di un vettore di strutture \struct{pollfd}, + qualora si debba osservare un solo file descriptor con un valore molto alto + ci si troverà ad utilizzare inutilmente un maggiore quantitativo di + memoria.} + +Inoltre con \func{select} lo stesso \itindex{file~descriptor~set} \textit{file + descriptor set} è usato sia in ingresso che in uscita, e questo significa +che tutte le volte che si vuole ripetere l'operazione occorre reinizializzarlo +da capo. Questa operazione, che può essere molto onerosa se i file descriptor +da tenere sotto osservazione sono molti, non è invece necessaria con +\func{poll}. + Abbiamo visto in sez.~\ref{sec:file_select} come lo standard POSIX preveda una variante di \func{select} che consente di gestire correttamente la ricezione dei segnali nell'attesa su un file descriptor. Con l'introduzione di una implementazione reale di \func{pselect} nel kernel 2.6.16, è stata aggiunta -anche una analoga funzione che svolga lo stesso ruolo per \func{poll}. +anche una analoga funzione che svolga lo stesso ruolo per \func{poll}. In questo caso si tratta di una estensione che è specifica di Linux e non è prevista da nessuno standard; essa può essere utilizzata esclusivamente se si @@ -489,13 +507,49 @@ puntatore ad una struttura \struct{timespec}, gli altri argomenti comuni con risultati illustrati in precedenza. -% TODO accennare a ppoll vedi articolo LWN http://lwn.net/Articles/176750/ +\subsection{L'interfaccia di \textit{epoll}} +\label{sec:file_epoll} + +\itindbeg{epoll} + +Nonostante \func{poll} presenti alcuni vantaggi rispetto a \func{select}, +anche questa funzione non è molto efficiente quando deve essere utilizzata con +un gran numero di file descriptor,\footnote{in casi del genere \func{select} + viene scartata a priori, perché può avvenire che il numero di file + descriptor ecceda le dimensioni massime di un \itindex{file~descriptor~set} + \textit{file descriptor set}.} in particolare nel caso in cui solo pochi di +questi diventano attivi. Il problema in questo caso è che il tempo impiegato +da \func{poll} a trasferire i dati da e verso il kernel è proporzionale al +numero di file descriptor osservati, non a quelli che presentano attività. + +Quando ci sono decine di migliaia di file descriptor osservati e migliaia di +eventi al secondo,\footnote{il caso classico è quello di un server web di un + sito con molti accessi.} l'uso di \func{poll} comporta la necessità di +trasferire avanti ed indietro da user space a kernel space la lunga lista +delle strutture \struct{pollfd} migliaia di volte al secondo. A questo poi si +aggiunge il fatto che la maggior parte del tempo di esecuzione sarà impegnato +ad eseguire una scansione su tutti i file descriptor tenuti sotto controllo +per determinare quali di essi (in genere una piccola percentuale) sono +diventati attivi. In una situazione come questa l'uso delle funzioni classiche +dell'interfaccia dell'\textit{I/O multiplexing} viene a costituire un collo di +bottiglia che degrada irrimediabilmente le prestazioni. + +Per risolvere questo tipo di situazioni è stata creata una nuova +interfaccia,\footnote{l'interfaccia è stata creata da Davide Libernzi, ed è + stata introdotta per la prima volta nel kernel 2.5.44, ma la sua forma + definitiva è stata raggiunta nel kernel 2.5.66.} detta \textit{epoll}, il +cui concetto fondamentale è quello di restituire solamente le informazioni +relative ai file descriptor osservati che presentano una attività, evitando +così tutti le problematiche appena illustrate. + + + -%\subsection{L'interfaccia di \textit{epoll}} -%\label{sec:file_epoll} -% placeholder ... + +\itindend{epoll} % TODO epoll +% \section{L'accesso \textsl{asincrono} ai file} \label{sec:file_asyncronous_access} @@ -541,13 +595,11 @@ sar accesso ai file; per questo motivo Stevens chiama questa modalità \textit{signal driven I/O}. -In questo modo si può evitare l'uso delle funzioni \func{poll} o \func{select} -che, quando vengono usate con un numero molto grande di file descriptor, non -hanno buone prestazioni. % TODO aggiungere cenno a epoll quando l'avrò scritta - In tal caso infatti la maggior parte del loro tempo -di esecuzione è impegnato ad eseguire una scansione su tutti i file descriptor -tenuti sotto controllo per determinare quali di essi (in genere una piccola -percentuale) sono diventati attivi. +Questa è un'altra modalità di gestione I/O, alternativa all'uso di +\itindex{epoll} \textit{epoll}, che consente di evitare l'uso delle funzioni +\func{poll} o \func{select} che, come illustrato in sez.~\ref{sec:file_epoll}, +quando vengono usate con un numero molto grande di file descriptor, non hanno +buone prestazioni. Tuttavia con l'implementazione classica dei segnali questa modalità di I/O presenta notevoli problemi, dato che non è possibile determinare, quando i @@ -797,7 +849,6 @@ tramite il contenuto della struttura \struct{siginfo\_t}. \label{tab:file_notify} \end{table} - Ci si può registrare per le notifiche dei cambiamenti al contenuto di una certa directory eseguendo la funzione \func{fcntl} su un file descriptor associato alla stessa con il comando \const{F\_NOTIFY}. In questo caso @@ -819,20 +870,32 @@ specificare un valore nullo. \index{file!inotify|(} -Il maggiore problema di \textit{dnotify} è quello della scalabilità, e della -complessità di gestione dovuta all'uso dei segnali. Per questo motivo a -partire dal kernel 2.6.13 è stata introdotta una nuova interfaccia per -l'osservazione delle modifiche a file o directory, chiamata -\textit{inotify}.\footnote{le corrispondenti funzioni di interfaccia sono - state introdotte nelle glibc 2.4.} +Il maggiore problema di \textit{dnotify} è quello della scalabilità: si deve +usare un file descriptor per ciascuna directory che si vuole tenere sotto +controllo, il che porta facilmente ad un eccesso di file aperti. Inoltre +quando la directory è su un dispositivo rimuovibile, mantenere un file +descriptor aperto comporta l'impossibilità di smontare il dispositivo e +rimuoverlo, complicando la gestione. + +Un secondo problema è che l'interfaccia consente solo di tenere sotto +controllo il contenuto di una directory; la modifica di un file viene +segnalata, ma poi devo verificare quale è. Infine l'uso dei segnali come +interfaccia di notifica comporta tutti i problemi di gestione visti in +sez.~\ref{sec:sig_management} e sez.~\ref{sec:sig_control}, e per questo in +generale quella di \textit{dnotify} viene considerata una interfaccia di +usabilità problematica. \index{file!dnotify|)} -L'interfaccia di \textit{inotify} è una caratteristica specifica di Linux -(pertanto non deve essere usata se si devono scrivere programmi portabili), ed -è basata sull'uso di una coda di notifica degli eventi associata ad un file -descriptor. La coda viene creata attraverso la funzione \funcd{inotify\_init}, -il cui prototipo è: +Per questa serie di motivi, a partire dal kernel 2.6.13, è stata introdotta +una nuova interfaccia per l'osservazione delle modifiche a file o directory, +chiamata \textit{inotify}.\footnote{le corrispondenti funzioni di interfaccia + sono state introdotte nelle glibc 2.4.} Questa è una interfaccia specifica +di Linux (pertanto non deve essere usata se si devono scrivere programmi +portabili), ed è basata sull'uso di una coda di notifica degli eventi +associata ad un singolo file descriptor, risolvendo così il principale +problema di \itindex{dnotify} \textit{dnotify}. La coda viene creata +attraverso la funzione \funcd{inotify\_init}, il cui prototipo è: \begin{prototype}{sys/inotify.h} {int inotify\_init(void)} @@ -856,31 +919,35 @@ associato alla coda, attraverso il quale verranno effettuate le operazioni di notifica. Si tratta di un file descriptor speciale, che non è associato a nessun file, ma che viene utilizzato per notificare gli eventi che si sono posti in osservazione all'applicazione che usa l'interfaccia di -\textit{inotify}. - -Trattandosi di un file descriptor a tutti gli effetti, esso potrà essere -utilizzato con le funzioni \func{select} e \func{poll}. Dato che gli eventi -vengono notificati come dati disponibili in lettura sul file descriptor -stesso, dette funzioni ritorneranno tutte le volte che si avrà un evento di -notifica. Si potrà cioè gestirlo secondo le modalità illustrate in -sez.~\ref{sec:file_multiplexing}. Inoltre, come per i file descriptor -associati ai socket (vedi sez.~\ref{sec:sock_ioctl_IP}) si potrà ottenere il -numero di byte disponibili in lettura eseguendo su di esso l'operazione -\const{FIONREAD} con \func{ioctl}. - -L'interfaccia di \textit{inotify} consente di mettere sotto osservazione sia -singoli file, che intere directory; in quest'ultimo caso l'interfaccia -restituirà informazioni sia riguardo alla directory che ai file che essa -contiene. Una volta creata la coda di notifica si devono definire gli eventi -da tenere sotto osservazione; questo viene fatto tramite una \textsl{lista di - osservazione} (o \textit{watch list}) associata alla coda. Per gestire la -lista di osservazione l'interfaccia fornisce due funzioni, la prima di queste -è \funcd{inotify\_add\_watch}, il cui prototipo è: +\textit{inotify}. Dato che questo file descriptor non è associato a nessun +file o directory, questo consente di evitare l'inconveniente di non poter +smontare un filesystem i cui file sono tenuti sotto osservazione.\footnote{ed + una delle caratteristiche dell'interfaccia di \textit{inotify} è proprio + quella di notificare il fatto che il filesystem su cui si trova il file o la + directory osservata è stato smontato.} + +Inoltre trattandosi di un file descriptor a tutti gli effetti, esso potrà +essere utilizzato come argomento per le funzioni \func{select} e \func{poll}, +e siccome gli eventi vengono notificati come dati disponibili in lettura sul +file descriptor, dette funzioni ritorneranno tutte le volte che si avrà un +evento di notifica. Così, invece di dover utilizzare i segnali, si potrà +gestire l'osservazione delle modifiche con l'\textit{I/O multiplexing}, +utilizzando secondo le modalità illustrate in +sez.~\ref{sec:file_multiplexing}. + +Infine l'interfaccia di \textit{inotify} consente di mettere sotto +osservazione sia singoli file, che intere directory; in quest'ultimo caso +l'interfaccia restituirà informazioni sia riguardo alla directory che ai file +che essa contiene. Una volta creata la coda di notifica si devono definire +gli eventi da tenere sotto osservazione; questo viene fatto tramite una +\textsl{lista di osservazione} (o \textit{watch list}) associata alla coda. +Per gestire la lista di osservazione l'interfaccia fornisce due funzioni, la +prima di queste è \funcd{inotify\_add\_watch}, il cui prototipo è: \begin{prototype}{sys/inotify.h} {int inotify\_add\_watch(int fd, const char *pathname, uint32\_t mask)} Aggiunge un evento di osservazione alla lista di osservazione di \param{fd}. - + \bodydesc{La funzione restituisce un valore positivo in caso di successo, o $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} @@ -896,13 +963,13 @@ lista di osservazione l'interfaccia fornisce due funzioni, la prima di queste La funzione consente di creare un \textsl{evento di osservazione} (un cosiddetto ``\textit{watch}'') nella lista di una coda di notifica, indicata specificando il file descriptor ad essa associato nell'argomento \param{fd}. -Il file o la directory da porre sotto osservazione viene invece indicati per -nome, passato nell'argomento \param{pathname}. Infine il terzo argomento, -indica quali eventi devono essere tenuti sotto osservazione; questo deve -essere specificato come maschera binaria combinando i valori delle costanti -riportate in tab.~\ref{tab:inotify_event_watch}; in essa si sono marcati con -un ``$\bullet$'' gli eventi che, quando specificati per una directory, vengono -osservati anche su tutti i file che essa contiene. +Il file o la directory da porre sotto osservazione viene invece indicato per +nome, che viene passato nell'argomento \param{pathname}. Infine il terzo +argomento, \param{mask}, indica che tipo di eventi devono essere tenuti sotto +osservazione. Questo deve essere specificato come maschera binaria combinando +i valori delle costanti riportate in tab.~\ref{tab:inotify_event_watch}. In +essa si sono marcati con un ``$\bullet$'' gli eventi che, quando specificati +per una directory, vengono osservati anche su tutti i file che essa contiene. \begin{table}[htb] \centering @@ -972,7 +1039,6 @@ identificare l'evento a cui si fa riferimento nella lista dei risultati restituiti da \textit{inotify} - \begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{15cm} @@ -984,6 +1050,16 @@ restituiti da \textit{inotify} \end{figure} +Inoltre l'interfaccia di \textit{inotify} permette di conoscere, come avviene +per i file descriptor associati ai socket (si veda al proposito quanto +trattato in sez.~\ref{sec:sock_ioctl_IP}) il numero di byte disponibili in +lettura sul nostro file descriptor, utilizzando su di esso l'operazione +\const{FIONREAD} con \func{ioctl}.\footnote{questa è una delle operazioni + speciali (che abbiamo visto in sez.~\ref{sec:file_ioctl}) che nel caso è + disponibile solo per i socket e per i file descriptor creati con + \func{inotify\_init}.} Questo consente anche di ottenere rapidamente il +numero di file che sono cambiati. + % TODO inserire anche inotify, vedi http://www.linuxjournal.com/article/8478 @@ -992,7 +1068,7 @@ restituiti da \textit{inotify} \index{file!inotify|)} - +% TODO inserire anche eventfd (vedi http://lwn.net/Articles/233462/) @@ -2059,8 +2135,8 @@ mappatura che gi \itindend{memory~mapping} -\subsection{L'I/O diretto fra file descriptor con \func{sendfile}} -\label{sec:file_sendfile} +\subsection{L'I/O diretto fra file descriptor} +\label{sec:file_sendfile_splice} Uno dei problemi @@ -2068,8 +2144,12 @@ NdA \href{http://www.cs.helsinki.fi/linux/linux-kernel/2001-03/0200.html} {\texttt{http://www.cs.helsinki.fi/linux/linux-kernel/2001-03/0200.html}} -% TODO documentare la funzione sendfile +% TODO documentare la funzione sendfile +% TODO documentare le funzioni tee e splice +% http://kerneltrap.org/node/6505 e http://lwn.net/Articles/178199/ e +% http://lwn.net/Articles/179492/ +% e http://en.wikipedia.org/wiki/Splice_(system_call) % i raw device @@ -2972,7 +3052,10 @@ possibilit % LocalWords: flock shared exclusive operation dup inode linked NFS cmd ENOLCK % LocalWords: EDEADLK whence SEEK CUR type pid GETLK SETLK SETLKW all'inode HP % LocalWords: switch bsd lockf mandatory SVr sgid group root mount mand TRUNC -% LocalWords: SVID UX Documentation sendfile dnotify inotify NdA +% LocalWords: SVID UX Documentation sendfile dnotify inotify NdA ppoll fds add +% LocalWords: init EMFILE FIONREAD ioctl watch char pathname uint mask ENOSPC +% LocalWords: dell'inode CLOSE NOWRITE MOVE MOVED FROM TO rm wd event page +% LocalWords: attribute Universe %%% Local Variables: