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
 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
   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}.
 
 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
 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
 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}.
 
   \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
 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
 
 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
 
 % 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.
 
 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}
   
 \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
     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
   \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}
   \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
 
 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}
 
 
 \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
  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}
   \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
 
 \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}
   \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
 
 \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}
   \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}
 
 
 \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}
 
 
 \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}.
 
 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
 
 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.
 
 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
 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:  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: 
 
 
 %%% 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/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 <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 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;
     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 */
     /*
      * 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 
        switch (i) {
        /* 
         * Handling options 
@@ -81,6 +79,12 @@ int main(int argc, char *argv[])
        case 'a':       /* all events */
            mask |= IN_ALL_EVENTS;
            break;
        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();
        case '?':       /* unrecognized options */
            printf("Unrecognized options -%c\n",optopt);
            usage();
@@ -88,6 +92,10 @@ int main(int argc, char *argv[])
            usage();
        }
     }
            usage();
        }
     }
+    if (mask == 0) {
+       printf("No events to monitor\n");
+       usage();
+    }
     /* ***********************************************************
      * 
      *          Options processing completed
     /* ***********************************************************
      * 
      *          Options processing completed
@@ -95,73 +103,49 @@ int main(int argc, char *argv[])
      *               Main code beginning
      * 
      * ***********************************************************/
      *               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();
     }
        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) {
     if (fd < 0) {
-        perror("Failing on inotify_init");
+       perror("Failing on inotify_init");
        exit(-1);
     }
        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) {
     }
     /* 
      * 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);
            }
                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]);
                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);
                    printf("On file %s\n", event->name);
+               }
                printevent(event->mask);
                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");
 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("  -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);
 }
     printf("  -a          watch all\n");
     exit(1);
 }