X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=fileadv.tex;h=2d000d8baa659e3a7e35f4bae109e3203262625b;hp=fb516a3179faf40307aa38ef7400eef1db4df549;hb=17bae834f41549575a11ddf444d97de8d575269e;hpb=65cdd0498d1d9473110f86ebddede62c298e60dd diff --git a/fileadv.tex b/fileadv.tex index fb516a3..2d000d8 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -12,73 +12,88 @@ \label{cha:file_advanced} In questo capitolo affronteremo le tematiche relative alla gestione avanzata -dei file, che non sono state trattate in \capref{cha:file_unix_interface}, -dove ci si è limitati ad una panoramica delle funzioni base. In particolare -tratteremo delle funzioni di input/output avanzato e del \textit{file - locking}. +dei file. In particolare tratteremo delle funzioni di input/output avanzato, +che permettono una gestione più sofisticata dell'I/O su file, a partire da +quelle che permettono di gestire l'accesso contemporaneo a più file, per +concludere con la gestione dell'I/O mappato in memoria. Dedicheremo poi la +fine del capitolo alle problematiche del \textit{file locking}. -\section{Le funzioni di I/O avanzato} -\label{sec:file_advanced_io} +\section{L'\textit{I/O multiplexing}} +\label{sec:file_multiplexing} -In questa sezione esamineremo le funzioni che permettono una gestione più -sofisticata dell'I/O su file, a partire da quelle che permettono di gestire -l'accesso contemporaneo a più file, per concludere con la gestione dell'I/O -mappato in memoria. +Uno dei problemi che si presentano quando si deve operare contemporaneamente +su molti file usando le funzioni illustrate in +\capref{cha:file_unix_interface} e \capref{cha:files_std_interface} è che si +può essere bloccati nelle operazioni su un file mentre un altro potrebbe +essere disponibile. L'\textit{I/O multiplexing} nasce risposta a questo +problema. In questa sezione forniremo una introduzione a questa problematica +ed analizzeremo le varie funzioni usate per implementare questa modalità di +I/O. -\subsection{La modalità di I/O \textsl{non-bloccante}} +\subsection{La problematica dell'\textit{I/O multiplexing}} \label{sec:file_noblocking} Abbiamo visto in \secref{sec:sig_gen_beha}, affrontando la suddivisione fra -\textit{fast} e \textit{slow} system call, 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. +\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. 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 essi 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 -\textit{deadlock}\index{deadlock}. +affrontare nelle operazioni di I/O, che si verifica quando si deve operare con +più file descriptor eseguendo funzioni che possono bloccarsi senza che sia +possibile prevedere quando questo può avvenire (il caso più classico è quello +di un server in attesa di dati in ingresso da vari client). Quello che può +accadere è di restare bloccati nell'eseguire una operazione su un file +descriptor che non è ``\textsl{pronto}'', quando ce ne potrebbe essere +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}. Abbiamo già accennato in \secref{sec:file_open} che è possibile prevenire -questo tipo di comportamento aprendo un file in modalità -\textsl{non-bloccante}, attraverso l'uso del flag \const{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 \errcode{EAGAIN}. +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. 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. - - - -\subsection{L'I/O multiplexing} -\label{sec:file_multiplexing} - -Per superare il problema di dover usare il \textit{polling}\index{polling} per -controllare la possibilità di effettuare operazioni su un gruppo di file -aperti in modalità non bloccante, sia BSD che System V hanno introdotto delle -nuove funzioni in grado di sospendere l'esecuzione di un processo fin quando -l'accesso ad un dato insieme di file 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}\index{socket}, compreso le varianti di System - V.} con la funzione \funcd{select}, il cui prototipo è: +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 +controllo più file descriptor in contemporanea, permettendo di bloccare un +processo quando le operazioni volute non sono possibili, e di riprenderne +l'esecuzione una volta che almeno una di quelle richieste sia disponibile, in +modo da poterla eseguire con la sicurezza di non restare bloccati. + +Dato che, come abbiamo già accennato, per i normali file su disco non si ha +mai un accesso bloccante, l'uso più comune delle funzioni che esamineremo nei +prossimi paragrafi è per i server di rete, in cui esse vengono utilizzate per +tenere sotto controllo dei socket; pertanto ritorneremo su di esse con +ulteriori dettagli e qualche esempio in \secref{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 è: \begin{functions} \headdecl{sys/time.h} \headdecl{sys/types.h} @@ -94,9 +109,10 @@ introdurre questa modalit caso \var{errno} assumerà uno dei valori: \begin{errlist} \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno - degli insiemi. + degli insiemi. \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale. - \item[\errcode{EINVAL}] Si è specificato per \param{n} un valore negativo. + \item[\errcode{EINVAL}] Si è specificato per \param{n} un valore negativo o + un valore non valido per \param{timeout}. \end{errlist} ed inoltre \errval{ENOMEM}. } @@ -111,8 +127,8 @@ degli insiemi specificati (\param{readfds}, \param{writefds} e 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 +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} @@ -146,10 +162,13 @@ dar luogo a comportamenti non prevedibili. 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:TCP_urgent_data}). +effettuare una lettura,\footnote{per essere precisi la funzione ritornerà in + tutti i casi in cui la successiva esecuzione di \func{read} risulti non + bloccante, quindi anche in caso di \textit{end-of-file}.} il secondo, +\param{writefds}, per verificare la possibilità effettuare una scrittura ed il +terzo, \param{exceptfds}, per verificare l'esistenza di eccezioni (come i +messaggi urgenti su un \textit{socket}\index{socket}, vedi +\secref{sec:TCP_urgent_data}). Dato che in genere non si tengono mai sotto controllo fino a \const{FD\_SETSIZE} file contemporaneamente la funzione richiede di @@ -204,35 +223,122 @@ numero massimo di 1024 file descriptor per processo, adesso che il numero pu essere arbitario si viene a creare una dipendenza del tutto artificiale dalle dimensioni della struttura \type{fd\_set}, che può necessitare di essere estesa, con ulteriori perdite di prestazioni. -Per questo System V, invece di utilizzare l'interfaccia di \func{select}, che -è una estensione creata nello sviluppo di BSD, ha introdotto una sua -interfaccia per gestire l'\textit{I/O multiplexing}, basata sulla funzione -\funcd{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 - \acr{libc} 5.4.28.} il cui prototipo è: + +Lo standard POSIX è rimasto a lungo senza primitive per l'\textit{I/O + multiplexing}, introdotto solo con le ultime revisioni dello standard (POSIX +1003.1g-2000 e POSIX 1003.1-2001). La scelta è stata quella di seguire +l'interfaccia creata da BSD, ma prevede che tutte le funzioni ad esso relative +vengano dichiarate nell'header \file{sys/select.h}, che sostituisce i +precedenti, ed inoltre aggiunge a \func{select} una nuova funzione +\funcd{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.1. 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} assumerà uno dei valori: + \begin{errlist} + \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno + degli insiemi. + \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale. + \item[\errcode{EINVAL}] Si è specificato per \param{n} un valore negativo o + un valore non valido per \param{timeout}. + \end{errlist} + ed inoltre \errval{ENOMEM}.} +\end{prototype} + +La funzione è sostanzialmente identica a \func{select}, solo che usa una +struttura \struct{timespec} (vedi \figref{fig:sys_timeval_struct}) 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\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 \secref{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 +può fare conto sul fatto che all'arrivo di un segnale essa verrebbe interrotta +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. + +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 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. + + + +\subsection{La funzione \func{poll}} +\label{sec:file_poll} + +Nello sviluppo di System V, invece di utilizzare l'interfaccia di +\func{select}, che è una estensione tipica di BSD, è stata introdotta un'altra +interfaccia, basata sulla funzione \funcd{poll},\footnote{la funzione è + prevista dallo standard XPG4, ed è stata introdotta in Linux come system + call a partire dal kernel 2.1.23 ed inserita nelle \acr{libc} 5.4.28.} il +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}. -\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: + \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: \begin{errlist} \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno degli insiemi. \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale. + \item[\errcode{EINVAL}] Il valore di \param{nfds} eccede il limite + \macro{RLIMIT\_NOFILE}. \end{errlist} ed inoltre \errval{EFAULT} e \errval{ENOMEM}.} \end{prototype} -La funzione tiene sotto controllo un numero \param{ndfs} di file descriptor -specificati attraverso un vettore di puntatori a strutture \struct{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 millisecondi (un valore -negativo indica un'attesa indefinita). +La funzione permette di tenere sotto controllo contemporaneamente \param{ndfs} +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à +\textsl{non-bloccante}). \begin{figure}[!htb] \footnotesize \centering @@ -245,12 +351,26 @@ negativo indica un'attesa indefinita). \label{fig:file_pollfd} \end{figure} -Per ciascun file da controllare deve essere opportunamente predisposta una -struttura \struct{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). +Per ciascun file da controllare deve essere inizializzata una struttura +\struct{pollfd} nel vettore indicato dall'argomento \param{ufds}. La +struttura, la cui definizione è riportata in \figref{fig:file_pollfd}, prevede +tre campi: in \var{fd} deve essere indicato il numero del file descriptor da +controllare, in \var{events} deve essere specificata una maschera binaria di +flag che indichino il tipo di evento che si vuole controllare, mentre in +\var{revents} il kernel restituirà il relativo risultato. Usando un valore +negativo per \param{fd} la corrispondente struttura sarà ignorata da +\func{poll}. Dato che i dati in ingresso sono del tutto indipendenti da quelli +in uscita (che vengono restituiti in \var{revents}) non è necessario +reinizializzare tutte le volte il valore delle strutture \struct{pollfd} a +meno di non voler cambiare qualche condizione. + +Le costanti che definiscono i valori relativi ai bit usati nelle maschere +binarie dei campi \var{events} e \var{revents} sono riportati in +\tabref{tab:file_pollfd_flags}, insieme al loro significato. Le si sono +suddivise in tre gruppi, nel primo gruppo si sono indicati i bit utilizzati +per controllare l'attività in ingresso, nel secondo quelli per l'attività in +uscita, mentre il terzo gruppo contiene dei valori che vengono utilizzati solo +nel campo \var{revents} per notificare delle condizioni di errore. \begin{table}[htb] \centering @@ -260,22 +380,20 @@ vengono utilizzati solo per \var{revents} come valori in uscita). \textbf{Flag} & \textbf{Significato} \\ \hline \hline - \const{POLLIN} & È possibile la lettura immediata.\\ - \const{POLLPRI} & Sono presenti dati urgenti.\\ + \const{POLLIN} & È possibile la lettura.\\ + \const{POLLRDNORM}& Sono disponibili in lettura dati normali.\\ + \const{POLLRDBAND}& Sono disponibili in lettura dati prioritari. \\ + \const{POLLPRI} & È possibile la lettura di dati urgenti.\\ + \hline \const{POLLOUT} & È possibile la scrittura immediata.\\ + \const{POLLWRNORM}& È possibile la scrittura di dati normali. \\ + \const{POLLWRBAND}& È possibile la scrittura di dati prioritari. \\ \hline \const{POLLERR} & C'è una condizione di errore.\\ \const{POLLHUP} & Si è verificato un hung-up.\\ \const{POLLNVAL} & Il file descriptor non è aperto.\\ \hline - \const{POLLRDNORM}& Sono disponibili in lettura dati normali.\\ - \const{POLLRDBAND}& Sono disponibili in lettura dati ad alta - priorità. \\ - \const{POLLWRNORM}& È possibile la scrittura di dati normali. \\ - \const{POLLWRBAND}& È possibile la scrittura di dati ad - alta priorità. \\ - \const{POLLMSG} & Un segnale \const{SIGPOLL} è arrivato alla - cima dello stream (non usato).\\ + \const{POLLMSG} & Definito per compatobilità con SysV.\\ \hline \end{tabular} \caption{Costanti per l'identificazione dei vari bit dei campi @@ -283,86 +401,53 @@ vengono utilizzati solo per \var{revents} come valori in uscita). \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 od un errore. Lo stato dei file -all'uscita della funzione viene restituito nel campo \var{revents} della -relativa struttura \struct{pollfd}, che viene impostato 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}, introdotto solo 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 -\funcd{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.1. 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} assumerà uno dei valori: - \begin{errlist} - \item[\errcode{EBADF}] Si è specificato un file descriptor sbagliato in uno - degli insiemi. - \item[\errcode{EINTR}] La funzione è stata interrotta da un segnale. - \item[\errcode{EINVAL}] Si è specificato per \param{n} un valore negativo. - \end{errlist} - ed inoltre \errval{ENOMEM}.} -\end{prototype} - -La funzione è sostanzialmente identica a \func{select}, solo che usa una -struttura \struct{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\index{race condition} resta.} quando si -deve eseguire un test su una variabile assegnata da un gestore 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\index{race condition} -diventa superabile disabilitando il segnale prima del test e riabilitandolo -poi grazie all'uso di \param{sigmask}. - -Dato che l'I/O multiplexing serve a risolvere il problema di dover attendere -la disponibilità di accesso ad un insieme di file, esso viene utilizzato -prevalentemente per programmi in cui l'accesso ad un file descriptor può -essere bloccante. Abbiamo già accennato come questo non avvenga mai per i -normali file su disco; l'uso più comune di queste funzioni infatti è nei -server di rete, in cui esse vengono utilizzate per tenere sotto controllo vari -socket; pertanto ritorneremo su di esse con maggiori dettagli e con qualche -esempio in \secref{sec:TCP_sock_multiplexing}. - - - -\subsection{L'I/O asincrono} -\label{sec:file_asyncronous_io} - -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. +Il valore \const{POLLMSG} non viene utilizzato ed è definito solo per +compatibilità con l'implementazione di SysV che usa gli +\textit{stream};\footnote{essi sono una interfaccia specifica di SysV non + presente in Linux, e non hanno nulla a che fare con i file \textit{stream} + delle librerie standard del C.} è da questi che derivano i nomi di alcune +costanti, in quanto per essi sono definite tre classi di dati: +\textsl{normali}, \textit{prioritari} ed \textit{urgenti}. In Linux la +distinzione ha senso solo per i dati \textit{out-of-band} dei socket (vedi +\secref{sec:TCP_urgent_data}), ma su questo e su come \func{poll} reagisce +alle varie condizioni dei socket torneremo in \secref{sec:TCP_serv_poll}, dove +vedremo anche un esempio del suo utilizzo. Si tenga conto comunque che le +costanti relative ai diversi tipi di dati (come \macro{POLLRDNORM} e +\macro{POLLRDBAND}) sono utilizzabili soltanto qualora si sia definito +\macro{\_XOPEN\_SOURCE}.\footnote{e ci si ricordi di farlo sempre in testa al + file, definirla soltanto prima di includere \file{sys/poll.h} non è + sufficiente.} + +In caso di successo funzione ritorna restituendo il numero di file (un valore +positivo) per i quali si è verificata una delle condizioni di attesa richieste +o per i quali si è verificato un errore (nel qual caso vengono utilizzati i +valori di \tabref{tab:file_pollfd_flags} esclusivi di \var{revents}). Un +valore nullo indica che si è raggiunto il timeout, mentre un valore negativo +indica un errore nella chiamata, il cui codice viene riportato al solito +tramite \var{errno}. + + +%\subsection{L'interfaccia di \textit{epoll}} +%\label{sec:file_epoll} +% placeholder ... + +%da fare + +\section{L'accesso \textsl{asincrono} ai file} +\label{sec:file_asyncronous_access} + +Benché l'\textit{I/O multiplexing} sia stata la prima, e sia tutt'ora una fra +le più diffuse modalità di gestire l'I/O in situazioni complesse in cui si +debba operare su più file contemporaneamente, esistono altre modalità di +gestione delle stesse problematiche. In particolare sono importanti in questo +contesto le modalità di accesso ai file eseguibili in maniera +\textsl{asincrona}, senza cioè che un processo debba bloccarsi, ed utilizzi +invece un meccanismo di notifica asincrono, come un segnale, per rilevare la +possibilità di eseguire le operazioni volute. + + +\subsection{Operazioni asincrone sui file} +\label{sec:file_asyncronous_operation} Abbiamo accennato in \secref{sec:file_open} che è possibile, attraverso l'uso del flag \const{O\_ASYNC},\footnote{l'uso del flag di \const{O\_ASYNC} e dei @@ -370,37 +455,49 @@ del flag \const{O\_ASYNC},\footnote{l'uso del flag di \const{O\_ASYNC} e dei di Linux e BSD.} aprire un file in modalità asincrona, così come è possibile attivare in un secondo tempo questa modalità impostando questo flag attraverso l'uso di \func{fcntl} con il comando \const{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 -\const{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 \const{F\_SETOWN} di -\func{fcntl}, quale processo (o gruppo di processi) riceverà il segnale. +\secref{sec:file_fcntl}). + +In realtà in questo caso non si tratta di eseguire delle operazioni di lettura +o scrittura del file in modo asincrono (tratteremo questo, che più +propriamente è detto \textsl{I/O asincrono} in +\secref{sec:file_asyncronous_io}), quanto di un meccanismo asincrono di +notifica delle variazione dello stato del file descriptor aperto in questo +modo. + +Quello che succede è che il sistema genera un segnale (normalmente +\const{SIGIO}, ma è possibile usarne altri con il comando \const{F\_SETSIG} di +\func{fcntl}) 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 \const{F\_SETOWN} di \func{fcntl}, quale processo (o gruppo di +processi) riceverà il segnale. Se pertanto si effettuano le operazioni in +risposta alla ricezione del segnale non ci sarà più la necessità di restare +bloccati in attesa della disponibilità di accesso ai file. In questo modo si può evitare l'uso delle funzioni \func{poll} o \func{select} che, quando vengono usate con un numero molto grande di file descriptor, non -hanno buone prestazioni. In tal caso infatti la maggior parte del loro tempo +hanno buone prestazioni. % aggiungere cenno a epoll quando l'avrò scritta + In tal caso infatti la maggior parte del loro tempo di esecuzione è impegnato ad eseguire una scansione su tutti i file descriptor tenuti sotto controllo per determinare quali di essi (in genere una piccola percentuale) sono diventati attivi. Tuttavia con l'implementazione classica dei segnali questa modalità di I/O -presenta notevoli problemi, dato che non è possibile determinare, quando sono -più di uno, qual'è il file descriptor responsabile dell'emissione del segnale. -Linux però supporta le estensioni POSIX.1b dei segnali che permettono di -superare il problema facendo ricorso alle informazioni aggiuntive restituite -attraverso la struttura \struct{siginfo\_t}, utilizzando la forma estesa -\var{sa\_sigaction} del gestore (si riveda quanto illustrato in +presenta notevoli problemi, dato che non è possibile determinare, quando i +file descriptor sono più di uno, qual'è quello responsabile dell'emissione del +segnale; inoltre dato che i segnali normali non si accumulano, in presenza di +più file descriptor attivi contemporaneamente, più segnali emessi nello stesso +tempo verrebbero notificati una volta sola. Linux però supporta le estensioni +POSIX.1b dei segnali real-time, che possono accumularsi e che permettono di +riconoscere il file descriptor facendo ricorso alle informazioni aggiuntive +restituite attraverso la struttura \struct{siginfo\_t}, utilizzando la forma +estesa \var{sa\_sigaction} del gestore (si riveda quanto illustrato in \secref{sec:sig_sigaction}). Per far questo però occorre utilizzare le funzionalità dei segnali real-time (vedi \secref{sec:sig_real_time}) impostando esplicitamente con il comando \const{F\_SETSIG} di \func{fcntl} un segnale real-time da inviare in caso di I/O asincrono (il segnale predefinito è \const{SIGIO}). In questo caso il -gestore tutte le volte che riceverà \const{SI\_SIGIO} come valore del +gestore, tutte le volte che riceverà \const{SI\_SIGIO} come valore del campo \var{si\_code}\footnote{il valore resta \const{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 @@ -416,17 +513,30 @@ come \func{poll} e \func{select}, almeno fintanto che non si satura la coda; si eccedono le dimensioni di quest'ultima; in tal caso infatti il kernel, non potendo più assicurare il comportamento corretto per un segnale real-time, invierà al suo posto un \const{SIGIO}, su cui si accumuleranno tutti i segnali -in eccesso, e si dovrà determinare al solito modo quali sono i file diventati +in eccesso, e si dovrà determinare con un ciclo quali sono i file diventati attivi. + +\subsection{L'interfaccia POSIX per l'I/O asincrono} +\label{sec:file_asyncronous_io} + +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 +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. + 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 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 anche una interfaccia apposita per l'I/O -asincrono, che prevede un insieme di funzioni dedicate, completamente separate -rispetto a quelle usate normalmente. +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 +asincrono vero e proprio, che prevede un insieme di funzioni dedicate per la +lettura e la scrittura dei file, completamente separate rispetto a quelle +usate normalmente. In generale questa interfaccia è completamente astratta e può essere implementata sia direttamente nel kernel, che in user space attraverso l'uso @@ -496,7 +606,7 @@ che serve a specificare il modo in cui si vuole che venga effettuata la notifica del completamento delle operazioni richieste. La struttura è riportata in \secref{fig:file_sigevent}; il campo \var{sigev\_notify} è quello che indica le modalità della notifica, esso può assumere i tre valori: -\begin{basedescript}{\desclabelwidth{3.0cm}} +\begin{basedescript}{\desclabelwidth{2.6cm}} \item[\const{SIGEV\_NONE}] Non viene inviata nessuna notifica. \item[\const{SIGEV\_SIGNAL}] La notifica viene effettuata inviando al processo chiamante il segnale specificato da \var{sigev\_signo}; se il gestore di @@ -766,6 +876,16 @@ la notifica del completamento di tutte le richieste, impostando l'argomento di \struct{aiocb}. +\section{Altre modalità di I/O avanzato} +\label{sec:file_advanced_io} + +Oltre alle precedenti modalità di \textit{I/O multiplexing} e \textsl{I/O + asincrono}, esistono altre funzioni che implementano delle modalità di +accesso ai file più evolute rispetto alle normali funzioni di lettura e +scrittura che abbiamo esaminato in \secref{sec:file_base_func}. In questa +sezione allora prenderemo in esame le interfacce per l'\textsl{I/O + vettorizzato} e per l'\textsl{I/O mappato in memoria}. + \subsection{I/O vettorizzato} \label{sec:file_multiple_io} @@ -852,9 +972,9 @@ file in una sezione dello spazio di indirizzi del processo. Il meccanismo illustrato in \figref{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 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. +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. \begin{figure}[htb] \centering @@ -873,16 +993,16 @@ 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, 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à allo stesso modo con cui vengono -caricate in memoria le pagine che sono state salvate sullo swap. Infine in -situazioni in cui la memoria è scarsa, le pagine che mappano un file vengono -salvate automaticamente, così come le pagine dei programmi vengono scritte -sulla swap; questo consente di accedere ai file su dimensioni il cui solo -limite è quello dello spazio di indirizzi disponibile, e non della memoria su -cui possono esserne lette delle porzioni. +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à +allo stesso modo con cui vengono caricate in memoria le pagine che sono state +salvate sullo swap. Infine in situazioni in cui la memoria è scarsa, le +pagine che mappano un file vengono salvate automaticamente, così come le +pagine dei programmi vengono scritte sulla swap; questo consente di accedere +ai file su dimensioni il cui solo limite è quello dello spazio di indirizzi +disponibile, e non della memoria su cui possono esserne lette delle porzioni. L'interfaccia prevede varie funzioni per la gestione del \textit{memory mapped I/O}, la prima di queste è \funcd{mmap}, che serve ad eseguire la mappatura @@ -1035,11 +1155,19 @@ come maschera binaria ottenuta dall'OR di uno o pi 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. 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 consentono questo tipo di -accesso. +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 +consentono questo tipo di accesso. + +\begin{figure}[!htb] + \centering + \includegraphics[width=10cm]{img/mmap_boundary} + \caption{Schema della mappatura in memoria di una sezione di file di + dimensioni non corrispondenti al bordo di una pagina.} + \label{fig:file_mmap_boundary} +\end{figure} È invece assai diversa la questione relativa agli accessi al di fuori della regione di cui si è richiesta la mappatura. A prima vista infatti si potrebbe @@ -1054,15 +1182,6 @@ file non rientra nei confini di una pagina: in tal caso verr mappato su un segmento di memoria che si estende fino al bordo della pagina successiva. -\begin{figure}[htb] - \centering - \includegraphics[width=10cm]{img/mmap_boundary} - \caption{Schema della mappatura in memoria di una sezione di file di - dimensioni non corrispondenti al bordo di una pagina.} - \label{fig:file_mmap_boundary} -\end{figure} - - In questo caso è possibile accedere a quella zona di memoria che eccede le dimensioni specificate da \param{lenght}, senza ottenere un \const{SIGSEGV} poiché essa è presente nello spazio di indirizzi del processo, anche se non è