X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=fileadv.tex;h=7b3a6b10b551532e357c3828ad6cb72fbd1c5ebe;hp=6281f75cc96698557fa78bc3efa34bd4abbe5022;hb=44ef16d86264e295b1987168b99aed3190753b3e;hpb=a0fdff527cf9690c0d16c48182bda2865b79f501 diff --git a/fileadv.tex b/fileadv.tex index 6281f75..7b3a6b1 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -28,43 +28,159 @@ I/O possono bloccarsi indefinitamente.\footnote{si ricordi per esempio le operazioni di lettura possono bloccarsi quando non ci sono dati disponibili sul descrittore su cui si sta operando. -Uno dei problemi più comuni che ci si trova ad affrontare, e che non può -essere risolto con le funzioni base trattate in -\capref{cha:file_unix_interface}, è quello in cui si devono eseguire su più -file descriptor operazioni che possono bloccarsi: il problema è che mentre si -è bloccati su uno di questi file su di un'altro potrebbero essere presenti dei -dati, così che nel migliore dei casi si avrebbe una lettura inutilmente -ritardata, e nel peggiore si potrebbe addirittura arrivare ad un deadlock. - -Abbiamo già accennato in \secref{sec:file_open} che è possibile prevenire +Questo comportamento causa uno dei problemi più comuni che ci si trova ad +affrontare nelle operazioni di I/O, che è quello che si verifica quando si +devono eseguire operazioni che possono bloccarsi su più file descriptor: +mentre si è bloccati su uno di questi file su di un'altro potrebbero essere +presenti dei dati, così che nel migliore dei casi si avrebbe una lettura +ritardata inutilmente, e nel peggiore si potrebbe addirittura arrivare ad un +deadlock. + +Abbiamo già accennato in \secref{sec:file_open} che però è possibile prevenire questo tipo di comportamento aprendo un file in modalità -\textsl{non-bloccante}, specificando il flag \macro{O\_NONBLOCK} alla chiamata -di \func{open}. In questo caso le funzioni di input/output che altrimenti si -sarebbero bloccate ritornano immediatamente, restituendo l'errore -\macro{EAGAIN}. +\textsl{non-bloccante}, attraverso l'uso del flag \macro{O\_NONBLOCK} nella +chiamata di \func{open}. In questo caso le funzioni di input/output che +altrimenti si sarebbero bloccate ritornano immediatamente, restituendo +l'errore \macro{EAGAIN}. -L'utilizzo di questa modalità di I/O permette allora di risolvere il problema +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}, è estremamente inefficiente: si tiene costantemente impiegata la CPU solo per eseguire in continuazione delle system call che -nella gran parte dei casi falliranno. +nella gran parte dei casi falliranno. Per evitare questo, come vedremo in +\secref{sec:file_multiplexing}, è stata introdotta una nuova interfaccia di +programmazione, che comporta comunque l'uso della modalità di I/O non +bloccante. + -Per questo motivo, quando come vedremo in dettaglio in -\secref{sec:file_multiplexing}, il sistema fornisce delle funzioni apposite -che permettono di aggirare questo problema, permettendo di attendere fino alla -disponibilità di un accesso; per usarle però è comunque comunque necessario -utilizzare la modalità di I/O non bloccante. \subsection{Le funzioni \func{poll} e \func{select}} \label{sec:file_multiplexing} +Per superare il problema di dover usare il \textit{polling} per controllare la +possibilità di effettuare operazioni su un file aperto in modalità non +bloccante, sia BSD che System V hanno introdotto delle nuove funzioni in grado +di sospendere l'esecuzione di un processo in attesa che l'accesso diventi +possibile. Il primo ad introdurre questa modalità di operazione, chiamata +usualmente \textit{I/O multiplexing}, è stato BSD,\footnote{la funzione è + 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)} + + 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 mette il processo in stato di \textit{sleep} (vedi +\tabref{tab:proc_proc_states}) fintanto che almeno uno dei file descriptor +degli insiemo specificati (\param{readfds}, \param{writefds} e +\param{exceptfds}), non diventa attivo, per un tempo massimo specificato da +\param{timeout}. + +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 +\secref{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: +\begin{functions} + \headdecl{sys/select.h} + \funcdecl{FD\_ZERO(fd\_set *set)} + Inizializza l'insieme (vuoto). + + \funcdecl{FD\_SET(int fd, fd\_set *set)} + Inserisce il file descriptor \param{fd} nell'insieme. + + \funcdecl{FD\_CLR(int fd, fd\_set *set)} + Rimuove il file descriptor \param{fd} nell'insieme. + + \funcdecl{FD\_ISSET(int fd, fd\_set *set)} + Controlla se il file descriptor \param{fd} è nell'insieme. +\end{functions} + +In genere un \textit{file descriptor set} può contenere fino ad un massimo di +\macro{FD\_SETSIZE} file descriptor. Questo valore in origine corrispondeva +al limite per il numero massimo di file aperti\footnote{ad esempio in Linux, + fino alla serie 2.0.x, c'era un limite di 256 file per processo.}, ma +quando, come nelle versioni più recenti del kernel, non c'è più un limite +massimo, esso indica le dimensioni massime dei numeri usati nei \textit{file + descriptor set}. + +La funzione richiede di specificare tre insiemi distinti di file descriptor; +il primo, \param{readfds}, verrà osservato per rilevare la disponibilità di +effettuare una lettura, il secondo, \param{writefds}, per verificare la +possibilità effettuare una scrittura ed il terzo, \param{exceptfds}, per +verificare l'esistenza di condizioni eccezionali (come i messaggi urgenti su +un \textit{socket}\index{socket}, vedi \secref{sec:xxx_urgent}). + +La funzione inoltre richiede anche di specificare, tramite l'argomento +\param{n}, un valore massimo del numero dei file descriptor usati +nell'insieme; si può usare il già citato \macro{FD\_SETSIZE}, oppure il numero +più alto dei file descriptor usati nei tre insiemi, aumentato di uno. + +Infine l'argomento \param{timeout}, specifica un tempo massimo di +attesa\footnote{il tempo è valutato come \textit{elapsed time}.} prima che la +funzione ritorni; se settato a \macro{NULL} la funzione attende +indefinitamente. Si può specificare anche un tempo nullo (cioè una \var{struct + timeval} con i campi settati a zero), qualora si voglia semplicemente +controllare lo stato corrente dei file descriptor. + +La funzione restituisce il totale dei file descriptor pronti nei tre insiemi, +il valore zero indica sempre che si è raggiunto un timeout. Ciascuno dei tre +insiemi viene sovrascritto per indicare quale file descriptor è pronto per le +operazioni ad esso relative, in modo da poterlo controllare con la macro +\macro{FD\_ISSET}. In caso di errore la funzione restituisce -1 e gli insiemi +non vengono toccati. + +In Linux \func{select} modifica anche il valore di \param{timeout}, settandolo +al tempo restante; questo è utile quando la funzione viene interrotta da un +segnale, in tal caso infatti si ha un errore di \macro{EINTR}, ed occorre +rilanciare la funzione; in questo modo non è necessario ricalcolare tutte le +volte il tempo rimanente.\footnote{questo però può causare problemi di + portabilità sia quando si trasporta codice scritto su Linux che legge questo + valore, sia quando si usano programmi scritti per altri sistemi che non + dispongono di questa caratteristica e ricalcolano \param{timeout} tutte le + volte. In genere la caratteristica è disponibile nei sistemi che derivano da + 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 è: +\begin{prototype}{sys/poll.h} + {int poll(struct pollfd *ufds, unsigned int nfds, int timeout)} + +La funzione attente un cambiamento di stato per uno dei file descriptor +specificati da \param{ufds}. + +\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} 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. + \end{errlist} + ed inoltre \macro{EFAULT} e \macro{ENOMEM}.} +\end{prototype} -%\section{I/O asincrono} -%\label{sec:file_asynchronous} -%Non supportato in Linux, in BSD e SRv4 c'è, ma usando il segnale \macro{SIGIO} -%per indicare che i dati sono disponibili, \subsection{L'I/O asincrono} \label{sec:file_asyncronous_io} @@ -76,16 +192,17 @@ modalit di \func{fcntl}. In tal caso il sistema genera un segnale \macro{SIGIO} tutte le volte che sono -presenti dei dati in input sul file. - - - -Uno dei problemi che si presentavano con le prime implementazioni di questa -modalità di I/O è che essa poteva essere usata in maniera semplice con un solo -file per processo, dato che altrimenti non sarebbe stato distinguere da quale -file provieniva l'attività che ha causato l'emissione del segnale. - +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. @@ -101,17 +218,18 @@ file provieniva l'attivit \section{Il file locking} \label{sec:file_locking} -In \secref{sec:file_sharing} abbiamo preso in esame le mosalità in cui un +In \secref{sec:file_sharing} abbiamo preso in esame le modalità in cui un sistema unix-like gestisce la condivisione dei file da parte di processi diversi. In quell'occasione si è visto come, con l'eccezione dei file aperti 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; 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 imprevedebile il loro output sul file. +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. In tutti questi casi il \textit{file locking} è la tecnica che permette di evitare le race condition, attraverso una serie di funzioni che permettono di