Riscritta (meglio) select
[gapil.git] / fileadv.tex
1 \chapter{La gestione avanzata dei file}
2 \label{cha:file_advanced}
3
4 In questo capitolo affronteremo le tematiche relative alla gestione avanzata
5 dei file, che non sono state trattate in \capref{cha:file_unix_interface},
6 dove ci si è limitati ad una panoramica delle funzioni base. In particolare
7 tratteremo delle funzioni di input/output avanzato e del \textit{file
8   locking}.
9
10
11 \section{Le funzioni di I/O avanzato}
12 \label{sec:file_advanced_io}
13
14 In questa sezione esamineremo le funzioni che permettono una gestione più
15 sofisticata dell'I/O su file, a partire da quelle che permettono di gestire
16 l'accesso contemporaneo a più file, per concludere con la gestione dell'I/O
17 mappato in memoria.
18
19
20 \subsection{La modalità di I/O \textsl{non-bloccante}}
21 \label{sec:file_noblocking}
22
23 Abbiamo visto in \secref{sec:sig_gen_beha}, affrontando la suddivisione fra
24 \textit{fast} e \textit{slow} system call, che in certi casi le funzioni di
25 I/O possono bloccarsi indefinitamente.\footnote{si ricordi però che questo può
26   accadere solo per le pipe, i socket ed alcuni file di dispositivo; sui file
27   normali le funzioni di lettura e scrittura ritornano sempre subito.}  Ad
28 esempio le operazioni di lettura possono bloccarsi quando non ci sono dati
29 disponibili sul descrittore su cui si sta operando.
30
31 Questo comportamento causa uno dei problemi più comuni che ci si trova ad
32 affrontare nelle operazioni di I/O, che è quello che si verifica quando si
33 devono eseguire operazioni che possono bloccarsi su più file descriptor:
34 mentre si è bloccati su uno di questi file su di un'altro potrebbero essere
35 presenti dei dati, così che nel migliore dei casi si avrebbe una lettura
36 ritardata inutilmente, e nel peggiore si potrebbe addirittura arrivare ad un
37 deadlock.
38
39 Abbiamo già accennato in \secref{sec:file_open} che però è possibile prevenire
40 questo tipo di comportamento aprendo un file in modalità
41 \textsl{non-bloccante}, attraverso l'uso del flag \macro{O\_NONBLOCK} nella
42 chiamata di \func{open}. In questo caso le funzioni di input/output che
43 altrimenti si sarebbero bloccate ritornano immediatamente, restituendo
44 l'errore \macro{EAGAIN}.
45
46 L'utilizzo di questa modalità di I/O permette di risolvere il problema
47 controllando a turno i vari file descriptor, in un ciclo in cui si ripete
48 l'accesso fintanto che esso non viene garantito.  Ovviamente questa tecnica,
49 detta \textit{polling}, è estremamente inefficiente: si tiene costantemente
50 impiegata la CPU solo per eseguire in continuazione delle system call che
51 nella gran parte dei casi falliranno. Per evitare questo, come vedremo in
52 \secref{sec:file_multiplexing}, è stata introdotta una nuova interfaccia di
53 programmazione, che comporta comunque l'uso della modalità di I/O non
54 bloccante.
55
56
57
58 \subsection{Le funzioni \func{poll} e \func{select}}
59 \label{sec:file_multiplexing}
60
61 Per superare il problema di dover usare il \textit{polling} per controllare la
62 possibilità di effettuare operazioni su un file aperto in modalità non
63 bloccante, sia BSD che System V hanno introdotto delle nuove funzioni in grado
64 di sospendere l'esecuzione di un processo in attesa che l'accesso diventi
65 possibile.  Il primo ad introdurre questa modalità di operazione, chiamata
66 usualmente \textit{I/O multiplexing}, è stato BSD,\footnote{la funzione è
67   apparsa in BSD4.2 e standardizzata in BSD4.4, ma è stata portata su tutti i
68   sistemi che supportano i \textit{socket}, compreso le varianti di System V.}
69 con la funzione \func{select}, il cui prototipo è:
70 \begin{prototype}{sys/select.h}
71   {int select(int n, fd\_set *readfds, fd\_set *writefds, fd\_set *exceptfds,
72     struct timeval *timeout)}
73   
74   Attende che uno dei file descriptor degli insiemi specificati diventi
75   attivo.
76   
77   \bodydesc{La funzione in caso di successo restituisce il numero di file
78     descriptor (anche nullo) che sono attivi, e -1 in caso di errore, nel qual
79     caso \var{errno} viene settata ai valori:
80   \begin{errlist}
81   \item[\macro{EBADF}] Si è specificato un file descriptor sbagliato in uno
82   degli insiemi.
83   \item[\macro{EINTR}] La funzione è stata interrotta da un segnale.
84   \item[\macro{EINVAL}] Si è specificato per \param{n} un valore negativo.
85   \end{errlist}
86   ed inoltre \macro{ENOMEM}.
87 }
88 \end{prototype}
89
90 La funzione mette il processo in stato di \textit{sleep} (vedi
91 \tabref{tab:proc_proc_states}) fintanto che almeno uno dei file descriptor
92 degli insiemo specificati (\param{readfds}, \param{writefds} e
93 \param{exceptfds}), non diventa attivo, per un tempo massimo specificato da
94 \param{timeout}.
95
96 Per specificare quali file descriptor si intende \textsl{selezionare}, la
97 funzione usa un particolare oggetto, il \textit{file descriptor set},
98 identificato dal tipo \type{fd\_set}, che serve ad identificare un insieme di
99 file descriptor, (in maniera analoga a come un \textit{signal set}, vedi
100 \secref{sec:sig_sigset}, identifica un insieme di segnali). Per la
101 manipolazione di questi \textit{file descriptor set} si possono usare delle
102 opportune macro di preprocessore:
103 \begin{functions}
104   \headdecl{sys/select.h}
105   \funcdecl{FD\_ZERO(fd\_set *set)}
106   Inizializza l'insieme (vuoto).
107
108   \funcdecl{FD\_SET(int fd, fd\_set *set)}
109   Inserisce il file descriptor \param{fd} nell'insieme.
110
111   \funcdecl{FD\_CLR(int fd, fd\_set *set)}
112   Rimuove il file descriptor \param{fd} nell'insieme.
113   
114   \funcdecl{FD\_ISSET(int fd, fd\_set *set)}
115   Controlla se il file descriptor \param{fd} è nell'insieme.
116 \end{functions}
117
118 In genere un \textit{file descriptor set} può contenere fino ad un massimo di
119 \macro{FD\_SETSIZE} file descriptor.  Questo valore in origine corrispondeva
120 al limite per il numero massimo di file aperti\footnote{ad esempio in Linux,
121   fino alla serie 2.0.x, c'era un limite di 256 file per processo.}, ma
122 quando, come nelle versioni più recenti del kernel, non c'è più un limite
123 massimo, esso indica le dimensioni massime dei numeri usati nei \textit{file
124   descriptor set}.
125
126 La funzione richiede di specificare tre insiemi distinti di file descriptor;
127 il primo, \param{readfds}, verrà osservato per rilevare la disponibilità di
128 effettuare una lettura, il secondo, \param{writefds}, per verificare la
129 possibilità effettuare una scrittura ed il terzo, \param{exceptfds}, per
130 verificare l'esistenza di condizioni eccezionali (come i messaggi urgenti su
131 un \textit{socket}\index{socket}, vedi \secref{sec:xxx_urgent}).
132
133 La funzione inoltre richiede anche di specificare, tramite l'argomento
134 \param{n}, un valore massimo del numero dei file descriptor usati
135 nell'insieme; si può usare il già citato \macro{FD\_SETSIZE}, oppure il numero
136 più alto dei file descriptor usati nei tre insiemi, aumentato di uno.
137
138 Infine l'argomento \param{timeout}, specifica un tempo massimo di
139 attesa\footnote{il tempo è valutato come \textit{elapsed time}.} prima che la
140 funzione ritorni; se settato a \macro{NULL} la funzione attende
141 indefinitamente. Si può specificare anche un tempo nullo (cioè una \var{struct
142   timeval} con i campi settati a zero), qualora si voglia semplicemente
143 controllare lo stato corrente dei file descriptor.
144
145 La funzione restituisce il totale dei file descriptor pronti nei tre insiemi,
146 il valore zero indica sempre che si è raggiunto un timeout. Ciascuno dei tre
147 insiemi viene sovrascritto per indicare quale file descriptor è pronto per le
148 operazioni ad esso relative, in modo da poterlo controllare con la macro
149 \macro{FD\_ISSET}. In caso di errore la funzione restituisce -1 e gli insiemi
150 non vengono toccati.
151
152 In Linux \func{select} modifica anche il valore di \param{timeout}, settandolo
153 al tempo restante; questo è utile quando la funzione viene interrotta da un
154 segnale, in tal caso infatti si ha un errore di \macro{EINTR}, ed occorre
155 rilanciare la funzione; in questo modo non è necessario ricalcolare tutte le
156 volte il tempo rimanente.\footnote{questo però può causare problemi di
157   portabilità sia quando si trasporta codice scritto su Linux che legge questo
158   valore, sia quando si usano programmi scritti per altri sistemi che non
159   dispongono di questa caratteristica e ricalcolano \param{timeout} tutte le
160   volte. In genere la caratteristica è disponibile nei sistemi che derivano da
161   System V e non disponibile per quelli che derivano da BSD.}
162
163 Come accennato l'interfaccia di \func{select} è una estensione di BSD; anche
164 System V ha introdotto una sua interfaccia per getire l'\textit{I/O
165   multiplexing}, basata sulla funzione \func{poll}, il cui prototipo è:
166 \begin{prototype}{sys/poll.h}
167   {int poll(struct pollfd *ufds, unsigned int nfds, int timeout)}
168
169 La funzione attente un cambiamento di stato per uno dei file descriptor
170 specificati da \param{ufds}.
171   
172 \bodydesc{La funzione restituisce il numero di file descriptor con attività in
173   caso di successo, o 0 se c'è stato un timeout; in caso di errore viene
174   restituito  -1 ed \var{errno} viene settata ai valori:
175   \begin{errlist}
176   \item[\macro{EBADF}] Si è specificato un file descriptor sbagliato in uno
177   degli insiemi.
178   \item[\macro{EINTR}] La funzione è stata interrotta da un segnale.
179   \end{errlist}
180   ed inoltre \macro{EFAULT} e \macro{ENOMEM}.}
181 \end{prototype}
182
183
184
185 \subsection{L'I/O asincrono}
186 \label{sec:file_asyncronous_io}
187
188 Una modalità alternativa all'uso dell'I/O non bloccante è quella di fare
189 ricorso all'I/O asincrono. Abbiamo accennato in \secref{sec:file_open} che è
190 possibile, attraverso l'uso del flag \macro{O\_ASYNC}, aprire un file in
191 modalità asincrona, così come è possibile settare questo flag attraverso l'uso
192 di \func{fcntl}.
193
194 In tal caso il sistema genera un segnale \macro{SIGIO} tutte le volte che sono
195 presenti dei dati in input su un file aperto in questa modalità.  Uno dei
196 problemi che si presentavano con le prime implementazioni di questa modalità
197 di I/O è che essa poteva essere usata in maniera semplice aprendo un solo file
198 per processo, dato che altrimenti si sarebbe dovuto provvedere ad effettuare
199 una serie di controlli su tutti i file aperti per distinguere a quale fosse
200 dovuto l'emissione del segnale.
201
202 Tutto questo adesso può essere evitato facendo ricorso alle informazioni
203 restituite al manipolatore del segnale attraverso la struttura
204 \var{siginfo\_t} (vedi \figref{fig:sig_siginfo_t}), il cui campo \var{si\_fd}
205 riporta il file descriptor che ha generato il segnale.
206
207
208
209 \subsection{File mappati in memoria}
210 \label{sec:file_memory_map}
211
212
213 \subsection{I/O multiplo}
214 \label{sec:file_multiple_io}
215
216
217
218 \section{Il file locking}
219 \label{sec:file_locking}
220
221 In \secref{sec:file_sharing} abbiamo preso in esame le modalità in cui un
222 sistema unix-like gestisce la condivisione dei file da parte di processi
223 diversi. In quell'occasione si è visto come, con l'eccezione dei file aperti
224 in \textit{append mode}, quando più processi scrivono contemporaneamente sullo
225 stesso file non è possibile determinare la sequenza in cui essi opereranno.
226
227 Questo causa la possibilità di race condition\index{race condition}; in
228 generale le situazioni più comuni sono due: l'interazione fra un processo che
229 scrive e altri che leggono, in cui questi ultimi possono leggere informazioni
230 scritte solo in maniera parziale o incompleta; o quella in cui diversi
231 processi scrivono, mescolando in maniera imprevedibile il loro output sul
232 file.
233
234 In tutti questi casi il \textit{file locking} è la tecnica che permette di
235 evitare le race condition, attraverso una serie di funzioni che permettono di
236 bloccare l'accesso al file da parte di altri processi, così da evitare le
237 sovrapposizioni, e garantire la atomicità delle operazioni di scrittura.
238
239
240 \subsection{L'\textit{advisory locking}}
241 \label{sec:file_record_locking}
242
243 La prima modalità di file locking che è stata implementata nei sistemi
244 unix-like è quella che viene usualmente chiamata \textit{advisory locking}, in
245 quanto è il processo, e non il sistema, che si incarica di verificare se
246 esiste una condizione di blocco per l'accesso ai file.
247
248
249
250
251 \subsection{Il \textit{mandatory locking}}
252 \label{sec:file_mand_locking}
253
254 Il \textit{mandatory locking} è una opzione introdotta inizialmente in SVr4, 
255
256
257
258
259
260
261 %%% Local Variables: 
262 %%% mode: latex
263 %%% TeX-master: "gapil"
264 %%% End: