Aggiornate le date nelle note di copyright
[gapil.git] / fileadv.tex
index f4ccd79ade39d6c3a482d7f16498e362c9b5a368..6d87e629831e9e6c992dd879bdffdbf9de4349f7 100644 (file)
@@ -1,6 +1,6 @@
 %% fileadv.tex
 %%
-%% Copyright (C) 2000-2004 Simone Piccardi.  Permission is granted to
+%% Copyright (C) 2000-2005 Simone Piccardi.  Permission is granted to
 %% copy, distribute and/or modify this document under the terms of the GNU Free
 %% Documentation License, Version 1.1 or any later version published by the
 %% Free Software Foundation; with the Invariant Sections being "Un preambolo",
@@ -36,14 +36,14 @@ I/O.
 \label{sec:file_noblocking}
 
 Abbiamo visto in sez.~\ref{sec:sig_gen_beha}, affrontando la suddivisione fra
-\textit{fast} e \textit{slow} system call,\index{system call lente} che in
+\textit{fast} e \textit{slow} system call,\index{system~call~lente} che in
 certi casi le funzioni di I/O possono bloccarsi indefinitamente.\footnote{si
   ricordi però che questo può accadere solo per le pipe, i
-  socket\index{socket} ed alcuni file di dispositivo\index{file!di
-    dispositivo}; sui file normali le funzioni di lettura e scrittura
-  ritornano sempre subito.}  Ad esempio le operazioni di lettura possono
-bloccarsi quando non ci sono dati disponibili sul descrittore su cui si sta
-operando.
+  socket\index{socket} ed alcuni file di
+  dispositivo\index{file!di~dispositivo}; sui file normali le funzioni di
+  lettura e scrittura ritornano sempre subito.}  Ad esempio le operazioni di
+lettura possono bloccarsi quando non ci sono dati disponibili sul descrittore
+su cui si sta operando.
 
 Questo comportamento causa uno dei problemi più comuni che ci si trova ad
 affrontare nelle operazioni di I/O, che si verifica quando si deve operare con
@@ -56,21 +56,20 @@ un'altro disponibile. Questo comporta nel migliore dei casi una operazione
 ritardata inutilmente nell'attesa del completamento di quella bloccata, mentre
 nel peggiore dei casi (quando la conclusione della operazione bloccata dipende
 da quanto si otterrebbe dal file descriptor ``\textsl{disponibile}'') si
-potrebbe addirittura arrivare ad un \textit{deadlock}\index{deadlock}.
+potrebbe addirittura arrivare ad un \textit{deadlock}\index{\textit{deadlock}}.
 
 Abbiamo già accennato in sez.~\ref{sec:file_open} che è possibile prevenire
-questo tipo di comportamento delle funzioni di I/O aprendo un file in quella
-che viene chiamata \textsl{modalità non-bloccante}, attraverso l'uso del flag
-\const{O\_NONBLOCK} nella chiamata di \func{open}. In questo caso le funzioni
-di input/output eseguite sul file che si sarebbero bloccate, ritornano
-immediatamente, restituendo l'errore \errcode{EAGAIN}.
-
-L'utilizzo di questa modalità di I/O permette di risolvere il problema
-controllando a turno i vari file descriptor, in un ciclo in cui si ripete
-l'accesso fintanto che esso non viene garantito.  Ovviamente questa tecnica,
-detta \textit{polling}\index{polling}, è estremamente inefficiente: si tiene
-costantemente impiegata la CPU solo per eseguire in continuazione delle system
-call che nella gran parte dei casi falliranno. 
+questo tipo di comportamento delle funzioni di I/O aprendo un file in
+\textsl{modalità non-bloccante}, attraverso l'uso del flag \const{O\_NONBLOCK}
+nella chiamata di \func{open}. In questo caso le funzioni di input/output
+eseguite sul file che si sarebbero bloccate, ritornano immediatamente,
+restituendo l'errore \errcode{EAGAIN}.  L'utilizzo di questa modalità di I/O
+permette di risolvere il problema controllando a turno i vari file descriptor,
+in un ciclo in cui si ripete l'accesso fintanto che esso non viene garantito.
+Ovviamente questa tecnica, detta \textit{polling}\index{\textit{polling}}, è
+estremamente inefficiente: si tiene costantemente impiegata la CPU solo per
+eseguire in continuazione delle system call che nella gran parte dei casi
+falliranno.
 
 Per superare questo problema è stato introdotto il concetto di \textit{I/O
   multiplexing}, una nuova modalità di operazioni che consenta di tenere sotto
@@ -89,11 +88,11 @@ ulteriori dettagli e qualche esempio in sez.~\ref{sec:TCP_sock_multiplexing}.
 \subsection{Le funzioni \func{select} e \func{pselect}}
 \label{sec:file_select}
 
-Il primo ad introdurre una interfaccia per l'\textit{I/O multiplexing} è stato
-BSD,\footnote{la funzione \func{select} è apparsa in BSD4.2 e standardizzata
-  in BSD4.4, ma è stata portata su tutti i sistemi che supportano i
-  \textit{socket}\index{socket}, compreso le varianti di System V.}  con la
-funzione \funcd{select}, il cui prototipo è:
+Il primo kernel unix-like ad introdurre una interfaccia per l'\textit{I/O
+  multiplexing} è stato BSD,\footnote{la funzione \func{select} è apparsa in
+  BSD4.2 e standardizzata in BSD4.4, ma è stata portata su tutti i sistemi che
+  supportano i \textit{socket}\index{socket}, compreso le varianti di System
+  V.}  con la funzione \funcd{select}, il cui prototipo è:
 \begin{functions}
   \headdecl{sys/time.h}
   \headdecl{sys/types.h}
@@ -124,10 +123,12 @@ degli insiemi specificati (\param{readfds}, \param{writefds} e
 \param{exceptfds}), non diventa attivo, per un tempo massimo specificato da
 \param{timeout}.
 
+\index{\textit{file~descriptor~set}|(}
 Per specificare quali file descriptor si intende \textsl{selezionare}, la
 funzione usa un particolare oggetto, il \textit{file descriptor set},
 identificato dal tipo \type{fd\_set}, che serve ad identificare un insieme di
-file descriptor, in maniera analoga a come un \textit{signal set} (vedi
+file descriptor, in maniera analoga a come un
+\index{\textit{signal~set}}\textit{signal set} (vedi
 sez.~\ref{sec:sig_sigset}) identifica un insieme di segnali. Per la
 manipolazione di questi \textit{file descriptor set} si possono usare delle
 opportune macro di preprocessore:
@@ -179,23 +180,24 @@ necessaria. Questo limite viene indicato tramite l'argomento \param{n}, che
 deve corrispondere al valore massimo aumentato di uno.\footnote{i file
   descriptor infatti sono contati a partire da zero, ed il valore indica il
   numero di quelli da tenere sotto controllo; dimenticarsi di aumentare di uno
-  il valore di \param{n} è un errore comune.}
-
-Infine l'argomento \param{timeout}, specifica un tempo massimo di attesa prima
-che la funzione ritorni; se impostato a \val{NULL} la funzione attende
-indefinitamente. Si può specificare anche un tempo nullo (cioè una struttura
-\struct{timeval} con i campi impostati a zero), qualora si voglia
-semplicemente controllare lo stato corrente dei file descriptor.
+  il valore di \param{n} è un errore comune.}  Infine l'argomento
+\param{timeout}, specifica un tempo massimo di attesa prima che la funzione
+ritorni; se impostato a \val{NULL} la funzione attende indefinitamente. Si può
+specificare anche un tempo nullo (cioè una struttura \struct{timeval} con i
+campi impostati a zero), qualora si voglia semplicemente controllare lo stato
+corrente dei file descriptor.
+\index{\textit{file~descriptor~set}|)}
 
 La funzione restituisce il numero di file descriptor pronti,\footnote{questo è
   il comportamento previsto dallo standard, ma la standardizzazione della
   funzione è recente, ed esistono ancora alcune versioni di Unix che non si
   comportano in questo modo.}  e ciascun insieme viene sovrascritto per
