X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=fileadv.tex;h=53e4cb39e1dfe14604b665d1a5007960c94fdfbf;hp=480b3da5444aeafc1fc28ada86140d75f1e9c542;hb=503cb84c790afd8432cfe28f421f37ec72a65c4e;hpb=79073aa5e38b418fc4844ba3bd8dbb55e75dc4ff diff --git a/fileadv.tex b/fileadv.tex index 480b3da..53e4cb3 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -382,9 +382,8 @@ tal caso l'intervallo coperto va da \var{l\_start}$+$\var{l\_len} a \var{l\_start}$-1$, mentre per un valore positivo l'intervallo va da \var{l\_start} a \var{l\_start}$+$\var{l\_len}$-1$. Si può però usare un valore negativo soltanto se l'inizio della regione indicata non cade prima -dell'inizio del file, con un valore positivo invece si può anche indicare una -regione che eccede la dimensione corrente del file, e questa verrà coperta in -una sua futura estensione. +dell'inizio del file, mentre come accennato con un valore positivo si +può anche indicare una regione che eccede la dimensione corrente del file. Il tipo di \textit{file lock} richiesto viene specificato dal campo \var{l\_type}, esso può assumere i tre valori definiti dalle costanti @@ -605,11 +604,11 @@ Nel caso si sia scelta la semantica BSD (\texttt{\small 25--34}) prima si controlla (\texttt{\small 27--31}) il valore di \var{cmd} per determinare se si vuole effettuare una chiamata bloccante o meno, reimpostandone il valore opportunamente, dopo di che a seconda del tipo di blocco al valore viene -aggiunta la relativa opzione (con un OR aritmetico, dato che \func{flock} +aggiunta la relativa opzione, con un OR aritmetico, dato che \func{flock} vuole un argomento \param{operation} in forma di maschera binaria. Nel caso invece che si sia scelta la semantica POSIX le operazioni sono molto più -immediate, si prepara (\texttt{\small 36--40}) la struttura per il lock, e lo -esegue (\texttt{\small 41}). +immediate si prepara (\texttt{\small 36--40}) la struttura per il lock, e lo +si esegue (\texttt{\small 41}). In entrambi i casi dopo aver richiesto il blocco viene controllato il risultato uscendo (\texttt{\small 44--46}) in caso di errore, o stampando un @@ -723,8 +722,8 @@ Lock acquired \end{Console} %$ che ci mostra come i due tipi di blocco siano assolutamente indipendenti; per -questo motivo occorre sempre tenere presente quale fra le due semantiche -disponibili stanno usando i programmi con cui si interagisce, dato che i +questo motivo occorre sempre tenere presente quale, fra le due semantiche +disponibili, stanno usando i programmi con cui si interagisce, dato che i blocchi applicati con l'altra non avrebbero nessun effetto. % \subsection{La funzione \func{lockf}} @@ -789,7 +788,7 @@ consentiti sono i seguenti: sovrappone ad una che è già stata bloccata da un altro processo; in caso di sovrapposizione con un altro blocco già ottenuto le sezioni vengono unite. \item[\const{F\_TLOCK}] Richiede un \textit{exclusive lock}, in maniera - identica a\const{F\_LOCK} ma in caso di indisponibilità non blocca il + identica a \const{F\_LOCK}, ma in caso di indisponibilità non blocca il processo restituendo un errore di \errval{EAGAIN}. \item[\const{F\_ULOCK}] Rilascia il blocco sulla sezione indicata, questo può anche causare la suddivisione di una sezione bloccata in precedenza nelle @@ -804,10 +803,10 @@ consentiti sono i seguenti: La funzione è semplicemente una diversa interfaccia al \textit{file locking} POSIX ed è realizzata utilizzando \func{fcntl}; pertanto la semantica delle operazioni è la stessa di quest'ultima e quindi la funzione presenta lo stesso -comportamento riguardo gli effetti della chiusura dei file, degli effetti sui -file duplicati e nel passaggio attraverso \func{fork} ed \func{exec}. Per -questo motivo la funzione non è affatto equivalente a \func{flock} e può -essere usata senza interferenze insieme a quest'ultima. +comportamento riguardo gli effetti della chiusura dei file, ed il +comportamento sui file duplicati e nel passaggio attraverso \func{fork} ed +\func{exec}. Per questo stesso motivo la funzione non è equivalente a +\func{flock} e può essere usata senza interferenze insieme a quest'ultima. @@ -824,20 +823,20 @@ direttamente al sistema, così che, anche qualora non si predisponessero le opportune verifiche nei processi, questo verrebbe comunque rispettato. Per poter utilizzare il \textit{mandatory locking} è stato introdotto un -utilizzo particolare del bit \itindex{sgid~bit} \acr{sgid}. Se si ricorda -quanto esposto in sez.~\ref{sec:file_special_perm}), esso viene di norma -utilizzato per cambiare il \ids{GID} effettivo con cui viene eseguito un -programma, ed è pertanto sempre associato alla presenza del permesso di -esecuzione per il gruppo. Impostando questo bit su un file senza permesso di -esecuzione in un sistema che supporta il \textit{mandatory locking}, fa sì che -quest'ultimo venga attivato per il file in questione. In questo modo una -combinazione dei permessi originariamente non contemplata, in quanto senza -significato, diventa l'indicazione della presenza o meno del \textit{mandatory - locking}.\footnote{un lettore attento potrebbe ricordare quanto detto in - sez.~\ref{sec:file_perm_management} e cioè che il bit \acr{sgid} viene - cancellato (come misura di sicurezza) quando di scrive su un file, questo - non vale quando esso viene utilizzato per attivare il \textit{mandatory - locking}.} +utilizzo particolare del bit \itindex{sgid~bit} \acr{sgid} dei permessi dei +file. Se si ricorda quanto esposto in sez.~\ref{sec:file_special_perm}), esso +viene di norma utilizzato per cambiare il \ids{GID} effettivo con cui viene +eseguito un programma, ed è pertanto sempre associato alla presenza del +permesso di esecuzione per il gruppo. Impostando questo bit su un file senza +permesso di esecuzione in un sistema che supporta il \textit{mandatory + locking}, fa sì che quest'ultimo venga attivato per il file in questione. In +questo modo una combinazione dei permessi originariamente non contemplata, in +quanto senza significato, diventa l'indicazione della presenza o meno del +\textit{mandatory locking}.\footnote{un lettore attento potrebbe ricordare + quanto detto in sez.~\ref{sec:file_perm_management} e cioè che il bit + \acr{sgid} viene cancellato (come misura di sicurezza) quando di scrive su + un file, questo non vale quando esso viene utilizzato per attivare il + \textit{mandatory locking}.} L'uso del \textit{mandatory locking} presenta vari aspetti delicati, dato che neanche l'amministratore può passare sopra ad un \textit{file lock}; pertanto @@ -849,9 +848,9 @@ inaccessibile, rendendo completamente inutilizzabile il sistema\footnote{il bloccare completamente un server NFS richiedendo una lettura su un file su cui è attivo un blocco. Per questo motivo l'abilitazione del \textit{mandatory locking} è di norma disabilitata, e deve essere attivata filesystem per -filesystem in fase di montaggio (specificando l'apposita opzione di -\func{mount} riportata in sez.~\ref{sec:filesystem_mounting}), o con l'opzione -\code{-o mand} per il comando omonimo). +filesystem in fase di montaggio, specificando l'apposita opzione di +\func{mount} riportata in sez.~\ref{sec:filesystem_mounting}, o con l'opzione +\code{-o mand} per il comando omonimo. Si tenga presente inoltre che il \textit{mandatory locking} funziona solo sull'interfaccia POSIX di \func{fcntl}. Questo ha due conseguenze: che non si @@ -915,7 +914,6 @@ può presentare anche con l'uso di file mappati in memoria; pertanto allo stato attuale delle cose è sconsigliabile fare affidamento sul \textit{mandatory locking}. - \itindend{file~locking} \itindend{mandatory~locking} @@ -940,45 +938,55 @@ I/O. Abbiamo visto in sez.~\ref{sec:sig_gen_beha}, affrontando la suddivisione fra \textit{fast} e \textit{slow} \textit{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 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 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 \itindex{deadlock} \textit{deadlock}. +che in certi casi le funzioni di I/O eseguite su un file descritor possono +bloccarsi indefinitamente. Questo non avviene mai per i file normali, per i +quali le funzioni di lettura e scrittura ritornano sempre subito, ma può +avvenire per alcuni \index{file!di~dispositivo} file di dispositivo, come ad +esempio una seriale o un terminale, o con l'uso di file descriptor collegati a +meccanismi di intercomunicazione come le \textit{pipe} (vedi +sez.~\ref{sec:ipc_unix}) ed i socket (vedi sez.~\ref{sec:sock_socket_def}). In +casi come questi ad esempio una operazione di lettura potrebbe bloccarsi se +non ci sono dati disponibili sul descrittore su cui la si sta effettuando. + +Questo comportamento è alla radice di una delle problematiche più comuni che +ci si trova ad affrontare nella gestione delle operazioni di I/O: la necessità +di operare su più file descriptor eseguendo funzioni che possono bloccarsi +indefinitamente senza che sia possibile prevedere quando questo può +avvenire. Un caso classico è quello di un server di rete (tratteremo la +problematica in dettaglio nella seconda parte della guida) in attesa di dati +in ingresso prevenienti da vari client. + +In un caso di questo tipo, se si andasse ad operare sui vari file descriptor +aperti uno dopo l'altro, potrebbe accadere di restare bloccati nell'eseguire +una lettura su uno di quelli che non è ``\textsl{pronto}'', quando ce ne +potrebbe essere un altro con dati disponibili. 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 +dell'operazione bloccata dipende da quanto si otterrebbe dal file descriptor +``\textsl{disponibile}'', si potrebbe addirittura arrivare ad un +\itindex{deadlock} \textit{deadlock}. Abbiamo già accennato in sez.~\ref{sec:file_open_close} che è possibile prevenire 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 +di lettura o scrittura 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 \itindex{polling} +viene garantito. Ovviamente questa tecnica, detta \itindex{polling} \textit{polling}, è estremamente inefficiente: si tiene costantemente impiegata la CPU solo per eseguire in continuazione delle \textit{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 consente 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 effettuabile, in -modo da poterla eseguire con la sicurezza di non restare bloccati. +É appunto per superare questo problema è stato introdotto il concetto di +\textit{I/O multiplexing}, una nuova modalità per la gestione dell'I/O che +consente di tenere sotto controllo più file descriptor in contemporanea, +permettendo di bloccare un processo quando le operazioni di lettura o +scrittura non sono immediatamente effettuabili, e di riprenderne l'esecuzione +una volta che almeno una di quelle che erano state richieste diventi +possibile, 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 @@ -992,33 +1000,34 @@ sez.~\ref{sec:TCP_sock_multiplexing}. \label{sec:file_select} 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 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} - \headdecl{unistd.h} - \funcdecl{int select(int ndfs, 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} assumerà uno dei valori: + multiplexing} è stato BSD, con la funzione \funcd{select} che è apparsa in +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, è 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} +\fdecl{int select(int ndfs, fd\_set *readfds, fd\_set *writefds, fd\_set + *exceptfds, \\ +\phantom{int select(}struct timeval *timeout)} +\fdesc{Attende che uno fra i file descriptor degli insiemi specificati diventi + attivo.} +} +{La funzione ritorna $0$ 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{EBADF}] si è specificato un file descriptor non valido + (chiuso o con errori) 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{functions} + ed inoltre \errval{ENOMEM} nel suo significato generico.} +\end{funcproto} La funzione mette il processo in stato di \textit{sleep} (vedi tab.~\ref{tab:proc_proc_states}) fintanto che almeno uno dei file descriptor @@ -1035,35 +1044,35 @@ maniera analoga a come un \itindex{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: -\begin{functions} - \headdecl{sys/time.h} - \headdecl{sys/types.h} - \headdecl{unistd.h} - \funcdecl{void \macro{FD\_ZERO}(fd\_set *set)} - Inizializza l'insieme (vuoto). - \funcdecl{void \macro{FD\_SET}(int fd, fd\_set *set)} - Inserisce il file descriptor \param{fd} nell'insieme. +{\centering +\vspace{3pt} +\begin{funcbox}{ +\fhead{sys/select.h} +\fdecl{void \macro{FD\_ZERO}(fd\_set *set)} +\fdesc{Inizializza l'insieme (vuoto).} +\fdecl{void \macro{FD\_SET}(int fd, fd\_set *set)} +\fdesc{Inserisce il file descriptor \param{fd} nell'insieme.} +\fdecl{void \macro{FD\_CLR}(int fd, fd\_set *set)} +\fdesc{Rimuove il file descriptor \param{fd} dall'insieme.} +\fdecl{int \macro{FD\_ISSET}(int fd, fd\_set *set)} +\fdesc{Controlla se il file descriptor \param{fd} è nell'insieme.} +} +\end{funcbox}} - \funcdecl{void \macro{FD\_CLR}(int fd, fd\_set *set)} - Rimuove il file descriptor \param{fd} dall'insieme. - - \funcdecl{int \macro{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 \const{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 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}.\footnote{il suo valore, secondo lo standard POSIX - 1003.1-2001, è definito in \headfile{sys/select.h}, ed è pari a 1024.} +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, 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. @@ -1071,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 @@ -1163,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.