X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=fileadv.tex;h=de7ae5d50ae1aa153e836965c4ffd8691b579a3a;hp=f4ccd79ade39d6c3a482d7f16498e362c9b5a368;hb=42701ce83e63097c7de382574ebe0074a65ed9b4;hpb=8e5a44367b043bfbebc67fb39a89d523076953af diff --git a/fileadv.tex b/fileadv.tex index f4ccd79..de7ae5d 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -59,18 +59,17 @@ da quanto si otterrebbe dal file descriptor ``\textsl{disponibile}'') si potrebbe addirittura arrivare ad un \textit{deadlock}\index{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{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} @@ -127,7 +126,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 +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 +179,23 @@ 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. 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 è @@ -269,13 +269,10 @@ 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 +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 @@ -291,15 +288,24 @@ condition,\index{race condition} perch 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 +321,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 +342,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]