From 503cb84c790afd8432cfe28f421f37ec72a65c4e Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Fri, 14 Feb 2014 18:49:29 +0000 Subject: [PATCH] Aggiornamenti su pselect e select --- fileadv.tex | 182 +++++++++++++++++++++++++++++----------------------- 1 file changed, 103 insertions(+), 79 deletions(-) diff --git a/fileadv.tex b/fileadv.tex index 575504e..53e4cb3 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -1004,9 +1004,10 @@ Il primo kernel unix-like ad introdurre una interfaccia per l'\textit{I/O BSD4.2 ed è stata standardizzata in BSD4.4, in seguito è stata portata su tutti i sistemi che supportano i socket, compreso le varianti di System V ed inserita in POSIX.1-2001; il suo prototipo è:\footnote{l'header - \texttt{sys/select.h} è stato introdotto con POSIX.1-2001, in precedenza - occorreva includere \texttt{sys/time.h}, \texttt{sys/types.h} e - \texttt{unistd.h}.} + \texttt{sys/select.h} è stato introdotto con POSIX.1-2001, è ed presente con + le \acr{glibc} a partire dalla versione 2.0, in precedenza, con le + \acr{libc4} e le \acr{libc5}, occorreva includere \texttt{sys/time.h}, + \texttt{sys/types.h} e \texttt{unistd.h}.} \begin{funcproto}{ \fhead{sys/select.h} @@ -1063,15 +1064,15 @@ opportune macro di preprocessore: In genere un \textit{file descriptor set} può contenere fino ad un massimo di \const{FD\_SETSIZE} file descriptor. Questo valore in origine corrispondeva al limite per il numero massimo di file aperti (ad esempio in Linux, fino alla -serie 2.0.x, c'era un limite di 256 file per processo), ma da quando, come -nelle versioni più recenti del kernel, questo limite è stato rimosso, esso -indica le dimensioni massime dei numeri usati nei \textit{file descriptor - set}, ed il suo valore, secondo lo standard POSIX 1003.1-2001, è definito in +serie 2.0.x, c'era un limite di 256 file per processo), ma da quando, nelle +versioni più recenti del kernel, questo limite è stato rimosso, esso indica le +dimensioni massime dei numeri usati nei \textit{file descriptor set}, ed il +suo valore, secondo lo standard POSIX 1003.1-2001, è definito in \headfile{sys/select.h}, ed è pari a 1024. Si tenga presente che i \textit{file descriptor set} devono sempre essere inizializzati con \macro{FD\_ZERO}; passare a \func{select} un valore non -inizializzato può dar luogo a comportamenti non prevedibili; allo stesso modo +inizializzato può dar luogo a comportamenti non prevedibili. Allo stesso modo usare \macro{FD\_SET} o \macro{FD\_CLR} con un file descriptor il cui valore eccede \const{FD\_SETSIZE} può dare luogo ad un comportamento indefinito. @@ -1079,68 +1080,88 @@ 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,\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}; inoltre con Linux - possono verificarsi casi particolari, ad esempio quando arrivano dati su un - socket dalla rete che poi risultano corrotti e vengono scartati, può - accadere che \func{select} riporti il relativo file descriptor come - leggibile, ma una successiva \func{read} si blocchi.} il secondo, + bloccante, quindi anche in caso di \textit{end-of-file}.} il secondo, \param{writefds}, per verificare la possibilità di effettuare una scrittura ed -il terzo, \param{exceptfds}, per verificare l'esistenza di eccezioni (come i -dati urgenti \itindex{out-of-band} su un socket, vedi +il terzo, \param{exceptfds}, per verificare l'esistenza di eccezioni come i +dati urgenti \itindex{out-of-band} su un socket, (vedi sez.~\ref{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 +\const{FD\_SETSIZE} file contemporaneamente, la funzione richiede di specificare qual è il valore più alto fra i file descriptor indicati nei tre insiemi precedenti. Questo viene fatto per efficienza, per evitare di passare e far controllare al kernel una quantità di memoria superiore a quella necessaria. Questo limite viene indicato tramite l'argomento \param{ndfs}, che -deve corrispondere al valore massimo aumentato di uno.\footnote{si ricordi che - i file descriptor sono numerati progressivamente a partire da zero, ed il - valore indica il numero più alto fra quelli da tenere sotto controllo; - dimenticarsi di aumentare di uno il valore di \param{ndfs} è un errore - comune.} - -Infine l'argomento \param{timeout}, espresso con una struttura di tipo -\struct{timeval} (vedi fig.~\ref{fig:sys_timeval_struct}) 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 quali sono i file descriptor pronti per le operazioni ad esso -relative, in modo da poterli controllare con \macro{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. +deve corrispondere al valore massimo aumentato di uno. Si ricordi infatti che +i file descriptor sono numerati progressivamente a partire da zero, ed il +valore indica il numero più alto fra quelli da tenere sotto controllo, +dimenticarsi di aumentare di uno il valore di \param{ndfs} è un errore comune. + +Infine l'argomento \param{timeout}, espresso con il puntatore ad una struttura +di tipo \struct{timeval} (vedi fig.~\ref{fig:sys_timeval_struct}) 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, e così può essere utilizzata eseguire il \itindex{polling} +\textit{polling} su un gruppo di file descriptor. Usare questo argomento con +tutti i \textit{file descriptor set} vuoti è un modo portabile, disponibile +anche su sistemi in cui non sono disponibili le funzioni avanzate di +sez.~\ref{sec:sig_timer_adv}, per tenere un processo in stato di +\textit{sleep} con precisioni inferiori al secondo. + +In caso di successo la funzione restituisce il numero di file descriptor +pronti, seguendo il comportamento previsto dallo standard +POSIX.1-2001,\footnote{si tenga però presente che esistono alcune versioni di + Unix che non si comportano in questo modo, restituendo un valore positivo + generico.} e ciascun insieme viene sovrascritto per indicare quali sono i +file descriptor pronti per le operazioni ad esso relative, in modo da poterli +controllare con \macro{FD\_ISSET}. Se invece scade il tempo indicato +da \param{timout} viene restituito un valore nullo e i \textit{file descriptor + set} non vengono modificati. In caso di errore la funzione restituisce -1, i +valori dei tre insiemi e di \param{timeout} sono indefiniti e non si può fare +nessun affidamento sul loro contenuto; nelle versioni più recenti della +funzione invece i \textit{file descriptor set} non vengono modificati anche in +caso di errore. + +Si tenga presente infine che su Linux, in caso di programmazione +\textit{multithread} se un file descriptor viene chiuso in un altro +\textit{thread} rispetto a quello in cui si sta usando \func{select}, questa +non subisce nessun effetto. In altre varianti di sistemi unix-like invece +\func{select} ritorna indicando che il file descriptor è pronto, con +conseguente possibile errore nel caso lo si usi senza che sia stato +riaperto. Lo standard non prevede niente al riguardo e non si deve dare per +assunto nessuno dei due comportamenti se si vogliono scrivere programmi +portabili. + \itindend{file~descriptor~set} -Una volta ritornata la funzione si potrà controllare quali sono i file -descriptor pronti ed operare su di essi, si tenga presente però che si tratta -solo di un suggerimento, esistono infatti condizioni\footnote{ad esempio - quando su un socket arrivano dei dati che poi vengono scartati perché - corrotti.} in cui \func{select} può riportare in maniera spuria che un file -descriptor è pronto in lettura, quando una successiva lettura si bloccherebbe. -Per questo quando si usa \textit{I/O multiplexing} è sempre raccomandato l'uso -delle funzioni di lettura e scrittura in modalità non bloccante. - -In Linux \func{select} modifica anche il valore di \param{timeout}, -impostandolo al tempo restante, quando la funzione viene interrotta da un -segnale. In tal caso infatti si ha un errore di \errcode{EINTR}, ed occorre -rilanciare la funzione; in questo modo non è necessario ricalcolare tutte le -volte il tempo rimanente. Questo può causare problemi di portabilità sia -quando si usa 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.\footnote{in - genere questa caratteristica è disponibile nei sistemi che derivano da - System V e non è disponibile per quelli che derivano da BSD; lo standard - POSIX.1-2001 non permette questo comportamento.} +Una volta ritornata la funzione, si potrà controllare quali sono i file +descriptor pronti, ed operare su di essi. Si tenga presente però che +\func{select} fornisce solo di un suggerimento, esistono infatti condizioni in +cui \func{select} può riportare in maniera spuria che un file descriptor è +pronto, ma l'esecuzione di una operazione di I/O si bloccherebbe: ad esempio +con Linux questo avviene quando su un socket arrivano dei dati che poi vengono +scartati perché corrotti (ma sono possibili pure altri casi); in tal caso pur +risultando il relativo file descriptor pronto in lettura una successiva +esecuzione di una \func{read} si bloccherebbe. Per questo motivo quando si usa +l'\textit{I/O multiplexing} è sempre raccomandato l'uso delle funzioni di +lettura e scrittura in modalità non bloccante. + +Su Linux quando la \textit{system call} \func{select} viene interrotta da un +segnale modifica il valore nella struttura puntata da \param{timeout}, +impostandolo al tempo restante. In tal caso infatti si ha un errore di +\errcode{EINTR} ed occorre rilanciare la funzione per proseguire l'attesa, ed +in questo modo non è necessario ricalcolare tutte le volte il tempo +rimanente. Questo può causare problemi di portabilità sia quando si usa 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 questa caratteristica è +disponibile nei sistemi che derivano da System V e non è disponibile per +quelli che derivano da BSD; lo standard POSIX.1-2001 non permette questo +comportamento e per questo motivo le \acr{glibc} nascondono il comportamento +passando alla \textit{system call} una copia dell'argomento \param{timeout}. Uno dei problemi che si presentano con l'uso di \func{select} è che il suo comportamento dipende dal valore del file descriptor che si vuole tenere sotto @@ -1171,36 +1192,39 @@ precedenti, ed inoltre aggiunge a \func{select} una nuova funzione \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{funcproto}{ +\fhead{sys/select.h} +\fdecl{int pselect(int n, fd\_set *readfds, fd\_set *writefds, + fd\_set *exceptfds, \\ +\phantom{int pselect(}struct timespec *timeout, sigset\_t *sigmask)} +\fdesc{Attende che uno dei file descriptor degli insiemi specificati diventi + attivo.} +} +{La funzione ritorna il numero (anche nullo) di file descriptor che sono + attivi in caso di successo e $-1$ per un 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{ndfs} un valore negativo o un valore non valido per \param{timeout}. - \end{errlist} - ed inoltre \errval{ENOMEM}.} -\end{prototype} + \end{errlist} + ed inoltre \errval{ENOMEM} nel suo significato generico. +} +\end{funcproto} La funzione è sostanzialmente identica a \func{select}, solo che usa una struttura \struct{timespec} (vedi fig.~\ref{fig:sys_timespec_struct}) per indicare con maggiore precisione il timeout e non ne aggiorna il valore in -caso di interruzione.\footnote{in realtà la \textit{system call} di Linux - aggiorna il valore al tempo rimanente, ma la funzione fornita dalle - \acr{glibc} modifica questo comportamento passando alla \textit{system call} - una variabile locale, in modo da mantenere l'aderenza allo standard POSIX - che richiede che il valore di \param{timeout} non sia modificato.} Inoltre -prende un argomento aggiuntivo \param{sigmask} che è il puntatore ad una -\index{maschera~dei~segnali} maschera di segnali (si veda +caso di interruzione. In realtà anche in questo caso la \textit{system call} +di Linux aggiorna il valore al tempo rimanente, ma la funzione fornita dalle +\acr{glibc} modifica questo comportamento passando alla \textit{system call} +una variabile locale, in modo da mantenere l'aderenza allo standard POSIX che +richiede che il valore di \param{timeout} non sia modificato. Ma rispetto a +\func{select} prende un argomento aggiuntivo \param{sigmask} che è il +puntatore ad una \index{maschera~dei~segnali} maschera di segnali (si veda sez.~\ref{sec:sig_sigmask}). La maschera corrente viene sostituita da questa immediatamente prima di eseguire l'attesa, e ripristinata al ritorno della funzione. -- 2.30.2