-indicare i file descriptor pronti per le operazioni ad esso relative, in modo
-da poterli controllare con \const{FD\_ISSET}.  Se invece si ha un timeout
-viene restituito un valore nullo e gli insiemi non vengono modificati.  In
-caso di errore la funzione restituisce -1, ed i valori dei tre insiemi sono
-indefiniti e non si può fare nessun affidamento sul loro contenuto.
+indicare quali sono i file descriptor pronti per le operazioni ad esso
+relative, in modo da poterli controllare con \const{FD\_ISSET}.  Se invece si
+ha un timeout viene restituito un valore nullo e gli insiemi non vengono
+modificati.  In caso di errore la funzione restituisce -1, ed i valori dei tre
+insiemi sono indefiniti e non si può fare nessun affidamento sul loro
+contenuto.
 
 In Linux \func{select} modifica anche il valore di \param{timeout},
 impostandolo al tempo restante in caso di interruzione prematura; questo è
@@ -268,18 +270,15 @@ 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\index{race condition} quando ci si deve porre in attesa sia di
-un segnale che di dati.\footnote{in Linux però non è stata ancora introdotta
-  la relativa system call, pertanto la funzione è implementata nelle
-  \acr{glibc} attraverso \func{select} e la possibilità di race condition
-  permane.} La tecnica classica è quella di utilizzare il gestore per
-impostare una variabile globale e controllare questa nel corpo principale del
-programma; abbiamo visto in sez.~\ref{sec:sig_example} come questo lasci spazio
-a possibili race condition, per cui diventa essenziale utilizzare
-\func{sigprocmask} per disabilitare la ricezione del segnale prima di eseguire
-il controllo e riabilitarlo dopo l'esecuzione delle relative operazioni, onde
-evitare l'arrivo di un segnale immediatamente dopo il controllo, che andrebbe
-perso.
+race condition\index{\textit{race~condition}} quando ci si deve porre in
+attesa sia di un segnale che di dati. La tecnica classica è quella di
+utilizzare il gestore per impostare una variabile globale e controllare questa
+nel corpo principale del programma; abbiamo visto in
+sez.~\ref{sec:sig_example} come questo lasci spazio a possibili race
+condition, per cui diventa essenziale utilizzare \func{sigprocmask} per
+disabilitare la ricezione del segnale prima di eseguire il controllo e
+riabilitarlo dopo l'esecuzione delle relative operazioni, onde evitare
+l'arrivo di un segnale immediatamente dopo il controllo, che andrebbe perso.
 
 Nel nostro caso il problema si pone quando oltre al segnale si devono tenere
 sotto controllo anche dei file descriptor con \func{select}, in questo caso si
@@ -287,19 +286,28 @@ pu
 e si potrebbero eseguire di conseguenza le operazioni relative al segnale e
 alla gestione dati con un ciclo del tipo:
 \includecodesnip{listati/select_race.c} qui però emerge una race
-condition,\index{race condition} perché se il segnale arriva prima della
-chiamata a \func{select}, questa non verrà interrotta, e la ricezione del
-segnale non sarà rilevata.
+condition,\index{\textit{race~condition}} perché se il segnale arriva prima
+della chiamata a \func{select}, questa non verrà interrotta, e la ricezione
+del segnale non sarà rilevata.
 
-Per questo è stata introdotta \func{pselect}, che attraverso l'argomento
+Per questo è stata introdotta \func{pselect} che attraverso l'argomento
 \param{sigmask} permette di riabilitare la ricezione il segnale
-contestualmente all'esecuzione della funzione, e ribloccandolo non appena essa
-ritorna. In questo modo il precedente codice potrebbe essere modificato
-nel seguente modo: 
-\includecodesnip{listati/pselect_norace.c} 
-in questo caso utilizzando \var{oldmask} durante l'esecuzione di
-\func{pselect} la ricezione del segnale sarà abilitata, ed in caso di
-interruzione si potranno eseguire le relative operazioni.
+contestualmente all'esecuzione della funzione,\footnote{in Linux però non è
+  presente la relativa system call, e la funzione è implementata nelle
+  \acr{glibc} attraverso \func{select} (vedi \texttt{man select\_tut}) per cui
+  la possibilità di race condition permane; esiste però una soluzione,
+  chiamata \index{\textit{self-pipe trick}}\textit{self-pipe trick}, che
+  consiste nell'aprire una pipe (vedi sez.~\ref{sec:ipc_pipes}) ed usare
+  \func{select} sul capo in lettura della stessa, e indicare l'arrivo di un
+  segnale scrivendo sul capo in scrittura all'interno del manipolatore; in
+  questo modo anche se il segnale va perso prima della chiamata di
+  \func{select} questa lo riconoscerà comunque dalla presenza di dati sulla
+  pipe.} ribloccandolo non appena essa ritorna, così che il precedente codice
+potrebbe essere riscritto nel seguente modo:
+\includecodesnip{listati/pselect_norace.c} in questo caso utilizzando
+\var{oldmask} durante l'esecuzione di \func{pselect} la ricezione del segnale
+sarà abilitata, ed in caso di interruzione si potranno eseguire le relative
+operazioni.
 
 
 
@@ -315,12 +323,12 @@ cui prototipo 
 \begin{prototype}{sys/poll.h}
   {int poll(struct pollfd *ufds, unsigned int nfds, int timeout)}
   
