%% license is included in the section entitled "GNU Free Documentation
%% License".
%%
+
\chapter{La gestione avanzata dei file}
\label{cha:file_advanced}
In questo capitolo affronteremo le tematiche relative alla gestione avanzata
\end{table}
I primi due valori, \const{LOCK\_SH} e \const{LOCK\_EX} permettono di
-richiedere un \textit{file lock}, ed ovviamente devono essere usati in maniera
-alternativa. Se si specifica anche \const{LOCK\_NB} la funzione non si
-bloccherà qualora il \textit{file lock} non possa essere acquisito, ma
-ritornerà subito con un errore di \errcode{EWOULDBLOCK}. Per rilasciare un
-\textit{file lock} si dovrà invece usare \const{LOCK\_UN}.
+richiedere un \textit{file lock} rispettivamente condiviso o esclusivo, ed
+ovviamente non possono essere usati insieme. Se con essi si specifica anche
+\const{LOCK\_NB} la funzione non si bloccherà qualora il \textit{file lock}
+non possa essere acquisito, ma ritornerà subito con un errore di
+\errcode{EWOULDBLOCK}. Per rilasciare un \textit{file lock} si dovrà invece
+usare direttamente const{LOCK\_UN}.
Si tenga presente che non esiste una modalità per eseguire atomicamente un
cambiamento del tipo di blocco (da \textit{shared lock} a \textit{esclusive
\acr{pid} del processo.
\begin{figure}[!bht]
- \centering \includegraphics[width=13cm]{img/file_posix_lock}
+ \centering \includegraphics[width=12cm]{img/file_posix_lock}
\caption{Schema dell'architettura del \textit{file locking}, nel caso
particolare del suo utilizzo secondo l'interfaccia standard POSIX.}
\label{fig:file_posix_lock}
permette di bloccare una sezione di un file usando la semantica POSIX, o un
intero file usando la semantica BSD; in fig.~\ref{fig:file_flock_code} è
riportata il corpo principale del codice del programma, (il testo completo è
-allegato nella directory dei sorgenti).
+allegato nella directory dei sorgenti, nel file \texttt{Flock.c}).
La sezione relativa alla gestione delle opzioni al solito si è omessa, come la
funzione che stampa le istruzioni per l'uso del programma, essa si cura di
sotto controllo. L'argomento viene ignorato con l'operazione
\const{EPOLL\_CTL\_DEL}.\footnote{fino al kernel 2.6.9 era comunque richiesto
che questo fosse un puntatore valido, anche se poi veniva ignorato; a
- partire dal 2.6.9 si può specificare anche un valore \texttt{NULL} ma se si
+ partire dal 2.6.9 si può specificare anche un valore \val{NULL} ma se si
vuole mantenere la compatibilità con le versioni precedenti occorre usare un
puntatore valido.}
rispettive funzioni consente di fare contemporaneamente entrambe le cose.
Per risolvere questo problema nello sviluppo del kernel si è pensato di
-introdurre un meccanismo alternativo alla notifica dei segnali (esteso anche
+introdurre un meccanismo alternativo per la notifica dei segnali (esteso anche
ad altri eventi generici) che, ispirandosi di nuovo alla filosofia di Unix per
cui tutto è un file, consentisse di eseguire la notifica con l'uso di
opportuni file descriptor.\footnote{ovviamente si tratta di una funzionalità
che per un bug i kernel fino al 2.6.25 non avvalorano correttamente i campi
\var{ssi\_ptr} e \var{ssi\_int} per segnali inviati con \func{sigqueue}.}
+Come esempio di questa nuova interfaccia ed anche come esempio di applicazione
+della interfaccia di \itindex{epoll} \textit{epoll}, si è scritto un programma
+elementare che stampi sullo standard output sia quanto viene scritto da terzi
+su una \textit{named fifo}, che l'avvenuta ricezione di alcuni segnali. Il
+codice completo si trova al solito nei sorgenti allegati alla guida (nel file
+\texttt{FifoReporter.c}).
+
+In fig.~\ref{fig:fiforeporter_code_init} si è riportata la parte iniziale del
+programma in cui vengono effettuate le varie inizializzazioni necessarie per
+l'uso di \itindex{epoll} \textit{epoll} e \func{signalfd}, a partire
+(\texttt{\small 12--16}) dalla definizione delle varie variabili e strutture
+necessarie. Al solito si è tralasciata la parte dedicata alla decodifica delle
+opzioni che consentono ad esempio di cambiare il nome del file associato alla
+fifo.
+
+\begin{figure}[!htb]
+ \footnotesize \centering
+ \begin{minipage}[c]{15cm}
+ \includecodesample{listati/FifoReporter-init.c}
+ \end{minipage}
+ \normalsize
+ \caption{Sezione di inizializzazione del codice del programma
+ \file{FifoReporter.c}.}
+ \label{fig:fiforeporter_code_init}
+\end{figure}
+
+Il primo passo (\texttt{\small 19--20}) è la crezione di un file descriptor
+\texttt{epfd} di \itindex{epoll} \textit{epoll} con \func{epoll\_create} che è
+quello che useremo per il controllo degli altri. É poi necessario
+disabilitare la ricezione dei segnali (nel caso \const{SIGINT},
+\const{SIGQUIT} e \const{SIGTERM}) per i quali si vuole la notifica tramite
+file descriptor. Per questo prima li si inseriscono (\texttt{\small 22--25}) in
+una maschera di segnali \texttt{sigmask} che useremo con (\texttt{\small 26})
+\func{sigprocmask} per disabilitarli. Con la stessa maschera si potrà per
+passare all'uso (\texttt{\small 28--29}) di \func{signalfd} per abilitare la
+notifica sul file descriptor \var{sigfd}. Questo poi (\texttt{\small 30--33})
+dovrà essere aggiunto con \func{epoll\_ctl} all'elenco di file descriptor
+controllati con \texttt{epfd}.
+
+Occorrerà infine (\texttt{\small 35--38}) creare la \textit{named fifo} se
+questa non esiste ed aprirla per la lettura (\texttt{\small 39--40}); una
+volta fatto questo sarà necessario aggiungere il relativo file descriptor
+(\var{fifofd}) a quelli osservati da \itindex{epoll} \textit{epoll} in maniera
+del tutto analoga a quanto fatto con quello relativo alla notifica dei
+segnali.
+
+\begin{figure}[!htb]
+ \footnotesize \centering
+ \begin{minipage}[c]{15cm}
+ \includecodesample{listati/FifoReporter-main.c}
+ \end{minipage}
+ \normalsize
+ \caption{Ciclo principale del codice del programma \file{FifoReporter.c}.}
+ \label{fig:fiforeporter_code_body}
+\end{figure}
+
+Una volta completata l'inizializzazione verrà eseguito indefinitamente il
+ciclo principale del programma (\texttt{\small 2--45}) che si è riportato in
+fig.~\ref{fig:fiforeporter_code_body}, fintanto che questo non riceva un
+segnale di \texttt{SIGINT} (ad esempio con la pressione di \texttt{C-c}). Il
+ciclo prevede che si attenda (\texttt{\small 2--3}) la presenza di un file
+descriptor pronto in lettura con \func{epoll\_wait},\footnote{si ricordi che
+ entrambi i file descriptor \var{fifofd} e \var{sigfd} sono stati posti in
+ osservazioni per eventi di tipo \const{EPOLLIN}.} che si bloccherà fintanto
+che non siano stati scritti dati sulla fifo o che non sia arrivato un
+segnale.\footnote{per semplificare il codice non si è trattato il caso in cui
+ \func{epoll\_wait} viene interrotta da un segnale, assumendo che tutti
+ quelli che possano interessare siano stati predisposti per la notifica
+ tramite file descriptor, per gli altri si otterrà semplicemente l'uscita dal
+ programma.}
+
+Anche se in questo caso i file descriptor pronti possono essere al più due, si
+è comunque adottato un approccio generico in cui questi verranno letti
+all'interno di un opportuno ciclo (\texttt{\small 5--44}) sul numero
+restituito da \func{epoll\_wait}, esaminando i risultati presenti nel vettore
+\var{events} all'interno di una catena di condizionali alternativi sul valore
+del file descriptor riconosciuto come pronto.\footnote{controllando cioè a
+ quale dei due file descriptor possibili corrisponde il campo relativo,
+ \var{events[i].data.fd}.}
+
+Il primo condizionale (\texttt{\small 6--24}) è relativo al caso che si sia
+ricevuto un segnale e che il file descriptor pronto corrisponda
+(\texttt{\small 6}) a \var{sigfd}. Dato che in generale si possono ricevere
+anche notifiche relativi a più di un singolo segnale, si è scelto di leggere
+una struttura \const{signalfd\_siginfo} alla volta, eseguendo la lettura
+all'interno di un ciclo (\texttt{\small 8--24}) che prosegue fintanto che vi
+siano dati da leggere.
+
+Per questo ad ogni lettura si esamina (\texttt{\small 9--14}) se il valore di
+ritorno della funzione \func{read} è negativo, uscendo dal programma
+(\texttt{\small 11}) in caso di errore reale, o terminando il ciclo
+(\texttt{\small 13}) con un \texttt{break} qualora si ottenga un errore di
+\errcode{EAGAIN} per via dell'esaurimento dei dati.\footnote{si ricordi come
+ sia la fifo che il file descriptor per i segnali siano stati aperti in
+ modalità non-bloccante, come previsto per l’\textit{I/O multiplexing},
+ pertanto ci si aspetta di ricevere un errore di \errcode{EAGAIN} quando non
+ vi saranno più dati da leggere.}
+
+In presenza di dati invece il programma proseguirà l'esecuzione stampando
+(\texttt{\small 19--20}) il nome del segnale ottenuto all'interno della
+struttura \const{signalfd\_siginfo} letta in \var{siginf}\footnote{per la
+ stampa si è usato il vettore \var{sig\_names} a ciascun elemento del quale
+ corrisponde il nome del segnale avente il numero corrispondente, la cui
+ definizione si è omessa dal codice di fig.~\ref{fig:fiforeporter_code_init}
+ per brevità.} ed il \textit{pid} del processo da cui lo ha ricevuto; inoltre
+(\texttt{\small 21--24}) si controllerà anche se il segnale ricevuto è
+\var{SIGINT}, che si è preso come segnale da utilizzare per la terminazione
+del programma, che verrà eseguita dopo aver rimosso il file della \textit{name
+ fifo}.
+
+Il secondo condizionale (\texttt{\small 26--39}) è invece relativo al caso in
+cui ci siano dati pronti in lettura sulla fifo e che il file descriptor pronto
+corrisponda (\texttt{\small 26}) a \var{fifofd}. Di nuovo si effettueranno le
+letture in un ciclo (\texttt{\small 28--39}) ripetendole fin tanto che la
+funzione \func{read} non resituisce un errore di \errcode{EAGAIN}
+(\texttt{\small 29--35}).\footnote{il procedimento è lo stesso adottato per il
+ file descriptor associato al segnale, in cui si esce dal programma in caso
+ di errore reale, in questo caso però alla fine dei dati prima di uscire si
+ stampa anche (\texttt{\small 32}) un messaggio di chiusura.} Se invece vi
+sono dati validi letti dalla fifo si inserirà (\texttt{\small 36}) una
+terminazione di stringa sul buffer e si stamperà il tutto (\texttt{\small
+ 37--38}) sullo \textit{standard output}. L'ultimo condizionale
+(\texttt{\small 40--44}) è semplicemente una condizione di cattura per una
+eventualità che comunque non dovrebbe mai verificarsi, e che porta alla uscita
+dal programma con una opportuna segnalazione di errore.
+
+A questo punto si potrà eseguire il comando lanciandolo su un terminale, ed
+osservarne le reazioni agli eventi generati da un altro terminale; lanciando
+il programma otterremo qualcosa del tipo:
+\begin{Verbatim}
+piccardi@hain:~/gapil/sources$ ./a.out
+FifoReporter starting, pid 4568
+\end{Verbatim}
+%$
+e scrivendo qualcosa sull'altro terminale con:
+\begin{Verbatim}
+root@hain:~# echo prova > /tmp/reporter.fifo
+\end{Verbatim}
+si otterrà:
+\begin{Verbatim}
+Message from fifo:
+prova
+end message
+\end{Verbatim}
+mentre inviando un segnale:
+\begin{Verbatim}
+root@hain:~# kill 4568
+\end{Verbatim}
+si avrà:
+\begin{Verbatim}
+Signal received:
+Got SIGTERM
+From pid 3361
+\end{Verbatim}
+ed infine premendo \texttt{C-\bslash} sul terminale in cui è in esecuzione si
+vedrà:
+\begin{Verbatim}
+^\Signal received:
+Got SIGQUIT
+From pid 0
+\end{Verbatim}
+e si potrà far uscire il programma con \texttt{C-c} ottenendo:
+\begin{Verbatim}
+^CSignal received:
+Got SIGINT
+From pid 0
+SIGINT means exit
+\end{Verbatim}
+
+
Lo stesso paradigma di notifica tramite file descriptor usato per i segnali è
-stato adottato anche per i timer; in questo caso, rispetto a quanto visto in
+stato adottato anche per i timer. In questo caso, rispetto a quanto visto in
sez.~\ref{sec:sig_timer_adv}, la scadenza di un timer potrà essere letta da un
-file descriptor, senza dover ricorrere ad altri meccanismi di notifica come un
+file descriptor senza dover ricorrere ad altri meccanismi di notifica come un
segnale o un \textit{thread}. Di nuovo questo ha il vantaggio di poter
utilizzare le funzioni dell'\textit{I/O multiplexing} per attendere allo
-stesso tempo la disponibilità di dati o la ricezione scadenza di un
+stesso tempo la disponibilità di dati o la ricezione della scadenza di un
timer.\footnote{in realtà per questo sarebbe già sufficiente \func{signalfd}
per ricevere i segnali associati ai timer, ma la nuova interfaccia
semplifica notevolmente la gestione e consente di fare tutto con una sola
\textit{system call}.}
-Le funzioni di questa interfaccia ricalcano da vicino la struttura delle
+Le funzioni di questa nuova interfaccia ricalcano da vicino la struttura delle
analoghe versioni ordinarie introdotte con lo standard POSIX.1-2001, che
abbiamo già illustrato in sez.~\ref{sec:sig_timer_adv}.\footnote{questa
interfaccia è stata introdotta in forma considerata difettosa con il kernel
2.6.22, per cui è stata immediatamente tolta nel successivo 2.6.23 e
reintrodotta in una forma considerata adeguata nel kernel 2.6.25, il
supporto nelle \acr{glibc} è stato introdotto a partire dalla versione
- 2.8.6, la versione del kernel 2.6.22 non è supportata e non deve essere
- usata.} La prima funzione prevista, quella che consente di creare un
-\textit{timer}, è \funcd{timerfd\_create}, il cui prototipo è:
+ 2.8.6, la versione del kernel 2.6.22, presente solo su questo kernel, non è
+ supportata e non deve essere usata.} La prima funzione prevista, quella che
+consente di creare un timer, è \funcd{timerfd\_create}, il cui prototipo è:
\begin{prototype}{sys/timerfd.h}
{int timerfd\_create(int clockid, int flags)}
verranno notificate le scadenze dei timer. Come per quelli restituiti da
\func{signalfd} anche questo file descriptor segue la semantica dei sistemi
unix-like, in particolare resta aperto attraverso una \func{exec},\footnote{a
- meno che non si sia impostato il flag di \textit{close-on exex} con
+ meno che non si sia impostato il flag di \textit{close-on exec} con
\const{TFD\_CLOEXEC}.} e viene duplicato attraverso una \func{fork}; questa
ultima caratteristica comporta però che anche il figlio può utilizzare i dati
di un timer creato nel padre, a differenza di quanto avviene invece con i
come illustrato in sez.~\ref{sec:proc_fork}, allarmi, timer e segnali
pendenti nel padre vengono cancellati per il figlio dopo una \func{fork}.}
-Una volta creato il timer con \funcd{timerfd\_create} per poterlo utilizzare
+Una volta creato il timer con \func{timerfd\_create} per poterlo utilizzare
occorre \textsl{armarlo} impostandone un tempo di scadenza ed una eventuale
-periodicità di ripetizione, per farlo su usa la funzione omologa di
+periodicità di ripetizione, per farlo si usa la funzione omologa di
\func{timer\_settime} per la nuova interfaccia; questa è
-\func{timerfd\_settime}; il suo prototipo è:
+\funcd{timerfd\_settime} ed il suo prototipo è:
\begin{prototype}{sys/timerfd.h}
{int timerfd\_settime(int fd, int flags,
- const struct itimerspec *new_value,
- struct itimerspec *old_value)}
+ const struct itimerspec *new\_value,
+ struct itimerspec *old\_value)}
Crea un timer associato ad un file descriptor per la notifica.
successo o $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno
dei valori:
\begin{errlist}
- \item[\errcode{EINVAL}] l'argomento \param{clockid} non è
- \const{CLOCK\_MONOTONIC} o \const{CLOCK\_REALTIME}, o
- l'argomento \param{flag} non è valido, o è diverso da zero per kernel
- precedenti il 2.6.27.
- \item[\errcode{ENOMEM}] non c'è memoria sufficiente per creare un nuovo file
- descriptor di \func{signalfd}.
- \item[\errcode{ENODEV}] il kernel non può montare internamente il
- dispositivo per la gestione anonima degli inode associati al file
- descriptor.
+ \item[\errcode{EBADF}] l'argomento \param{fd} non corrisponde ad un file
+ descriptor.
+ \item[\errcode{EINVAL}] il file descriptor \param{fd} non è stato ottenuto
+ con \func{timerfd\_create}, o i valori di \param{flag} o dei campi
+ \var{tv\_nsec} in \param{new\_value} non sono validi.
+ \item[\errcode{EFAULT}] o \param{new\_value} o \param{old\_value} non sono
+ puntatori validi.
\end{errlist}
- ed inoltre \errval{EMFILE} e \errval{ENFILE}.
}
\end{prototype}
+In questo caso occorre indicare su quale timer si intende operare specificando
+come primo argomento il file descriptor ad esso associato, che deve essere
+stato ottenuto da una precedente chiamata a \func{timerfd\_create}. I restanti
+argomenti sono del tutto analoghi a quelli della omologa funzione
+\func{timer\_settime}, e prevedono l'uso di strutture \struct{itimerspec}
+(vedi fig.~\ref{fig:struct_itimerspec}) per le indicazioni di temporizzazione.
+
+I valori ed il significato di questi argomenti sono gli stessi che sono già
+stati illustrati in dettaglio in sez.~\ref{sec:sig_timer_adv} e non staremo a
+ripetere quanto detto in quell'occasione;\footnote{per brevità si ricordi che
+ con \param{new\_value.it\_value} si indica la prima scadenza del timer e
+ con \param{new\_value.it\_interval} la sua periodicità.} l'unica differenza
+riguarda l'argomento \param{flags} che serve sempre ad indicare se il tempo di
+scadenza del timer è da considerarsi relativo o assoluto rispetto al valore
+corrente dell'orologio associato al timer, ma che in questo caso ha come
+valori possibili rispettivamente soltanto $0$ e
+\const{TFD\_TIMER\_ABSTIME}.\footnote{anche questo valore, che è l'analogo di
+ \const{TIMER\_ABSTIME} è l'unico attualmente possibile per \param{flags}.}
+
+L'ultima funzione prevista dalla nuova interfaccia è \funcd{timerfd\_gettime},
+che è l'analoga di \func{timer\_gettime}, il suo prototipo è:
+\begin{prototype}{sys/timerfd.h}
+ {int timerfd\_gettime(int fd, struct itimerspec *curr\_value)}
+ Crea un timer associato ad un file descriptor per la notifica.
-
-.\footnote{si otterranno in entrambi i casi gli stessi
- risultati, il file descriptor risulterà pronto in lettura e in entrambi i
- processi si potranno leggere il numero di scadenze.}
+ \bodydesc{La funzione restituisce un numero di file descriptor in caso di
+ successo o $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno
+ dei valori:
+ \begin{errlist}
+ \item[\errcode{EBADF}] l'argomento \param{fd} non corrisponde ad un file
+ descriptor.
+ \item[\errcode{EINVAL}] il file descriptor \param{fd} non è stato ottenuto
+ con \func{timerfd\_create}.
+ \item[\errcode{EFAULT}] o \param{curr\_value} non è un puntatore valido.
+ \end{errlist}
+}
+\end{prototype}
-Questo infatti diverrà pronto in
-lettura per tutte le varie funzioni dell'I/O multiplexing in presenza di una o
-più scadenze del timer ad esso associato.
+Questo infatti diverrà pronto in lettura per tutte le varie funzioni dell'I/O
+multiplexing in presenza di una o più scadenze del timer ad esso associato.
-Inoltre sarà possibile ottenere il
-numero di volte che il timer è scaduto dalla ultima impostazione
+Inoltre sarà possibile ottenere il numero di volte che il timer è scaduto
+dalla ultima impostazione
che può essere
usato per leggere le notifiche delle scadenze dei timer. Queste possono essere
% http://lwn.net/Articles/267331/
-\begin{figure}[!htb]
- \footnotesize \centering
- \begin{minipage}[c]{15cm}
- \includecodesample{listati/FifoReporter-init.c}
- \end{minipage}
- \normalsize
- \caption{Sezione di inizializzazione del codice del programma
- \file{FifoReporter.c}.}
- \label{fig:fiforeporter_code}
-\end{figure}
-
-
-
-\begin{figure}[!htb]
- \footnotesize \centering
- \begin{minipage}[c]{15cm}
- \includecodesample{listati/FifoReporter-main.c}
- \end{minipage}
- \normalsize
- \caption{Ciclo principale del codice del programma \file{FifoReporter.c}.}
- \label{fig:fiforeporter_code}
-\end{figure}
-
-
-
\section{L'accesso \textsl{asincrono} ai file}
\label{sec:file_asyncronous_access}
\subsection{L'interfaccia POSIX per l'I/O asincrono}
\label{sec:file_asyncronous_io}
+% vedere anche http://davmac.org/davpage/linux/async-io.html
+
Una modalità alternativa all'uso dell'\textit{I/O multiplexing} per gestione
dell'I/O simultaneo su molti file è costituita dal cosiddetto \textsl{I/O
asincrono}. Il concetto base dell'\textsl{I/O asincrono} è che le funzioni
\item[\errcode{ENOMEM}] non c'è memoria sufficiente per l'operazione
richiesta.
\item[\errcode{ESPIPE}] o \param{off\_in} o \param{off\_out} non sono
- \const{NULL} ma il corrispondente file descriptor è una \textit{pipe}.
+ \val{NULL} ma il corrispondente file descriptor è una \textit{pipe}.
\end{errlist}
}
\end{functions}
-%\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
% TODO non so dove trattarli, ma dal 2.6.39 ci sono i file handle, vedi
% http://lwn.net/Articles/432757/
% LocalWords: Jens Anxboe vmsplice seek ESPIPE GIFT TCP CORK MSG splicecp nr
% LocalWords: nwrite segs patch readahead posix fadvise TC advice FADV NORMAL
% LocalWords: SEQUENTIAL NOREUSE WILLNEED DONTNEED streaming fallocate EFBIG
-% LocalWords: POLLRDHUP half close pwait Gb madvise MADV ahead REMOVE tmpfs
+% LocalWords: POLLRDHUP half close pwait Gb madvise MADV ahead REMOVE tmpfs it
% LocalWords: DONTFORK DOFORK shmfs preadv pwritev syscall linux loff head XFS
% LocalWords: MERGEABLE EOVERFLOW prealloca hole FALLOC KEEP stat fstat union
% LocalWords: conditions sigwait CLOEXEC signalfd sizemask SIGKILL SIGSTOP ssi
-% LocalWords: sigwaitinfo FifoReporter Windows ptr sigqueue
+% LocalWords: sigwaitinfo FifoReporter Windows ptr sigqueue named timerfd TFD
+% LocalWords: clockid CLOCK MONOTONIC REALTIME itimerspec interval
+% LocalWords: ABSTIME gettime
%%% Local Variables: