Fatta prima parte dell'I/O asincrono (quella relativa a SIGIO)
[gapil.git] / fileadv.tex
index 7b3a6b10b551532e357c3828ad6cb72fbd1c5ebe..591f1494453baa0b6ea51d51d13f5c518dd8d0bf 100644 (file)
@@ -55,7 +55,7 @@ bloccante.
 
 
 
-\subsection{Le funzioni \func{poll} e \func{select}}
+\subsection{L'I/O multiplexing}
 \label{sec:file_multiplexing}
 
 Per superare il problema di dover usare il \textit{polling} per controllare la
@@ -67,9 +67,12 @@ usualmente \textit{I/O multiplexing}, 
   apparsa in BSD4.2 e standardizzata in BSD4.4, ma è stata portata su tutti i
   sistemi che supportano i \textit{socket}, compreso le varianti di System V.}
 con la funzione \func{select}, il cui prototipo è:
-\begin{prototype}{sys/select.h}
-  {int select(int n, fd\_set *readfds, fd\_set *writefds, fd\_set *exceptfds,
-    struct timeval *timeout)}
+\begin{functions}
+  \headdecl{sys/time.h}
+  \headdecl{sys/types.h}
+  \headdecl{unistd.h}
+  \funcdecl{int select(int n, fd\_set *readfds, fd\_set *writefds, fd\_set
+    *exceptfds, struct timeval *timeout)}
   
   Attende che uno dei file descriptor degli insiemi specificati diventi
   attivo.
@@ -85,7 +88,7 @@ con la funzione \func{select}, il cui prototipo 
   \end{errlist}
   ed inoltre \macro{ENOMEM}.
 }
-\end{prototype}
+\end{functions}
 
 La funzione mette il processo in stato di \textit{sleep} (vedi
 \tabref{tab:proc_proc_states}) fintanto che almeno uno dei file descriptor
@@ -101,7 +104,9 @@ file descriptor, (in maniera analoga a come un \textit{signal set}, vedi
 manipolazione di questi \textit{file descriptor set} si possono usare delle
 opportune macro di preprocessore:
 \begin{functions}
-  \headdecl{sys/select.h}
+  \headdecl{sys/time.h}
+  \headdecl{sys/types.h}
+  \headdecl{unistd.h}
   \funcdecl{FD\_ZERO(fd\_set *set)}
   Inizializza l'insieme (vuoto).
 
@@ -161,8 +166,10 @@ volte il tempo rimanente.\footnote{questo per
   System V e non disponibile per quelli che derivano da BSD.}
 
 Come accennato l'interfaccia di \func{select} è una estensione di BSD; anche
-System V ha introdotto una sua interfaccia per getire l'\textit{I/O
-  multiplexing}, basata sulla funzione \func{poll}, il cui prototipo è:
+System V ha introdotto una sua interfaccia per gestire l'\textit{I/O
+  multiplexing}, basata sulla funzione \func{poll},\footnote{la funzione è
+  prevista dallo standard XPG4, ed è stata introdotta in Linux come system
+  call a partire dal kernel 2.1.23 e dalle libc 5.4.28.} il cui prototipo è:
 \begin{prototype}{sys/poll.h}
   {int poll(struct pollfd *ufds, unsigned int nfds, int timeout)}
 
@@ -180,29 +187,216 @@ specificati da \param{ufds}.
   ed inoltre \macro{EFAULT} e \macro{ENOMEM}.}
 \end{prototype}
 
+La funzione tiene sotto controllo un numero \param{ndfs} di file descriptor
+specificati attraverso un vettore di puntatori a strutture di tipo
+\type{pollfd}, la cui definizione è riportata in \figref{fig:file_pollfd}.
+Come \func{select} anche \func{poll} permette di interrompere l'attesa dopo un
+certo tempo, che va specificato attraverso \param{timeout} in numero di
+millesecondi (un valore negativo indica un'attesa indefinita).
+
+\begin{figure}[!htb]
+  \footnotesize \centering
+  \begin{minipage}[c]{15cm}
+    \begin{lstlisting}[labelstep=0]{}%,frame=,indent=1cm]{}
+struct pollfd {
+        int fd;           /* file descriptor */
+        short events;     /* requested events */
+        short revents;    /* returned events */
+};
+    \end{lstlisting}
+  \end{minipage} 
+  \normalsize 
+  \caption{La struttura \type{pollfd}, utilizzata per specificare le modalità
+    di controllo di un file descriptor alla funzione \func{poll}.}
+  \label{fig:file_pollfd}
+\end{figure}
+
+Per ciascun file da controllare deve essere opportunamente predisposta una
+struttura \type{pollfd}; nel campo \var{fd} deve essere specificato il file
+descriptor, mentre nel campo \var{events} il tipo di evento su cui si vuole
+attendere; quest'ultimo deve essere specificato come maschera binaria dei
+primi tre valori riportati in \tabref{tab:file_pollfd_flags} (gli altri
+vengono utilizzati solo per \var{revents} come valori in uscita).
+
+\begin{table}[htb]
+  \centering
+  \footnotesize
+  \begin{tabular}[c]{|l|c|l|}
+    \hline
+    \textbf{Flag} & \textbf{Valore} & \textbf{Significato} \\
+    \hline
+    \hline
+    \macro{POLLIN}    & 0x001 & È possibile la lettura immediata.\\
+    \macro{POLLPRI}   & 0x002 & Sono presenti dati urgenti.\\
+    \macro{POLLOUT}   & 0x004 & È possibile la scrittura immediata.\\
+    \hline
+    \macro{POLLERR}   & 0x008 & C'è una condizione di errore.\\
+    \macro{POLLHUP}   & 0x010 & Si è vericato un hung-up.\\
+    \macro{POLLNVAL}  & 0x020 & Il file descriptor non è aperto.\\
+    \hline
+    \macro{POLLRDNORM}& 0x040 & Sono disponibili in lettura dati normali.\\ 
+    \macro{POLLRDBAND}& 0x080 & Sono disponibili in lettura dati ad alta 
+                                priorità. \\
+    \macro{POLLWRNORM}& 0x100 & È possibile la scrittura di dati normali.  \\ 
+    \macro{POLLWRBAND}& 0x200 & È possibile la scrittura di dati ad 
+                                alta priorità. \\
+    \macro{POLLMSG}   & 0x400 & Estensione propria di Linux.\\
+    \hline    
+  \end{tabular}
+  \caption{Costanti per l'identificazione dei vari bit dei campi
+    \var{events} e \var{revents} di \type{pollfd}.}
+  \label{tab:file_pollfd_flags}
+\end{table}
+
+La funzione ritorna, restituendo il numero di file per i quali si è verificata
+una delle condizioni di attesa richieste o un errore. Lo stato dei file
+all'uscita della funzione viene restituito nel campo \var{revents} della
+relativa struttura \type{pollfd}, che viene settato alla maschera binaria dei
+valori riportati in \tabref{tab:file_pollfd_flags}, ed oltre alle tre
+condizioni specificate tramite \var{events} può riportare anche l'occorrere di
+una condizione di errore.
+
+Lo standard POSIX è rimasto a lungo senza primitive per l'\textit{I/O
+  multiplexing}, che è stata introdotto con le ultime revisioni dello standard
+(POSIX 1003.1g-2000 e POSIX 1003.1-2001). Esso prevede che tutte le funzioni
+ad esso relative vengano dichiarate nell'header \file{sys/select.h}, che
+sostituisce i precedenti, ed aggiunge a \func{select} una nuova funzione
+\func{pselect},\footnote{il supporto per lo standard POSIX 1003.1-2001, ed
+  l'header \file{sys/select.h}, compaiono in Linux a partire dalle \acr{glibc}
+  2.0. Le \acr{libc4} e \acr{libc5} non contengono questo header, le
+  \acr{glibc} 2.0 contengono una definizione sbagliata di \func{psignal},
+  senza l'argomento \param{sigmask}, la definizione corretta è presente dalle
+  \acr{glibc} 2.1-2.2.1 se si è definito \macro{\_GNU\_SOURCE} e nelle
+  \acr{glibc} 2.2.2-2.2.4 se si è definito \macro{\_XOPEN\_SOURCE} con valore
+  maggiore di 600.} il cui prototipo è:
+\begin{prototype}{sys/select.h}
+  {int pselect(int n, fd\_set *readfds, fd\_set *writefds, fd\_set *exceptfds,
+    struct timespec *timeout, sigset\_t *sigmask)}
+  
+  Attende che uno dei file descriptor degli insiemi specificati diventi
+  attivo.
+  
+  \bodydesc{La funzione in caso di successo restituisce il numero di file
+    descriptor (anche nullo) che sono attivi, e -1 in caso di errore, nel qual
+    caso \var{errno} viene settata ai valori:
+  \begin{errlist}
+  \item[\macro{EBADF}] Si è specificato un file descriptor sbagliato in uno
+  degli insiemi.
+  \item[\macro{EINTR}] La funzione è stata interrotta da un segnale.
+  \item[\macro{EINVAL}] Si è specificato per \param{n} un valore negativo.
+  \end{errlist}
+  ed inoltre \macro{ENOMEM}.}
+\end{prototype}
+
+La funzione è sostanzialmente identica a \func{select}, solo che usa una
+struttura \type{timespec} per indicare con maggiore precisione il timeout e
+non ne aggiorna il valore in caso di interruzione, inoltre prende un argomento
+aggiuntivo \param{sigmask} che è il puntatore ad una maschera di segnali (si
+veda \secref{sec:sig_sigmask}). La maschera corrente viene sostituita da
+questa immediatamente prima di eseguire l'attesa, e ripristinata al ritorno
+della funzione.
+
+L'uso di \param{sigmask} è stato introdotto allo scopo di prevenire possibili
+race condition\footnote{in Linux però, non esistendo una system call apposita,
+  la funzione è implementata nelle \acr{glibc} usando \func{select}, e la
+  possibilità di una race condition resta.} quando si deve eseguire un test su
+una variabile settata da un manipolatore sulla base dell'occorrenza di un
+segnale per decidere se lanciare \func{select}. Fra il test e l'esecuzione è
+presente una finestra in cui potrebbe arrivare il segnale che non sarebbe
+rilevato; la race condition diventa superabile disabilitando il segnale prima
+del test e riabilitandolo poi grazie all'uso di \param{sigmask}.
 
 
-\subsection{L'I/O asincrono}
+
+\subsection{L'\textsl{I/O asincrono}}
 \label{sec:file_asyncronous_io}
 
-Una modalità alternativa all'uso dell'I/O non bloccante è quella di fare
-ricorso all'I/O asincrono. Abbiamo accennato in \secref{sec:file_open} che è
-possibile, attraverso l'uso del flag \macro{O\_ASYNC}, aprire un file in
-modalità asincrona, così come è possibile settare questo flag attraverso l'uso
-di \func{fcntl}.
+Una modalità alternativa all'uso dell'\textit{I/O multiplexing} è quella di
+fare ricorso al cosiddetto \textsl{I/O asincrono}. Il concetto base
+dell'\textsl{I/O asincrono} è che le funzioni di I/O non attendono il
+completamento delle operazioni prima di ritornare, così che il processo non
+viene bloccato.  In questo modo diventa ad esempio possibile effettuare una
+richiesta preventiva di dati, in modo da poter effettuare in contemporanea le
+operazioni di calcolo e quelle di I/O.
+
+Abbiamo accennato in \secref{sec:file_open} che è possibile, attraverso l'uso
+del flag \macro{O\_ASYNC},\footnote{l'uso del flag di \macro{O\_ASYNC} e dei
+  comandi \macro{F\_SETOWN} e \macro{F\_GETOWN} per \func{fcntl} è specifico
+  di Linux e BSD.} aprire un file in modalità asincrona, così come è possibile
+attivare in un secondo tempo questa modalità settando questo flag attraverso
+l'uso di \func{fcntl} con il comando \macro{F\_SETFL} (vedi
+\secref{sec:file_fcntl}).
+
+In realtà in questo caso non si tratta di I/O asincrono vero e proprio, quanto
+di un meccanismo asincrono di notifica delle variazione dello stato del file
+descriptor; quello che succede è che il sistema genera un segnale (normalmente
+\macro{SIGIO}, ma è possibile usarne altri) tutte le volte che diventa
+possibile leggere o scrivere dal file descriptor che si è posto in questa
+modalità. Si può inoltre selezionare, con il comando \macro{F\_SETOWN} di
+\func{fcntl}, quale processo (o gruppo di processi) riceverà il segnale.
+
+Uno dei problemi che si presenta con l'implementazione usuale di questa
+modalità di I/O è che essa può essere usata in maniera immediata aprendo in
+modalità asincrona un solo file per processo, altrimenti ad ogni segnale si
+dovrebbe provvedere ad effettuare un controllo (utilizzando di nuovo
+\func{select}) su tutti i file tenuti in modalità asincrona per distinguere
+quelli cui è dovuta l'emissione del segnale.
+
+Linux però supporta una estensione che permette di evitare tutto questo
+facendo ricorso alle informazioni aggiuntive restituite attraverso la
+struttura \type{siginfo\_t} quando il manipolatore del segnale viene
+installato come \macro{SA\_SIGINFO} (si riveda quanto illustrato in
+\secref{sec:sig_sigaction}).
+
+Per attivare questa caratteristica occorre settare esplicitamente il segnale
+da inviare in caso di I/O asincrono (di norma sempre \macro{SIGIO}) con il
+comando \macro{F\_SETSIG} di \func{fcntl}. In questo caso il manipolatore
+tutte le volte che riceverà \macro{SI\_SIGIO} come valore del campo
+\var{si\_code}\footnote{il valore resta \macro{SI\_SIGIO} qualunque sia il
+  segnale che si è associato all'I/O asincrono, ed indica appunto che il
+  segnale è stato generato a causa di attività nell'I/O asincrono.} di
+\type{siginfo\_t}, troverà nel campo \var{si\_fd} il valore del file
+descriptor che ha generato il segnale. In questo modo è possibile identificare
+immediatamente il file evitando completamente l'uso di funzioni come
+\func{poll} o \func{select}. Inoltre, a differenza degli altri segnali, il
+sistema mantiene una coda per \macro{SIGIO}, in modo che arrivi un segnale per
+ogni file attivo.
+
+
+Benché la modalità di apertura asincrona di un file possa risultare utile in
+varie occasioni (in particolar modo con i socket e gli altri file per i quali
+le funzioni di I/O sono system call lente), essa è comunque limitata alla
+notifica della disponibilità del file descriptor per le operazioni di I/O, e
+non ad uno svolgimento asincrono delle medesime.  Lo standard POSIX.1b
+definisce invece una interfaccia apposita per l'I/O asincrono, che prevede un
+insieme di funzioni dedicate, completamente separato rispetto a quelle usate
+normalmente.
+
+In generale questa interfaccia è completamente astratta e può essere
+implementata sia direttamente nel kernel, che attraverso l'uso di thread. Al
+momento\footnote{fino ai kernel della serie 2.4.x sono presenti solo le due
+  alternative citate, nella serie 2.5.x è però iniziato un lavoro completo di
+  riscrittura di tutto il sistema di I/O, che prevede anche l'introduzione di
+  un nuovo layer per l'I/O asincrono.} sono disponibili due implementazioni,
+una in user space, che è integrata nelle \acr{glibc}, ed una ibrida, che si
+appoggia ad una estensione del kernel (il cosiddetto KAIO, non incluso nella
+versione ufficiale) prodotta da SGI. Quest'ultima ha funzionalità più limitate
+ma prestazioni nettamente superiori.
+
+Alle funzioni definite essa si può accedere includendo \file{unistd.h} e
+definendo la macro \macro{\_POSIX\_ASYNCHRONOUS\_IO}, mentre alla
+implementazione di KAIO si accede includendo \file{linux/aio.h}; ambedue però
+usano la stessa interfaccia di programmazione.
 
-In tal caso il sistema genera un segnale \macro{SIGIO} tutte le volte che sono
-presenti dei dati in input su un file aperto in questa modalità.  Uno dei
-problemi che si presentavano con le prime implementazioni di questa modalità
-di I/O è che essa poteva essere usata in maniera semplice aprendo un solo file
-per processo, dato che altrimenti si sarebbe dovuto provvedere ad effettuare
-una serie di controlli su tutti i file aperti per distinguere a quale fosse
-dovuto l'emissione del segnale.
 
-Tutto questo adesso può essere evitato facendo ricorso alle informazioni
-restituite al manipolatore del segnale attraverso la struttura
-\var{siginfo\_t} (vedi \figref{fig:sig_siginfo_t}), il cui campo \var{si\_fd}
-riporta il file descriptor che ha generato il segnale.
+
+
+\subsection{I/O multiplo}
+\label{sec:file_multiple_io}
+
+Un caso abbastanza comune è quello in cui ci si trova a dover affrontare una
+serie multipla di operazioni di I/O, come una serie di letture o scritture di
+vari buffer. In questo caso 
 
 
 
@@ -210,8 +404,7 @@ riporta il file descriptor che ha generato il segnale.
 \label{sec:file_memory_map}
 
 
-\subsection{I/O multiplo}
-\label{sec:file_multiple_io}
+