-  La funzione attende un cambiamento di stato per uno dei file descriptor
-  specificati da \param{ufds}.
+  La funzione attende un cambiamento di stato su un insieme di file
+  descriptor.
   
   \bodydesc{La funzione restituisce il numero di file descriptor con attività
-    in caso di successo, o 0 se c'è stato un timeout; in caso di errore viene
-    restituito -1 ed \var{errno} assumerà uno dei valori:
+    in caso di successo, o 0 se c'è stato un timeout e -1 in caso di errore,
+    ed in quest'ultimo caso \var{errno} assumerà uno dei valori:
   \begin{errlist}
   \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno
     degli insiemi.
@@ -336,8 +344,8 @@ file descriptor, specificati attraverso il puntatore \param{ufds} ad un
 vettore di strutture \struct{pollfd}.  Come con \func{select} si può
 interrompere l'attesa dopo un certo tempo, questo deve essere specificato con
 l'argomento \param{timeout} in numero di millisecondi: un valore negativo
-indica un'attesa indefinita, mentre un valore comporta il ritorno immediato (e
-può essere utilizzato per impiegare \func{poll} in modalità
+indica un'attesa indefinita, mentre un valore nullo comporta il ritorno
+immediato (e può essere utilizzato per impiegare \func{poll} in modalità
 \textsl{non-bloccante}).
 
 \begin{figure}[!htb]
@@ -536,7 +544,7 @@ effettuare in contemporanea le operazioni di calcolo e quelle di I/O.
 
 Benché la modalità di apertura asincrona di un file possa risultare utile in
 varie occasioni (in particolar modo con i socket\index{socket} e gli altri
-file per i quali le funzioni di I/O sono \index{system call lente}system call
+file per i quali le funzioni di I/O sono \index{system~call~lente}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 una interfaccia apposita per l'I/O
@@ -732,7 +740,7 @@ compiuta dalla funzione \func{aio\_fsync}, che ha lo stesso effetto della
 analoga \func{fsync}, ma viene eseguita in maniera asincrona; il suo prototipo
 è:
 \begin{prototype}{aio.h}
-{ssize\_t aio\_return(int op, struct aiocb *aiocbp)} 
+{int aio\_fsync(int op, struct aiocb *aiocbp)} 
 
 Richiede la sincronizzazione dei dati per il file indicato da \param{aiocbp}.
   
@@ -978,7 +986,7 @@ file in una sezione dello spazio di indirizzi del processo. Il meccanismo 
 illustrato in fig.~\ref{fig:file_mmap_layout}, una sezione del file viene
 riportata direttamente nello spazio degli indirizzi del programma. Tutte le
 operazioni su questa zona verranno riportate indietro sul file dal meccanismo
-della memoria virtuale\index{memoria virtuale} che trasferirà il contenuto di
+della memoria virtuale\index{memoria~virtuale} che trasferirà il contenuto di
 quel segmento sul file invece che nella swap, per cui si può parlare tanto di
 file mappato in memoria, quanto di memoria mappata su file.
 
@@ -999,7 +1007,7 @@ memoria solo le parti del file che sono effettivamente usate ad un dato
 istante.
 
 Infatti, dato che l'accesso è fatto direttamente attraverso la memoria
-virtuale,\index{memoria virtuale} la sezione di memoria mappata su cui si
+virtuale,\index{memoria~virtuale} la sezione di memoria mappata su cui si
 opera sarà a sua volta letta o scritta sul file una pagina alla volta e solo
 per le parti effettivamente usate, il tutto in maniera completamente
 trasparente al processo; l'accesso alle pagine non ancora caricate avverrà
@@ -1118,8 +1126,8 @@ tab.~\ref{tab:file_mmap_flag}.
                              riportati sul file. Ne viene fatta una copia
                              privata cui solo il processo chiamante ha
                              accesso.  Le modifiche sono mantenute attraverso
-                             il meccanismo del 
-                             \textit{copy on write}\index{copy on write} e
+                             il meccanismo del \textit{copy on
+                               write}\index{\textit{copy~on~write}} e 
                              salvate su swap in caso di necessità. Non è
                              specificato se i cambiamenti sul file originale
                              vengano riportati sulla regione
@@ -1130,8 +1138,9 @@ tab.~\ref{tab:file_mmap_flag}.
                              dovevano fallire con \errcode{ETXTBSY}).\\
     \const{MAP\_EXECUTABLE}& Ignorato. \\
     \const{MAP\_NORESERVE} & Si usa con \const{MAP\_PRIVATE}. Non riserva
-                             delle pagine di swap ad uso del meccanismo di
-                             \textit{copy on write}\index{copy on write}
+                             delle pagine di swap ad uso del meccanismo del
+                             \textit{copy on
+                               write}\index{\textit{copy~on~write}}
                              per mantenere le
                              modifiche fatte alla regione mappata, in
                              questo caso dopo una scrittura, se non c'è più
@@ -1161,7 +1170,7 @@ tab.~\ref{tab:file_mmap_flag}.
 Gli effetti dell'accesso ad una zona di memoria mappata su file possono essere
 piuttosto complessi, essi si possono comprendere solo tenendo presente che
 tutto quanto è comunque basato sul basato sul meccanismo della memoria
-virtuale.\index{memoria virtuale} Questo comporta allora una serie di
+virtuale.\index{memoria~virtuale} Questo comporta allora una serie di
 conseguenze. La più ovvia è che se si cerca di scrivere su una zona mappata in
 sola lettura si avrà l'emissione di un segnale di violazione di accesso
 (\const{SIGSEGV}), dato che i permessi sul segmento di memoria relativo non
@@ -1246,12 +1255,12 @@ consentita la scrittura sul file (cio
 o in corrispondenza di una eventuale \func{msync}.
 
 Dato per i file mappati in memoria le operazioni di I/O sono gestite
-direttamente dalla memoria virtuale, occorre essere consapevoli delle
-interazioni che possono esserci con operazioni effettuate con l'interfaccia
-standard dei file di cap.~\ref{cha:file_unix_interface}. Il problema è che una
-volta che si è mappato un file, le operazioni di lettura e scrittura saranno
-eseguite sulla memoria, e riportate su disco in maniera autonoma dal sistema
-della memoria virtuale.
+direttamente dalla \index{memoria~virtuale}memoria virtuale, occorre essere
+consapevoli delle interazioni che possono esserci con operazioni effettuate
+con l'interfaccia standard dei file di cap.~\ref{cha:file_unix_interface}. Il
+problema è che una volta che si è mappato un file, le operazioni di lettura e
+scrittura saranno eseguite sulla memoria, e riportate su disco in maniera
+autonoma dal sistema della memoria virtuale.
 
 Pertanto se si modifica un file con l'interfaccia standard queste modifiche
 potranno essere visibili o meno a seconda del momento in cui la memoria
@@ -1360,16 +1369,16 @@ diversi. In quell'occasione si 
 in \textit{append mode}, quando più processi scrivono contemporaneamente sullo
 stesso file non è possibile determinare la sequenza in cui essi opereranno.
 
-Questo causa la possibilità di race condition\index{race condition}; in
-generale le situazioni più comuni sono due: l'interazione fra un processo che
-scrive e altri che leggono, in cui questi ultimi possono leggere informazioni
-scritte solo in maniera parziale o incompleta; o quella in cui diversi
-processi scrivono, mescolando in maniera imprevedibile il loro output sul
-file.
+Questo causa la possibilità di race condition\index{\textit{race~condition}};
+in generale le situazioni più comuni sono due: l'interazione fra un processo
+che scrive e altri che leggono, in cui questi ultimi possono leggere
+informazioni scritte solo in maniera parziale o incompleta; o quella in cui
+diversi processi scrivono, mescolando in maniera imprevedibile il loro output
+sul file.
 
 In tutti questi casi il \textit{file locking} è la tecnica che permette di
-evitare le race condition\index{race condition}, attraverso una serie di
-funzioni che permettono di bloccare l'accesso al file da parte di altri
+evitare le race condition\index{\textit{race~condition}}, attraverso una serie
+di funzioni che permettono di bloccare l'accesso al file da parte di altri
 processi, così da evitare le sovrapposizioni, e garantire la atomicità delle
 operazioni di scrittura.
 
@@ -1619,8 +1628,8 @@ essa viene usata solo secondo il prototipo:
     \item[\errcode{EDEADLK}] Si è richiesto un lock su una regione bloccata da
       un altro processo che è a sua volta in attesa dello sblocco di un lock
       mantenuto dal processo corrente; si avrebbe pertanto un
-      \textit{deadlock}\index{deadlock}. Non è garantito che il sistema
-      riconosca sempre questa situazione.
+      \textit{deadlock}\index{\textit{deadlock}}. Non è garantito che il
+      sistema riconosca sempre questa situazione.
     \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale prima
       di poter acquisire un lock.
     \end{errlist}
@@ -1739,7 +1748,8 @@ stato effettivamente acquisito.
 
 \begin{figure}[htb]
   \centering \includegraphics[width=9cm]{img/file_lock_dead}
-  \caption{Schema di una situazione di \textit{deadlock}\index{deadlock}.}
+  \caption{Schema di una situazione di
+    \textit{deadlock}\index{\textit{deadlock}}.}
   \label{fig:file_flock_dead}
 \end{figure}
 
@@ -1751,11 +1761,12 @@ un lock sulla regione 2 che non pu
 del processo 2; il processo 1 si bloccherà fintanto che il processo 2 non
 rilasci il blocco. Ma cosa accade se il processo 2 nel frattempo tenta a sua
 volta di ottenere un lock sulla regione A? Questa è una tipica situazione che
-porta ad un \textit{deadlock}\index{deadlock}, dato che a quel punto anche il
-processo 2 si bloccherebbe, e niente potrebbe sbloccare l'altro processo. Per
-questo motivo il kernel si incarica di rilevare situazioni di questo tipo, ed
-impedirle restituendo un errore di \errcode{EDEADLK} alla funzione che cerca
-di acquisire un lock che porterebbe ad un \textit{deadlock}.
+porta ad un \textit{deadlock}\index{\textit{deadlock}}, dato che a quel punto
+anche il processo 2 si bloccherebbe, e niente potrebbe sbloccare l'altro
+processo. Per questo motivo il kernel si incarica di rilevare situazioni di
+questo tipo, ed impedirle restituendo un errore di \errcode{EDEADLK} alla
+funzione che cerca di acquisire un lock che porterebbe ad un
+\textit{deadlock}.
 
 \begin{figure}[!bht]
   \centering \includegraphics[width=13cm]{img/file_posix_lock}