From 492c6b5ca2c851077e63d3a53c9f735e854e8765 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Sun, 29 Jul 2007 20:52:23 +0000 Subject: [PATCH] Fatto un programma di esempio per inotify senza tirare in mezzo epoll, e trattato l'esempio nella relativa sezione (conclusa). Alcuni aggiornamenti per readv e writev, e spostati delle sottosezioni nella relativa sezione. --- fileadv.tex | 407 ++++++++++++++++++++++---------- listati/inotify_monitor.c | 56 +++++ sources/epoll_inotify_monitor.c | 215 +++++++++++++++++ sources/inotify_monitor.c | 100 ++++---- 4 files changed, 590 insertions(+), 188 deletions(-) create mode 100644 listati/inotify_monitor.c create mode 100644 sources/epoll_inotify_monitor.c diff --git a/fileadv.tex b/fileadv.tex index 6c2bfcc..67887c8 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -1461,7 +1461,7 @@ sottodirectory; se si vogliono osservare anche questi sar ulteriori \textit{watch} per ciascuna sottodirectory. Infine usando il flag \const{IN\_ONESHOT} è possibile richiedere una notifica -singola;\footnote{questa funzionalità però è disponibile soltato a partire dal +singola;\footnote{questa funzionalità però è disponibile soltanto a partire dal kernel 2.6.16.} una volta verificatosi uno qualunque fra gli eventi richiesti con \func{inotify\_add\_watch} l'\textsl{osservatore} verrà automaticamente rimosso dalla lista di osservazione e nessun ulteriore evento @@ -1504,7 +1504,6 @@ viene cancellato o un filesystem viene smontato i relativi osservatori vengono rimossi automaticamente e non è necessario utilizzare \func{inotify\_rm\_watch}. - Come accennato l'interfaccia di \textit{inotify} prevede che gli eventi siano notificati come dati presenti in lettura sul file descriptor associato alla coda di notifica. Una applicazione pertanto dovrà leggere i dati da detto file @@ -1544,7 +1543,7 @@ osservatore maschera di bit che identifica il tipo di evento verificatosi; in essa compariranno sia i bit elencati nella prima parte di tab.~\ref{tab:inotify_event_watch}, che gli eventuali valori -aggiuntivi\footnote{questi compaiono solo nel campo \var{maks} di +aggiuntivi\footnote{questi compaiono solo nel campo \var{mask} di \struct{inotify\_event}, e non utilizzabili in fase di registrazione dell'osservatore.} di tab.~\ref{tab:inotify_read_event_flag}. @@ -1598,15 +1597,137 @@ osservazione, in tal caso essi contengono rispettivamente il nome del file byte. Il campo \var{name} viene sempre restituito come stringa terminata da NUL, con uno o più zeri di terminazione, a seconda di eventuali necessità di allineamento del risultato, ed il valore di \var{len} corrisponde al totale -della dimensione di \var{name}, zeri aggiuntivi compresi. Questo significa che -le dimensioni di ciascun evento di \textit{inotify} saranno pari al valore -\code{sizeof(\struct{inotify\_event}) + len}. +della dimensione di \var{name}, zeri aggiuntivi compresi. La stringa con il +nome del file viene restituita nella lettura subito dopo la struttura +\struct{inotify\_event}; questo significa che le dimensioni di ciascun evento +di \textit{inotify} saranno pari a \code{sizeof(\struct{inotify\_event}) + + len}. Vediamo allora un esempio dell'uso dell'interfaccia di \textit{inotify} con un -semplice programma che permette di mettere sotto osservazione un file o una -directory. +semplice programma che permette di mettere sotto osservazione uno o più file e +directory. Il programma si chiama \texttt{inotify\_monitor.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:inotify_monitor_example}. +\begin{figure}[!htbp] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \includecodesample{listati/inotify_monitor.c} + \end{minipage} + \normalsize + \caption{Esempio di codice che usa l'interfaccia di \textit{inotify}.} + \label{fig:inotify_monitor_example} +\end{figure} + +Una volta completata la scansione delle opzioni il corpo principale del +programma inizia controllando (\texttt{\small 11--15}) che sia rimasto almeno +un argomento che indichi quale file o directory mettere sotto osservazione (e +qualora questo non avvenga esce stampando la pagina di aiuto); dopo di che +passa (\texttt{\small 16--20}) all'inizializzazione di \textit{inotify} +ottenendo con \func{inotify\_init} il relativo file descriptor (oppure usce in +caso di errore). + +Il passo successivo è aggiungere (\texttt{\small 21--30}) alla coda di +notifica gli opportuni osservatori per ciascuno dei file o directory indicati +all'invocazione del comando; questo viene fatto eseguendo un ciclo +(\texttt{\small 22--29}) fintanto che la variabile \var{i}, inizializzata a +zero (\texttt{\small 21}) all'inizio del ciclo, è minore del numero totale di +argomenti rimasti. All'interno del ciclo si invoca (\texttt{\small 23}) +\func{inotify\_add\_watch} per ciascuno degli argomenti, usando la maschera +degli eventi data dalla variabile \var{mask} (il cui valore viene impostato +nella scansione delle opzioni), in caso di errore si esce dal programma +altrimenti si incrementa l'indice (\texttt{\small 29}). + +Completa l'inizializzazione di \textit{inotify} inizia il ciclo principale +(\texttt{\small 32--56}) del programma, nel quale si resta in attesa degli +eventi che si intendono osservare. Questo viene fatto eseguendo all'inizio del +ciclo (\texttt{\small 33}) una \func{read} che si bloccherà fintanto che non +si saranno verificati eventi. + +Dato che l'interfaccia di \textit{inotify} può riportare anche più eventi in +una sola lettura, si è avuto cura di passare alla \func{read} un buffer di +dimensioni adeguate, inizializzato in (\texttt{\small 7}) ad un valore di +approssimativamente 512 eventi.\footnote{si ricordi che la quantità di dati + restituita da \textit{inotify} è variabile a causa della diversa lunghezza + del nome del file restituito insieme a \struct{inotify\_event}.} In caso di +errore di lettura (\texttt{\small 35--40}) il programma esce con un messaggio +di errore (\texttt{\small 37--39}), a meno che non si tratti di una +interruzione della system call, nel qual caso (\texttt{\small 36}) si ripete la +lettura. + +Se la lettura è andata a buon fine invece si esegue un ciclo (\texttt{\small + 43--52}) per leggere tutti gli eventi restituiti, al solito si inizializza +l'indice \var{i} a zero (\texttt{\small 42}) e si ripetono le operazioni +(\texttt{\small 43}) fintanto che esso non supera il numero di byte restituiti +in lettura. Per ciascun evento all'interno del ciclo si assegna\footnote{si + noti come si sia eseguito un opportuno \textit{casting} del puntatore.} alla +variabile \var{event} l'indirizzo nel buffer della corrispondente struttura +\struct{inotify\_event} (\texttt{\small 44}), e poi si stampano il numero di +\textit{watch descriptor} (\texttt{\small 45}) ed il file a cui questo fa +riferimento (\texttt{\small 46}), ricavato dagli argomenti passati a riga di +comando sfruttando il fatto che i \textit{watch descriptor} vengono assegnati +in ordine progressivo crescente a partire da 1. + +Qualora sia presente il riferimento ad un nome di file associato all'evento lo +si stampa (\texttt{\small 47--49}); si noti come in questo caso si sia +utilizzato il valore del campo \var{event->len} e non al fatto che +\var{event->name} riporti o meno un puntatore nullo.\footnote{l'interfaccia + infatti, qualora il nome non sia presente, non avvalora il campo + \var{event->name}, che si troverà a contenere quello che era precedentemente + presente nella rispettiva locazione di memoria, nel caso più comune il + puntatore al nome di un file osservato in precedenza.} Si utilizza poi +(\texttt{\small 50}) la funzione \code{printevent}, che interpreta il valore +del campo \var{event->mask} per stampare il tipo di eventi +accaduti.\footnote{per il relativo codice, che non riportiamo in quanto non + essenziale alla comprensione dell'esempio, si possono utilizzare direttamente + i sorgenti allegati alla guida.} Infine (\texttt{\small 51}) si provvede ad +aggiornare l'indice \var{i} per farlo puntare all'evento successivo. + +Se adesso usiamo il programma per mettere sotto osservazione una directory, e +da un altro terminale eseguiamo il comando \texttt{ls} otterremo qualcosa del +tipo di: +\begin{verbatim} +piccardi@gethen:~/gapil/sources$ ./inotify_monitor -a /home/piccardi/gapil/ +Watch descriptor 1 +Observed event on /home/piccardi/gapil/ +IN_OPEN, +Watch descriptor 1 +Observed event on /home/piccardi/gapil/ +IN_CLOSE_NOWRITE, +\end{verbatim} +I lettori più accorti si saranno resi conto che nel ciclo di lettura degli +eventi appena illustrato non viene trattato il caso particolare in cui la +funzione \func{read} restituisce in \var{nread} un valore nullo. Lo si è fatto +perché con \textit{inotify} il ritorno di una \func{read} con un valore nullo +avviene soltanto, come forma di avviso, quando si sia eseguita la funzione +specificando un buffer di dimensione insufficiente a contenere anche un solo +evento. Nel nostro caso le dimensioni erano senz'altro sufficienti, per cui +tale evenienza non si verificherà mai. + +Ci si potrà però chiedere cosa succede se il buffer è sufficiente per un +evento, ma non per tutti gli eventi verificatisi. Come si potrà notare nel +codice illustrato in precedenza non si è presa nessuna precauzione per +verificare che non ci fossero stati troncamenti dei dati. Anche in questo caso +il comportamento scelto è corretto, perché l'interfaccia di \textit{inotify} +garantisce automaticamente, anche quando ne sono presenti in numero maggiore, +di restituire soltanto il numero di eventi che possono rientrare completamente +nelle dimensioni del buffer specificato.\footnote{si avrà cioè, facendo + riferimento sempre al codice di fig.~\ref{fig:inotify_monitor_example}, che + \var{read} sarà in genere minore delle dimensioni di \var{buffer} ed uguale + soltanto qualora gli eventi corrispondano esattamente alle dimensioni di + quest'ultimo.} Se gli eventi sono di più ne saranno restituiti solo quelli +che entrano interamente nel buffer e gli altri saranno restituiti alla +successiva chiamata di \func{read}. + +Infine un'ultima caratteristica dell'interfaccia di \textit{inotify} è che gli +eventi restituiti nella lettura formano una sequenza ordinata, è cioè +garantito che se si esegue uno spostamento di un file gli eventi vengano +generati nella sequenza corretta. L'interfaccia garantisce anche che se si +verificano più eventi consecutivi identici (vale a dire con gli stessi valori +dei campi \var{wd}, \var{mask}, \var{cookie}, e \var{name}) questi vengono +raggruppati in un solo evento. % TODO inserire anche inotify, vedi http://www.linuxjournal.com/article/8478 % TODO e man inotify @@ -1999,14 +2120,12 @@ l'operazione sia facilmente eseguibile attraverso una serie multipla di chiamate, ci sono casi in cui si vuole poter contare sulla atomicità delle operazioni. -Per questo motivo BSD 4.2\footnote{Le due funzioni sono riprese da BSD4.4 ed - integrate anche dallo standard Unix 98. Fino alle libc5, Linux usava - \type{size\_t} come tipo dell'argomento \param{count}, una scelta logica, - che però è stata dismessa per restare aderenti allo standard.} ha introdotto -due nuove system call, \funcd{readv} e \funcd{writev}, che permettono di -effettuare con una sola chiamata una lettura o una scrittura su una serie di -buffer (quello che viene chiamato \textsl{I/O vettorizzato}. I relativi -prototipi sono: +Per questo motivo su BSD 4.2 sono state introdotte due nuove system call, +\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.} che +permettono di effettuare con una sola chiamata una lettura o una scrittura su +una serie di buffer (quello che viene chiamato \textsl{I/O vettorizzato}. I +relativi prototipi sono: \begin{functions} \headdecl{sys/uio.h} @@ -2020,11 +2139,11 @@ prototipi sono: assumerà uno dei valori: \begin{errlist} \item[\errcode{EINVAL}] si è specificato un valore non valido per uno degli - argomenti (ad esempio \param{count} è maggiore di \const{MAX\_IOVEC}). + 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. + 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} @@ -2052,10 +2171,132 @@ essere letti o scritti ed in che quantit La lista dei buffer da utilizzare viene indicata attraverso l'argomento \param{vector} che è un vettore di strutture \struct{iovec}, la cui lunghezza -è specificata dall'argomento \param{count}. Ciascuna struttura dovrà essere -inizializzata opportunamente per indicare i vari buffer da e verso i quali -verrà eseguito il trasferimento dei dati. Essi verranno letti (o scritti) -nell'ordine in cui li si sono specificati nel vettore \param{vector}. +è specificata dall'argomento \param{count}.\footnote{fino alle libc5, Linux + usava \type{size\_t} come tipo dell'argomento \param{count}, una scelta + logica, che però è stata dismessa per restare aderenti allo standard + POSIX.1-2001.} Ciascuna struttura dovrà essere inizializzata opportunamente +per indicare i vari buffer da e verso i quali verrà eseguito il trasferimento +dei dati. Essi verranno letti (o scritti) nell'ordine in cui li si sono +specificati nel vettore \param{vector}. + +La standardizzazione delle due funzioni all'interno della revisione +POSIX.1-2001 prevede anche che sia possibile avere un limite al numero di +elementi del vettore \param{vector}. Qualora questo sussista, esso deve essere +indicato dal valore dalla costante \const{IOV\_MAX}, definita come le altre +costanti analoghe (vedi sez.~\ref{sec:sys_limits}) in \file{limits.h}; lo +stesso valore deve essere ottenibile in esecuzione tramite la funzione +\func{sysconf} richiedendo l'argomento \const{\_SC\_IOV\_MAX} (vedi +sez.~\ref{sec:sys_sysconf}). + +Nel caso di Linux il limite di sistema è di 1024, però se si usano le +\acr{glibc} queste forniscono un \textit{wrapper} per le system call che si +accorge se una operazione supererà il precedente limite, in tal caso i dati +verranno letti o scritti con le usuali \func{read} e \func{write} usando un +buffer di dimensioni sufficienti appositamente allocato e sufficiente a +contenere tutti i dati indicati da \param{vector}. L'operazione avrà successo +ma si perderà l'atomicità del trasferimento da e verso la destinazione finale. + + +% TODO verificare cosa succederà a preadv e pwritev o alla nuova niovec +% vedi http://lwn.net/Articles/164887/ + + +\subsection{L'I/O diretto fra file descriptor} +\label{sec:file_sendfile_splice} + + +Uno dei problemi che si presenta nella gestione dell'I/O è quello in cui si +devono trasferire grandi quantità di dati da un file descriptor ed un altro; +questo usualmente comporta la lettura dei dati dal primo file descriptor in un +buffer in memoria, da cui essi vengono poi scritti sul secondo. + +Benché il kernel ottimizzi la gestione di questo processo quando si ha a che +fare con file normali, in generale quando i dati da trasferire sono molti si +pone il problema di effettuare trasferimenti di grandi quantità di dati da +kernel space a user space e all'indietro, quando in realtà sarebbe molto più +efficiente tenere tutto in kernel space. Tratteremo in questa sezione alcune +funzioni specialistiche che permettono di ottimizzare le prestazioni in questo +tipo di situazioni. + +La prima funzione che si pone l'obiettivo di ottimizzare il trasferimento dei +dati fra due file descriptor è \funcd{sendfile}; 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, +per cui vengono utilizzati diversi prototipi e semantiche +differenti;\footnote{pertanto si eviti di utilizzarla se si devono scrivere + programmi portabili.} nel caso di Linux il suo prototipo è: +\begin{functions} + \headdecl{sys/sendfile.h} + + \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 0 in caso di successo e $-1$ in caso di + 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 + o una operazione di \func{mmap} non è disponibile per \param{in\_fd}. + \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} + + +%NdA è da finire, sul perché non è abilitata fra file vedi: +%\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 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) +% e http://kerneltrap.org/node/6505 + + + + +\subsection{Gestione avanzata dell'accesso ai dati dei file} +\label{sec:file_fadvise} + +Nell'uso generico dell'interfaccia per l'accesso al contenuto dei file le +operazioni di lettura e scrittura non necessitano di nessun intervento di +supervisione da parte dei programmi, si eseguirà una \func{read} o una +\func{write}, i dati verranno passati al kernel che provvederà ad effettuare +tutte le operazioni (e a gestire il \textit{caching} dei dati) per portarle a +termine in quello che ritiene essere il modo più efficiente. + +Il problema è che il concetto di migliore efficienza impiegato dal kernel è +relativo all'uso generico, mentre esistono molti casi in cui ci sono esigenze +specifiche dei singoli programmi, che avendo una conoscenza diretta di come +verranno usati i file, possono necessitare di effettuare delle ottimizzazioni +specifiche, relative alle proprie modalità di I/O sugli stessi. Tratteremo in +questa sezione una serie funzioni che consentono ai programmi di ottimizzare +il loro accesso ai dati dei file. + + +% TODO documentare \func{madvise} +% TODO documentare \func{mincore} +% TODO documentare \func{posix\_fadvise} +% vedi http://insights.oetiker.ch/linux/fadvise.html +% questo tread? http://www.ussg.iu.edu/hypermail/linux/kernel/0703.1/0032.html +% TODO documentare \func{fallocate}, introdotta con il 2.6.23 +% vedi http://lwn.net/Articles/226710/ e http://lwn.net/Articles/240571/ + + +%\subsection{L'utilizzo delle porte di I/O} +%\label{sec:file_io_port} +% +% TODO l'I/O sulle porte di I/O +% consultare le manpage di ioperm, iopl e outb \subsection{File mappati in memoria} @@ -2071,7 +2312,7 @@ file in una sezione dello spazio di indirizzi del processo. che lo ha allocato \begin{figure}[htb] \centering - \includegraphics[width=10cm]{img/mmap_layout} + \includegraphics[width=12cm]{img/mmap_layout} \caption{Disposizione della memoria di un processo quando si esegue la mappatura in memoria di un file.} \label{fig:file_mmap_layout} @@ -2308,7 +2549,7 @@ effettive del file o della sezione che si vuole mappare. \begin{figure}[!htb] \centering - \includegraphics[width=12cm]{img/mmap_boundary} + \includegraphics[width=13cm]{img/mmap_boundary} \caption{Schema della mappatura in memoria di una sezione di file di dimensioni non corrispondenti al bordo di una pagina.} \label{fig:file_mmap_boundary} @@ -2351,7 +2592,7 @@ che sono utilizzabili solo con questa interfaccia. \begin{figure}[htb] \centering - \includegraphics[width=12cm]{img/mmap_exceed} + \includegraphics[width=13cm]{img/mmap_exceed} \caption{Schema della mappatura in memoria di file di dimensioni inferiori alla lunghezza richiesta.} \label{fig:file_mmap_exceed} @@ -2682,104 +2923,6 @@ mappatura che gi \itindend{memory~mapping} -\subsection{L'I/O diretto fra file descriptor} -\label{sec:file_sendfile_splice} - - -Uno dei problemi che si presenta nella gestione dell'I/O è quello in cui si -devono trasferire grandi quantità di dati da un file descriptor ed un altro; -questo usualmente comporta la lettura dei dati dal primo file descriptor in un -buffer in memoria, da cui essi vengono poi scritti sul secondo. - -Benché il kernel ottimizzi la gestione di questo processo quando si ha a che -fare con file normali, in generale quando i dati da trasferire sono molti si -pone il problema di effettuare trasferimenti di grandi quantità di dati da -kernel space a user space e all'indietro, quando in realtà sarebbe molto più -efficiente tenere tutto in kernel space. Tratteremo in questa sezione alcune -funzioni specialistiche che permettono di ottimizzare le prestazioni in questo -tipo di situazioni. - -La prima funzione che si pone l'obiettivo di ottimizzare il trasferimento dei -dati fra due file descriptor è \funcd{sendfile}; 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, -per cui vengono utilizzati diversi prototipi e semantiche -differenti;\footnote{pertanto si eviti di utilizzarla se si devono scrivere - programmi portabili.} nel caso di Linux il suo prototipo è: -\begin{functions} - \headdecl{sys/sendfile.h} - - \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 0 in caso di successo e $-1$ in caso di - 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 - o una operazione di \func{mmap} non è disponibile per \param{in\_fd}. - \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} - - -%NdA è da finire, sul perché non è abilitata fra file vedi: -%\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 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) -% e http://kerneltrap.org/node/6505 - - - - -\subsection{Gestione avanzata dell'accesso ai dati dei file} -\label{sec:file_fadvise} - -Nell'uso generico dell'interfaccia per l'accesso al contenuto dei file le -operazioni di lettura e scrittura non necessitano di nessun intervento di -supervisione da parte dei programmi, si eseguirà una \func{read} o una -\func{write}, i dati verranno passati al kernel che provvederà ad effettuare -tutte le operazioni (e a gestire il \textit{caching} dei dati) per portarle a -termine in quello che ritiene essere il modo più efficiente. - -Il problema è che il concetto di migliore efficienza impiegato dal kernel è -relativo all'uso generico, mentre esistono molti casi in cui ci sono esigenze -specifiche dei singoli programmi, che avendo una conoscenza diretta di come -verranno usati i file, possono necessitare di effettuare delle ottimizzazioni -specifiche, relative alle proprie modalità di I/O sugli stessi. Tratteremo in -questa sezione una serie funzioni che consentono ai programmi di ottimizzare -il loro accesso ai dati dei file. - - -% TODO documentare \func{madvise} -% TODO documentare \func{mincore} -% TODO documentare \func{posix\_fadvise} -% vedi http://insights.oetiker.ch/linux/fadvise.html -% questo tread? http://www.ussg.iu.edu/hypermail/linux/kernel/0703.1/0032.html -% TODO documentare \func{fallocate}, introdotta con il 2.6.23 -% vedi http://lwn.net/Articles/226710/ e http://lwn.net/Articles/240571/ - - -%\subsection{L'utilizzo delle porte di I/O} -%\label{sec:file_io_port} -% -% TODO l'I/O sulle porte di I/O -% consultare le manpage di ioperm, iopl e outb - - \section{Il file locking} @@ -3196,13 +3339,6 @@ ed impedirle restituendo un errore di \errcode{EDEADLK} alla funzione che cerca di acquisire un lock che porterebbe ad un \itindex{deadlock} \textit{deadlock}. -\begin{figure}[!bht] - \centering \includegraphics[width=13cm]{img/file_posix_lock} - \caption{Schema dell'architettura del file locking, nel caso particolare - del suo utilizzo secondo l'interfaccia standard POSIX.} - \label{fig:file_posix_lock} -\end{figure} - Per capire meglio il funzionamento del file locking in semantica POSIX (che differisce alquanto rispetto da quello di BSD, visto @@ -3221,6 +3357,13 @@ questo caso la titolarit voce nella \itindex{file~table} \textit{file table}, ma con il valore del \acr{pid} del processo. +\begin{figure}[!bht] + \centering \includegraphics[width=13cm]{img/file_posix_lock} + \caption{Schema dell'architettura del file locking, nel caso particolare + del suo utilizzo secondo l'interfaccia standard POSIX.} + \label{fig:file_posix_lock} +\end{figure} + Quando si richiede un lock il kernel effettua una scansione di tutti i lock presenti sul file\footnote{scandisce cioè la \itindex{linked~list} \textit{linked list} delle strutture \struct{file\_lock}, scartando @@ -3673,7 +3816,9 @@ possibilit % LocalWords: Lemon BSDCON edge Libenzi kevent backporting epfd EEXIST ENOENT % LocalWords: MOD wait EPOLLIN EPOLLOUT EPOLLRDHUP SOCK EPOLLPRI EPOLLERR one % LocalWords: EPOLLHUP EPOLLET EPOLLONESHOT shot maxevents ctlv ALL DONT HPUX -% LocalWords: FOLLOW ONESHOT ONLYDIR FreeBSD EIO caching +% LocalWords: FOLLOW ONESHOT ONLYDIR FreeBSD EIO caching sysctl instances name +% LocalWords: watches IGNORED ISDIR OVERFLOW overflow UNMOUNT queued cookie ls +% LocalWords: NUL sizeof casting printevent nread %%% Local Variables: diff --git a/listati/inotify_monitor.c b/listati/inotify_monitor.c new file mode 100644 index 0000000..310195a --- /dev/null +++ b/listati/inotify_monitor.c @@ -0,0 +1,56 @@ +#include /* Linux inotify interface */ +... +int main(int argc, char *argv[]) +{ + int i, narg, nread; + int fd, wd; + char buffer[512 * (sizeof(struct inotify_event) + 16)]; + unsigned int mask=0; + struct inotify_event * event; + ... + narg = argc - optind; + if (narg < 1) { /* There must be at least one argument */ + printf("Wrong number of arguments %d\n", argc - optind); + usage(); + } + fd = inotify_init(); /* initialize inotify */ + if (fd < 0) { + perror("Failing on inotify_init"); + exit(-1); + } + i = 0; + while (i < narg) { + wd = inotify_add_watch(fd, argv[optind+i], mask); /* add watch */ + if (wd <= 0) { + printf("Failing to add watched file %s, mask %i; %s\n", + argv[optind+i], mask, strerror(errno)); + exit(-1); + } + i++; + } + /* Main Loop: read events and print them */ + while (1) { + nread = read(fd, buffer, sizeof(buffer)); + if (nread < 0) { + if (errno == EINTR) { + continue; + } else { + perror("error reading inotify data"); + exit(1); + } + } else { + i = 0; + while (i < nread) { + event = (struct inotify_event *) buffer + i; + printf("Watch descriptor %i\n", event->wd); + printf("Observed event on %s\n", argv[optind-1+event->wd]); + if (event->len) { + printf("On file %s\n", event->name); + } + printevent(event->mask); + i += sizeof(struct inotify_event) + event->len; + } + } + } + return 0; +} diff --git a/sources/epoll_inotify_monitor.c b/sources/epoll_inotify_monitor.c new file mode 100644 index 0000000..2a6dbf0 --- /dev/null +++ b/sources/epoll_inotify_monitor.c @@ -0,0 +1,215 @@ +/* epoll_inotify_monitor.c + * + * Copyright (C) 2007 Simone Piccardi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/***************************************************************************** + * + * File epoll_inotify_monitor.c: + * + * An example of the inotify and epoll interface: use inotify to watch the + * status of a directory or a file and epoll to wait for inotify events. + * + * Author: S. Piccardi Jul. 2007 + * + *****************************************************************************/ +#include +#include +#include /* Linux inotify interface */ +#include /* Linux epoll interface */ +#include /* C standard library */ +#include /* Unix standard library */ +#include /* error definitions and routines */ +#include /* standard I/O library */ +#include /* string functions */ +#include /* fcntl function */ +#include /* ioctl function */ + + +#include "macros.h" + +/* Help printing routine */ +void usage(void); +void printevent(unsigned int mask); + +int main(int argc, char *argv[]) +{ + int i, size, nread; + int fd, wd, epfd; + char buffer[1024 + sizeof(struct inotify_event)]; + unsigned int mask=0; + struct inotify_event * event; + struct epoll_event epev; + /* + * Input section: decode command line parameters + * Use getopt function + */ + opterr = 0; /* don't want writing to stderr */ + while ((i = getopt(argc, argv, "hrwcda")) != -1) { + switch (i) { + /* + * Handling options + */ + case 'h': /* help option */ + printf("Wrong -h option use\n"); + usage(); + case 'r': /* read access */ + mask |= IN_ACCESS; + break; + case 'w': /* write access */ + mask |= IN_MODIFY; + break; + case 'c': /* creation */ + mask |= IN_CREATE; + break; + case 'd': /* deletion */ + mask |= IN_DELETE; + break; + case 'a': /* all events */ + mask |= IN_ALL_EVENTS; + break; + case '?': /* unrecognized options */ + printf("Unrecognized options -%c\n",optopt); + usage(); + default: /* should not reached */ + usage(); + } + } + /* *********************************************************** + * + * Options processing completed + * + * Main code beginning + * + * ***********************************************************/ + if ((argc - optind) != 1) { /* There must be one argument */ + printf("Wrong number of arguments %d\n", argc - optind); + usage(); + } + epfd = epoll_create(5); /* initialize epoll */ + if (epfd < 0) { + perror("Failing on epoll_create"); + exit(-1); + } + fd = inotify_init(); /* initialize inotify */ + if (fd < 0) { + perror("Failing on inotify_init"); + exit(-1); + } + if (fcntl(fd, F_SETFL, O_NONBLOCK)) { /* no blocking I/O on inotify */ + perror("Cannot set noblocking I/O on inotify fd"); + exit(-1); + } + wd = inotify_add_watch(fd, argv[optind], mask); /* add watch */ + if (wd <= 0) { + printf("Failing to add watched file %s, mask %i; %s\n", + argv[optind], mask, strerror(errno)); + exit(-1); + } + epev.data.fd = fd; /* add inotify fd to epoll */ + epev.events = EPOLLIN; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &epev)) { + perror("Failing on epoll_ctl"); + exit(-1); + } + /* + * Main Loop: read events and print them + */ + while (1) { + if (epoll_wait(epfd, &epev, 1, -1) < 0) { + perror("error on epoll_wait"); + exit(-1); + } + if (epev.data.fd != fd) + printf("something wrong, epoll activity on %i instead of %i\n", + epev.data.fd, fd); + + if (ioctl(fd, FIONREAD, &size)) { + perror("error on getting inotify event size"); + exit(-1); + } + if (size > sizeof(buffer)) { + printf("Too many %i data to read, something wrong\n", size); + exit(-1); + } + i = 0; + while (i < size) { + nread = read(fd, buffer, size); + if (nread < 0) { + perror("error reading inotify data"); + exit(1); + } + i += nread; + event = (struct inotify_event *) buffer; + if (wd != event->wd) { + printf("Getting different watch descriptor, %i and %i\n", + wd, event->wd); + } else { + printf("Observed event on %s\n", argv[optind-1+event->wd]); + if (event->name != NULL) + printf("On file %s\n", event->name); + printevent(event->mask); + } + } + } + return 0; +} +/* + * routine to print usage info and exit + */ +void usage(void) { + printf("Program inotify_monitor: monitor file changes \n"); + printf("Usage:\n"); + printf(" inotify_monitor [-h] -rwcd dirname/filename dir/file ... \n"); + printf(" -h print this help\n"); + printf(" -w watch write\n"); + printf(" -r watch read\n"); + printf(" -c watch create\n"); + printf(" -d watch delete\n"); + printf(" -a watch all\n"); + exit(1); +} + +void printevent(unsigned int mask) { + int i; + int val; + char * inotify_event[] = { + "IN_ACCESS", + "IN_MODIFY", + "IN_ATTRIB", + "IN_CLOSE_WRITE", + "IN_CLOSE_NOWRITE", + "IN_OPEN", + "IN_MOVED_FROM", + "IN_MOVED_TO", + "IN_CREATE", + "IN_DELETE", + "IN_DELETE_SELF", + "IN_MOVE_SELF", + "ERROR!!!", + "IN_UNMOUNT", + "IN_Q_OVERFLOW", + "IN_IGNORED" + }; + + val=1; + for (i=0; i<16; i++) { + if (mask & val) + printf("%s, ", inotify_event[i]); + val = val << 1; + } + printf("\n"); +} diff --git a/sources/inotify_monitor.c b/sources/inotify_monitor.c index e5e04d9..4d46f7e 100644 --- a/sources/inotify_monitor.c +++ b/sources/inotify_monitor.c @@ -29,7 +29,6 @@ #include #include #include /* Linux inotify interface */ -#include /* Linux epoll interface */ #include /* C standard library */ #include /* Unix standard library */ #include /* error definitions and routines */ @@ -47,18 +46,17 @@ void printevent(unsigned int mask); int main(int argc, char *argv[]) { - int i, size, nread; - int fd, wd, epfd; - char buffer[1024 + sizeof(struct inotify_event)]; + int i, narg, nread; + int fd, wd; + char buffer[512 * (sizeof(struct inotify_event) + 16)]; unsigned int mask=0; struct inotify_event * event; - struct epoll_event epev; /* * Input section: decode command line parameters * Use getopt function */ opterr = 0; /* don't want writing to stderr */ - while ((i = getopt(argc, argv, "hrwcda")) != -1) { + while ((i = getopt(argc, argv, "hrwcdaCM")) != -1) { switch (i) { /* * Handling options @@ -81,6 +79,12 @@ int main(int argc, char *argv[]) case 'a': /* all events */ mask |= IN_ALL_EVENTS; break; + case 'C': /* creation */ + mask |= IN_CLOSE; + break; + case 'M': /* creation */ + mask |= IN_MOVE; + break; case '?': /* unrecognized options */ printf("Unrecognized options -%c\n",optopt); usage(); @@ -88,6 +92,10 @@ int main(int argc, char *argv[]) usage(); } } + if (mask == 0) { + printf("No events to monitor\n"); + usage(); + } /* *********************************************************** * * Options processing completed @@ -95,73 +103,49 @@ int main(int argc, char *argv[]) * Main code beginning * * ***********************************************************/ - if ((argc - optind) != 1) { /* There must be one argument */ + narg = argc - optind; + if (narg < 1) { /* There must be at least one argument */ printf("Wrong number of arguments %d\n", argc - optind); usage(); } - epfd = epoll_create(5); /* initialize epoll */ - if (epfd < 0) { - perror("Failing on epoll_create"); - exit(-1); - } - fd = inotify_init(); /* initialize inotify */ + fd = inotify_init(); /* initialize inotify */ if (fd < 0) { - perror("Failing on inotify_init"); + perror("Failing on inotify_init"); exit(-1); } - if (fcntl(fd, F_SETFL, O_NONBLOCK)) { /* no blocking I/O on inotify */ - perror("Cannot set noblocking I/O on inotify fd"); - exit(-1); - } - wd = inotify_add_watch(fd, argv[optind], mask); /* add watch */ - if (wd <= 0) { - printf("Failing to add watched file %s, mask %i; %s\n", - argv[optind], mask, strerror(errno)); - exit(-1); - } - epev.data.fd = fd; /* add inotify fd to epoll */ - epev.events = EPOLLIN; - if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &epev)) { - perror("Failing on epoll_ctl"); - exit(-1); + i = 0; + while (i < narg) { + wd = inotify_add_watch(fd, argv[optind+i], mask); /* add watch */ + if (wd <= 0) { + printf("Failing to add watched file %s, mask %i; %s\n", + argv[optind+i], mask, strerror(errno)); + exit(-1); + } + i++; } /* * Main Loop: read events and print them */ while (1) { - if (epoll_wait(epfd, &epev, 1, -1) < 0) { - perror("error on epoll_wait"); - exit(-1); - } - if (epev.data.fd != fd) - printf("something wrong, epoll activity on %i instead of %i\n", - epev.data.fd, fd); - - if (ioctl(fd, FIONREAD, &size)) { - perror("error on getting inotify event size"); - exit(-1); - } - if (size > sizeof(buffer)) { - printf("Too many %i data to read, something wrong\n", size); - exit(-1); - } - i = 0; - while (i < size) { - nread = read(fd, buffer, size); - if (nread < 0) { + nread = read(fd, buffer, sizeof(buffer)); + if (nread < 0) { + if (errno == EINTR) { + continue; + } else { perror("error reading inotify data"); exit(1); } - i += nread; - event = (struct inotify_event *) buffer; - if (wd != event->wd) { - printf("Getting different watch descriptor, %i and %i\n", - wd, event->wd); - } else { + } else { + i = 0; + while (i < nread) { + event = (struct inotify_event *) buffer + i; + printf("Watch descriptor %i\n", event->wd); printf("Observed event on %s\n", argv[optind-1+event->wd]); - if (event->name != NULL) + if (event->len) { printf("On file %s\n", event->name); + } printevent(event->mask); + i += sizeof(struct inotify_event) + event->len; } } } @@ -173,12 +157,14 @@ int main(int argc, char *argv[]) void usage(void) { printf("Program inotify_monitor: monitor file changes \n"); printf("Usage:\n"); - printf(" inotify_monitor [-h] -rwcd dirname/filename dir/file ... \n"); + printf(" inotify_monitor [-h] -rwcdCMa dirname/filename dir/file ... \n"); printf(" -h print this help\n"); printf(" -w watch write\n"); printf(" -r watch read\n"); printf(" -c watch create\n"); printf(" -d watch delete\n"); + printf(" -C watch closing\n"); + printf(" -M watch moving\n"); printf(" -a watch all\n"); exit(1); } -- 2.30.2