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
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
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}.
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
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}
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}
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}
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}
\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}
\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}
\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}
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
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
% 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:
--- /dev/null
+/* 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");
+}
#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 */
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
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();
usage();
}
}
+ if (mask == 0) {
+ printf("No events to monitor\n");
+ usage();
+ }
/* ***********************************************************
*
* Options processing completed
* 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;
}
}
}
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);
}