Fatto un programma di esempio per inotify senza tirare in mezzo epoll,
authorSimone Piccardi <piccardi@gnulinux.it>
Sun, 29 Jul 2007 20:52:23 +0000 (20:52 +0000)
committerSimone Piccardi <piccardi@gnulinux.it>
Sun, 29 Jul 2007 20:52:23 +0000 (20:52 +0000)
e trattato l'esempio nella relativa sezione (conclusa).

Alcuni aggiornamenti per readv e writev, e spostati delle sottosezioni
nella relativa sezione.

fileadv.tex
listati/inotify_monitor.c [new file with mode: 0644]
sources/epoll_inotify_monitor.c [new file with mode: 0644]
sources/inotify_monitor.c

index 6c2bfccc4cb177bc1309572285b2c380b871e32b..67887c80e9eebed448e62ec16837a22ba1e8c491 100644 (file)
@@ -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 (file)
index 0000000..310195a
--- /dev/null
@@ -0,0 +1,56 @@
+#include <sys/inotify.h> /* 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 (file)
index 0000000..2a6dbf0
--- /dev/null
@@ -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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/inotify.h> /* Linux inotify interface */
+#include <sys/epoll.h>   /* Linux epoll interface */
+#include <stdlib.h>      /* C standard library */
+#include <unistd.h>      /* Unix standard library */
+#include <errno.h>       /* error definitions and routines */ 
+#include <stdio.h>       /* standard I/O library */
+#include <string.h>      /* string functions */
+#include <fcntl.h>       /* fcntl function */
+#include <sys/ioctl.h>   /* 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");
+}
index e5e04d9aaf369543a59de9093971197fccb5b563..4d46f7e3eaec8ffc2e063300e1e937d6d32a02cf 100644 (file)
@@ -29,7 +29,6 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/inotify.h> /* Linux inotify interface */
-#include <sys/epoll.h>   /* Linux epoll interface */
 #include <stdlib.h>      /* C standard library */
 #include <unistd.h>      /* Unix standard library */
 #include <errno.h>       /* 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);
 }