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
\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}
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:
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 è
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
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.
\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.
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]