X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=fileadv.tex;h=60a002b3e2f4769c897878ed68a891fb25c88354;hp=480b3da5444aeafc1fc28ada86140d75f1e9c542;hb=2ee09e1100e6d070dbf5d4d13d900dba516d83de;hpb=79073aa5e38b418fc4844ba3bd8dbb55e75dc4ff diff --git a/fileadv.tex b/fileadv.tex index 480b3da..60a002b 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 @@ -591,29 +590,29 @@ semantica POSIX), e la variabile \var{cmd} che specifica la modalità di richiesta del \textit{file lock} (bloccante o meno), a seconda dell'opzione \cmd{-b}. -Il programma inizia col controllare (\texttt{\small 11--14}) che venga passato +Il programma inizia col controllare (\texttt{\small 11-14}) che venga passato un argomento (il file da bloccare), che sia stato scelto (\texttt{\small - 15--18}) il tipo di blocco, dopo di che apre (\texttt{\small 19}) il file, -uscendo (\texttt{\small 20--23}) in caso di errore. A questo punto il + 15-18}) il tipo di blocco, dopo di che apre (\texttt{\small 19}) il file, +uscendo (\texttt{\small 20-23}) in caso di errore. A questo punto il comportamento dipende dalla semantica scelta; nel caso sia BSD occorre reimpostare il valore di \var{cmd} per l'uso con \func{flock}; infatti il valore preimpostato fa riferimento alla semantica POSIX e vale rispettivamente \const{F\_SETLKW} o \const{F\_SETLK} a seconda che si sia impostato o meno la modalità bloccante. -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 +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 -messaggio (\texttt{\small 47--49}) in caso di successo. Infine il programma si +risultato uscendo (\texttt{\small 44-46}) in caso di errore, o stampando un +messaggio (\texttt{\small 47-49}) in caso di successo. Infine il programma si pone in attesa (\texttt{\small 50}) finché un segnale (ad esempio un \cmd{C-c} dato da tastiera) non lo interrompa; in questo caso il programma termina, e tutti i blocchi vengono rilasciati. @@ -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,53 +1192,59 @@ 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 -sez.~\ref{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 -\textit{race condition} \itindex{race~condition} quando ci si deve porre in -attesa sia di un segnale che di dati. La tecnica classica è quella di -utilizzare il gestore per impostare una \index{variabili!globali} variabile -globale e controllare questa nel corpo principale del programma; abbiamo visto -in sez.~\ref{sec:sig_example} come questo lasci spazio a possibili -\itindex{race~condition} \textit{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 +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. + +Rispetto a \func{select} la nuova funzione prende un argomento +aggiuntivo \param{sigmask}, un puntatore ad una \index{maschera~dei~segnali} +maschera di segnali (si veda sez.~\ref{sec:sig_sigmask}). Nell'esecuzione la +maschera dei segnali corrente viene sostituita da quella così indicata +immediatamente prima di eseguire l'attesa, e viene poi ripristinata al ritorno +della funzione. L'uso di \param{sigmask} è stato introdotto allo scopo di +prevenire possibili \textit{race condition} \itindex{race~condition} quando +oltre alla presenza di dati sui file descriptor come nella \func{select} +ordinaria, ci si deve porre in attesa anche dell'arrivo di un segnale. + +Come abbiamo visto in sez.~\ref{sec:sig_example} la tecnica classica per +rilevare l'arrivo di un segnale è quella di utilizzare il gestore per +impostare una \index{variabili!globali} variabile globale e controllare questa +nel corpo principale del programma; abbiamo visto in quell'occasione come +questo lasci spazio a possibili \itindex{race~condition} \textit{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 @@ -1245,20 +1280,24 @@ interruzione si potranno eseguire le relative operazioni. \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 +\func{select}, che è una estensione tipica di BSD, è stata introdotta una +interfaccia completamente diversa, basata sulla funzione di sistema +\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, originariamente l'argomento \param{nfds} era di + tipo \ctyp{unsigned int}, la funzione è stata inserita nello standard + POSIX.1-2001 in cui è stato introdotto il tipo nativo \type{nfds\_t}.} 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 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 e -1 in caso di errore, - ed in quest'ultimo caso \var{errno} assumerà uno dei valori: + +\begin{funcproto}{ +\fhead{sys/poll.h} +\fdecl{int poll(struct pollfd *ufds, nfds\_t nfds, int timeout)} +\fdesc{Attende un cambiamento di stato su un insieme di file + descriptor.} +} + +{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. @@ -1266,8 +1305,8 @@ cui prototipo è: \item[\errcode{EINVAL}] il valore di \param{nfds} eccede il limite \const{RLIMIT\_NOFILE}. \end{errlist} - ed inoltre \errval{EFAULT} e \errval{ENOMEM}.} -\end{prototype} + ed inoltre \errval{EFAULT} e \errval{ENOMEM} nel loro significato generico.} +\end{funcproto} La funzione permette di tenere sotto controllo contemporaneamente \param{ndfs} file descriptor, specificati attraverso il puntatore \param{ufds} ad un @@ -1275,8 +1314,19 @@ 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 nullo comporta il ritorno -immediato (e può essere utilizzato per impiegare \func{poll} in modalità -\textsl{non-bloccante}). +immediato, e può essere utilizzato per impiegare \func{poll} in modalità +\textsl{non-bloccante}. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{0.90\textwidth} + \includestruct{listati/pollfd.h} + \end{minipage} + \normalsize + \caption{La struttura \structd{pollfd}, utilizzata per specificare le + modalità di controllo di un file descriptor alla funzione \func{poll}.} + \label{fig:file_pollfd} +\end{figure} Per ciascun file da controllare deve essere inizializzata una struttura \struct{pollfd} nel vettore indicato dall'argomento \param{ufds}. La @@ -1285,30 +1335,24 @@ 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 +risultato. + +Usando un valore negativo per \param{fd} la corrispondente struttura sarà +ignorata da \func{poll} ed il campo \var{revents} verrà azzerato, questo +consente di eliminare temporaneamente un file descriptor dalla lista senza +dover modificare il vettore \param{ufds}. 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. -\begin{figure}[!htb] - \footnotesize \centering - \begin{minipage}[c]{\textwidth} - \includestruct{listati/pollfd.h} - \end{minipage} - \normalsize - \caption{La struttura \structd{pollfd}, utilizzata per specificare le - modalità di controllo di un file descriptor alla funzione \func{poll}.} - \label{fig:file_pollfd} -\end{figure} - Le costanti che definiscono i valori relativi ai bit usati nelle maschere -binarie dei campi \var{events} e \var{revents} sono riportati in +binarie dei campi \var{events} e \var{revents} sono riportate in tab.~\ref{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. +suddivise in tre gruppi principali, nel primo gruppo si sono indicati i bit +utilizzati per controllare l'attività in ingresso, nel secondo quelli per +l'attività in uscita, infine il terzo gruppo contiene dei valori che vengono +utilizzati solo nel campo \var{revents} per notificare delle condizioni di +errore. \begin{table}[htb] \centering @@ -1350,33 +1394,35 @@ nel campo \var{revents} per notificare delle condizioni di errore. dettagli in sez.~\ref{sec:TCP_shutdown}.} 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 urgenti \itindex{out-of-band} dei socket -(vedi sez.~\ref{sec:TCP_urgent_data}), ma su questo e su come \func{poll} -reagisce alle varie condizioni dei socket torneremo in +compatibilità con l'implementazione di System V che usa i cosiddetti +``\textit{stream}''. Si tratta di una interfaccia specifica di SysV non +presente in Linux, che non ha nulla a che fare con gli \textit{stream} delle +librerie standard del C visti in sez.~\ref{sec:file_stream}. Da essa derivano +i nomi di alcune costanti poiché per quegli \textit{stream} sono definite tre +classi di dati: \textsl{normali}, \textit{prioritari} ed \textit{urgenti}. In +Linux la distinzione ha senso solo per i dati urgenti \itindex{out-of-band} +dei socket (vedi sez.~\ref{sec:TCP_urgent_data}), ma su questo e su come +\func{poll} reagisce alle varie condizioni dei socket torneremo in sez.~\ref{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 -normali e prioritari, vale a dire \const{POLLRDNORM}, \const{POLLWRNORM}, -\const{POLLRDBAND} e \const{POLLWRBAND} fanno riferimento alle implementazioni -in stile SysV (in particolare le ultime due non vengono usate su Linux), e -sono utilizzabili soltanto qualora si sia definita la macro -\macro{\_XOPEN\_SOURCE}.\footnote{e ci si ricordi di farlo sempre in testa al - file, definirla soltanto prima di includere \headfile{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 tab.~\ref{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}. +Le costanti relative ai diversi tipi di dati normali e prioritari che fanno +riferimento alle implementazioni in stile System V sono \const{POLLRDNORM}, +\const{POLLWRNORM}, \const{POLLRDBAND} e \const{POLLWRBAND}. Le prime due sono +equivalenti rispettivamente a \const{POLLIN} e \const{POLLOUT}, +\const{POLLRDBAND} non viene praticamente mai usata su Linux mentre +\const{POLLWRBAND} ha senso solo sui socket. In ogni caso queste costanti sono +utilizzabili soltanto qualora si sia definita la macro +\macro{\_XOPEN\_SOURCE}. + +In caso di successo \func{poll} 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, avvalorando i relativi bit +di \var{revents}. In caso di errori sui file vengono utilizzati i valori della +terza sezione di tab.~\ref{tab:file_pollfd_flags} che hanno significato solo +per \var{revents} (se specificati in \var{events} vengono ignorati). Un valore +di ritorno 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}. L'uso di \func{poll} consente di superare alcuni dei problemi illustrati in precedenza per \func{select}; anzitutto, dato che in questo caso si usa un @@ -1384,11 +1430,10 @@ vettore di strutture \struct{pollfd} di dimensione arbitraria, non esiste il limite introdotto dalle dimensioni massime di un \itindex{file~descriptor~set} \textit{file descriptor set} e la dimensione dei dati passati al kernel dipende solo dal numero dei file descriptor che si vogliono controllare, non -dal loro valore.\footnote{anche se usando dei bit un \textit{file descriptor - set} può essere più efficiente di un vettore di strutture \struct{pollfd}, - qualora si debba osservare un solo file descriptor con un valore molto alto - ci si troverà ad utilizzare inutilmente un maggiore quantitativo di - memoria.} +dal loro valore. Infatti, anche se usando dei bit un \textit{file descriptor + set} può essere più efficiente di un vettore di strutture \struct{pollfd}, +qualora si debba osservare un solo file descriptor con un valore molto alto ci +si troverà ad utilizzare inutilmente un maggiore quantitativo di memoria. Inoltre con \func{select} lo stesso \itindex{file~descriptor~set} \textit{file descriptor set} è usato sia in ingresso che in uscita, e questo significa @@ -1408,16 +1453,19 @@ prevista da nessuno standard; essa può essere utilizzata esclusivamente se si definisce la macro \macro{\_GNU\_SOURCE} ed ovviamente non deve essere usata se si ha a cuore la portabilità. La funzione è \funcd{ppoll}, ed il suo prototipo è: -\begin{prototype}{sys/poll.h} - {int ppoll(struct pollfd *fds, nfds\_t nfds, const struct timespec *timeout, - const sigset\_t *sigmask)} - - 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 e -1 in caso di errore, - ed in quest'ultimo caso \var{errno} assumerà uno dei valori: + +\begin{funcproto}{ +\fhead{sys/poll.h} +\fdecl{int ppoll(struct pollfd *fds, nfds\_t nfds, + const struct timespec *timeout, \\ +\phantom{int ppoll(}const sigset\_t *sigmask)} + +\fdesc{Attende un cambiamento di stato su un insieme di file descriptor.} +} + +{La funzione ritorna il numero di file descriptor con attività in caso di + successo, $0$ se c'è stato un timeout 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. @@ -1425,8 +1473,9 @@ prototipo è: \item[\errcode{EINVAL}] il valore di \param{nfds} eccede il limite \const{RLIMIT\_NOFILE}. \end{errlist} - ed inoltre \errval{EFAULT} e \errval{ENOMEM}.} -\end{prototype} +ed inoltre \errval{EFAULT} e \errval{ENOMEM} nel loro significato generico. +} +\end{funcproto} La funzione ha lo stesso comportamento di \func{poll}, solo che si può specificare, con l'argomento \param{sigmask}, il puntatore ad una @@ -1440,14 +1489,17 @@ del seguente codice: Eccetto per \param{timeout}, che come per \func{pselect} deve essere un puntatore ad una struttura \struct{timespec}, gli altri argomenti comuni con \func{poll} hanno lo stesso significato, e la funzione restituisce gli stessi -risultati illustrati in precedenza. Come nel caso di \func{pselect} la system -call che implementa \func{ppoll} restituisce, se la funzione viene interrotta -da un segnale, il tempo mancante in \param{timeout}, e come per \func{pselect} -la funzione di libreria fornita dalle \acr{glibc} maschera questo -comportamento non modificando mai il valore di \param{timeout}.\footnote{anche - se in questo caso non esiste nessuno standard che richiede questo - comportamento.} - +risultati illustrati in precedenza. Come nel caso di \func{pselect} la +\textit{system call} che implementa \func{ppoll} restituisce, se la funzione +viene interrotta da un segnale, il tempo mancante in \param{timeout}, e come +per \func{pselect} la funzione di libreria fornita dalle \acr{glibc} maschera +questo comportamento non modificando mai il valore di \param{timeout} anche se +in questo caso non esiste nessuno standard che richieda questo comportamento. + +Infine anche per \func{poll} e \func{ppoll} valgono le considerazioni relative +alla possibilità di avere delle notificazione spurie della disponibilita di +accesso ai file descriptor illustrate per \func{select} in +sez.~\ref{sec:file_select}, che non staremo a ripetere qui. \subsection{L'interfaccia di \textit{epoll}} \label{sec:file_epoll} @@ -1465,10 +1517,10 @@ da \func{poll} a trasferire i dati da e verso il kernel è proporzionale al numero di file descriptor osservati, non a quelli che presentano attività. Quando ci sono decine di migliaia di file descriptor osservati e migliaia di -eventi al secondo,\footnote{il caso classico è quello di un server web di un - sito con molti accessi.} l'uso di \func{poll} comporta la necessità di -trasferire avanti ed indietro da user space a kernel space la lunga lista -delle strutture \struct{pollfd} migliaia di volte al secondo. A questo poi si +eventi al secondo (il caso classico è quello di un server web di un sito con +molti accessi) l'uso di \func{poll} comporta la necessità di trasferire avanti +ed indietro da \textit{user space} a \textit{kernel space} una lunga lista di +strutture \struct{pollfd} migliaia di volte al secondo. A questo poi si aggiunge il fatto che la maggior parte del tempo di esecuzione sarà impegnato ad eseguire una scansione su tutti i file descriptor tenuti sotto controllo per determinare quali di essi (in genere una piccola percentuale) sono @@ -1477,13 +1529,12 @@ dell'interfaccia dell'\textit{I/O multiplexing} viene a costituire un collo di bottiglia che degrada irrimediabilmente le prestazioni. Per risolvere questo tipo di situazioni sono state ideate delle interfacce -specialistiche\footnote{come \texttt{/dev/poll} in Solaris, o \texttt{kqueue} - in BSD.} il cui scopo fondamentale è quello di restituire solamente le -informazioni relative ai file descriptor osservati che presentano una -attività, evitando così le problematiche appena illustrate. In genere queste -prevedono che si registrino una sola volta i file descriptor da tenere sotto -osservazione, e forniscono un meccanismo che notifica quali di questi -presentano attività. +specialistiche (come \texttt{/dev/poll} in Solaris, o \texttt{kqueue} in BSD) +il cui scopo fondamentale è quello di restituire solamente le informazioni +relative ai file descriptor osservati che presentano una attività, evitando +così le problematiche appena illustrate. In genere queste prevedono che si +registrino una sola volta i file descriptor da tenere sotto osservazione, e +forniscono un meccanismo che notifica quali di questi presentano attività. Le modalità con cui avviene la notifica sono due, la prima è quella classica (quella usata da \func{poll} e \func{select}) che viene chiamata \textit{level @@ -1510,88 +1561,87 @@ il file descriptor sia tornato non essere pronto, si potrà ricevere una ulteriore notifica qualora ritornasse pronto. Nel caso di Linux al momento la sola interfaccia che fornisce questo tipo di -servizio è \textit{epoll},\footnote{l'interfaccia è stata creata da Davide - Libenzi, ed è stata introdotta per la prima volta nel kernel 2.5.44, ma la - sua forma definitiva è stata raggiunta nel kernel 2.5.66.} anche se sono in -discussione altre interfacce con le quali si potranno effettuare lo stesso -tipo di operazioni;\footnote{al momento della stesura di queste note (Giugno - 2007) un'altra interfaccia proposta è quella di \textit{kevent}, che - fornisce un sistema di notifica di eventi generico in grado di fornire le - stesse funzionalità di \textit{epoll}, esiste però una forte discussione - intorno a tutto ciò e niente di definito.} \textit{epoll} è in grado di -operare sia in modalità \textit{level triggered} che \textit{edge triggered}. - -La prima versione \textit{epoll} prevedeva l'apertura di uno speciale file di -dispositivo, \texttt{/dev/epoll}, per ottenere un file descriptor da -utilizzare con le funzioni dell'interfaccia,\footnote{il backporting - dell'interfaccia per il kernel 2.4, non ufficiale, utilizza sempre questo - file.} ma poi si è passati all'uso di apposite \textit{system call}. Il -primo passo per usare l'interfaccia di \textit{epoll} è pertanto quello -ottenere detto file descriptor chiamando una delle funzioni -\funcd{epoll\_create} e \funcd{epoll\_create1},\footnote{l'interfaccia di - \textit{epoll} è stata inserita nel kernel a partire dalla versione 2.5.44, - ed il supporto è stato aggiunto alle \acr{glibc} 2.3.2.} i cui prototipi -sono: -\begin{functions} - \headdecl{sys/epoll.h} +servizio è chiamata \textit{epoll},\footnote{l'interfaccia è stata creata da + Davide Libenzi, ed è stata introdotta per la prima volta nel kernel 2.5.44, + ma la sua forma definitiva è stata raggiunta nel kernel 2.5.66, il supporto + è stato aggiunto nelle \acr{glibc} a partire dalla versione 2.3.2.} anche se +sono state in discussione altre interfacce con le quali effettuare lo stesso +tipo di operazioni; \textit{epoll} è in grado di operare sia in modalità +\textit{level triggered} che \textit{edge triggered}. + +La prima versione di \textit{epoll} prevedeva l'apertura di uno speciale file +di dispositivo, \texttt{/dev/epoll}, per ottenere un file descriptor da +utilizzare con le funzioni dell'interfaccia ma poi si è passati all'uso di +apposite \textit{system call}. Il primo passo per usare l'interfaccia di +\textit{epoll} è pertanto quello ottenere detto file descriptor chiamando una +delle due funzioni di sistema \funcd{epoll\_create} e \funcd{epoll\_create1}, +i cui prototipi sono: - \funcdecl{int epoll\_create(int size)} - \funcdecl{int epoll\_create1(int flags)} - - Apre un file descriptor per \textit{epoll}. - - \bodydesc{Le funzioni restituiscono un file descriptor per \textit{epoll} in - caso di successo, o $-1$ in caso di errore, nel qual caso \var{errno} - assumerà uno dei valori: +\begin{funcproto}{ +\fhead{sys/epoll.h} +\fdecl{int epoll\_create(int size)} +\fdecl{int epoll\_create1(int flags)} + +\fdesc{Apre un file descriptor per \textit{epoll}.} +} +{Le funzioni ritornano un file descriptor per \textit{epoll} in caso di + successo e $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei + valori: \begin{errlist} \item[\errcode{EINVAL}] si è specificato un valore di \param{size} non positivo o non valido per \param{flags}. - \item[\errcode{ENFILE}] si è raggiunto il massimo di file descriptor aperti - nel sistema. \item[\errcode{EMFILE}] si è raggiunto il limite sul numero massimo di istanze di \textit{epoll} per utente stabilito da \sysctlfile{fs/epoll/max\_user\_instances}. + \item[\errcode{ENFILE}] si è raggiunto il massimo di file descriptor aperti + nel sistema. \item[\errcode{ENOMEM}] non c'è sufficiente memoria nel kernel per creare l'istanza. \end{errlist} -} -\end{functions} +} +\end{funcproto} Entrambe le funzioni restituiscono un file descriptor speciale,\footnote{esso non è associato a nessun file su disco, inoltre a differenza dei normali file descriptor non può essere inviato ad un altro processo attraverso un socket locale (vedi sez.~\ref{sec:sock_fd_passing}).} detto anche \textit{epoll descriptor}, che viene associato alla infrastruttura utilizzata -dal kernel per gestire la notifica degli eventi. Nel caso di -\func{epoll\_create} l'argomento \param{size} serviva a dare l'indicazione del -numero di file descriptor che si vorranno tenere sotto controllo, e costituiva -solo un suggerimento per semplificare l'allocazione di risorse sufficienti, -non un valore massimo.\footnote{ma a partire dal kernel 2.6.8 esso viene - totalmente ignorato e l'allocazione è sempre dinamica.} - -La seconda versione della funzione, \func{epoll\_create1} è stata -introdotta\footnote{è disponibile solo a partire dal kernel 2.6.27.} come -estensione della precedente, per poter passare dei flag di controllo come -maschera binaria in fase di creazione del file descriptor. Al momento l'unico -valore legale per \param{flags} (a parte lo zero) è \const{EPOLL\_CLOEXEC}, -che consente di impostare in maniera atomica sul file descriptor il flag di -\itindex{close-on-exec} \textit{close-on-exec} (si veda il significato di -\const{O\_CLOEXEC} in sez.~\ref{sec:file_open_close}), senza che sia +dal kernel per gestire la notifica degli eventi. Una volta che se ne sia +terminato l'uso si potranno rilasciare tutte le risorse allocate chiudendolo +come ogni altro file descriptor con \func{close}. + +Nel caso di \func{epoll\_create} l'argomento \param{size} serviva a dare +l'indicazione del numero di file descriptor che si vorranno tenere sotto +controllo, e costituiva solo un suggerimento per semplificare l'allocazione di +risorse sufficienti, non un valore massimo, ma a partire dal kernel 2.6.8 esso +viene totalmente ignorato e l'allocazione è sempre dinamica. + +La seconda versione della funzione, \func{epoll\_create1} è stata introdotta +come estensione della precedente (è disponibile solo a partire dal kernel +2.6.27) per poter passare dei flag di controllo come maschera binaria in fase +di creazione del file descriptor. Al momento l'unico valore legale +per \param{flags} (a parte lo zero) è \const{EPOLL\_CLOEXEC}, che consente di +impostare in maniera atomica sul file descriptor il flag di +\itindex{close-on-exec} \textit{close-on-exec} (si è trattato il significato +di \const{O\_CLOEXEC} in sez.~\ref{sec:file_open_close}), senza che sia necessaria una successiva chiamata a \func{fcntl}. Una volta ottenuto un file descriptor per \textit{epoll} il passo successivo è indicare quali file descriptor mettere sotto osservazione e quali operazioni -controllare, per questo si deve usare la seconda funzione dell'interfaccia, -\funcd{epoll\_ctl}, il cui prototipo è: -\begin{prototype}{sys/epoll.h} - {int epoll\_ctl(int epfd, int op, int fd, struct epoll\_event *event)} - - Esegue le operazioni di controllo di \textit{epoll}. - - \bodydesc{La funzione restituisce $0$ in caso di successo o $-1$ in caso di - errore, nel qual caso \var{errno} assumerà uno dei valori: +controllare, per questo si deve usare la seconda funzione di sistema +dell'interfaccia, \funcd{epoll\_ctl}, il cui prototipo è: + +\begin{funcproto}{ +\fhead{sys/epoll.h} +\fdecl{int epoll\_ctl(int epfd, int op, int fd, struct epoll\_event *event)} + +\fdesc{Esegue le operazioni di controllo di \textit{epoll}.} +} + +{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}] il file descriptor \param{epfd} o \param{fd} non sono + \item[\errcode{EBADF}] i file descriptor \param{epfd} o \param{fd} non sono validi. \item[\errcode{EEXIST}] l'operazione richiesta è \const{EPOLL\_CTL\_ADD} ma \param{fd} è già stato inserito in \param{epfd}. @@ -1602,13 +1652,14 @@ controllare, per questo si deve usare la seconda funzione dell'interfaccia, \const{EPOLL\_CTL\_DEL} ma \param{fd} non è inserito in \param{epfd}. \item[\errcode{ENOMEM}] non c'è sufficiente memoria nel kernel gestire l'operazione richiesta. - \item[\errcode{EPERM}] il file \param{fd} non supporta \textit{epoll}. \item[\errcode{ENOSPC}] si è raggiunto il limite massimo di registrazioni per utente di file descriptor da osservare imposto da \sysctlfile{fs/epoll/max\_user\_watches}. + \item[\errcode{EPERM}] il file associato a \param{fd} non supporta l'uso di + \textit{epoll}. \end{errlist} -} -\end{prototype} + } +\end{funcproto} Il comportamento della funzione viene controllato dal valore dall'argomento \param{op} che consente di specificare quale operazione deve essere eseguita. @@ -1634,7 +1685,8 @@ delle operazioni cui fanno riferimento. \param{event}.\\ \const{EPOLL\_CTL\_DEL}& Rimuove il file descriptor \param{fd} dalla lista dei file controllati tramite \param{epfd}.\\ - \hline + \const{EPOLL\_CTL\_DISABLE}& da fare.\\ + \hline \end{tabular} \caption{Valori dell'argomento \param{op} che consentono di scegliere quale operazione di controllo effettuare con la funzione \func{epoll\_ctl}.} @@ -1664,7 +1716,7 @@ sotto controllo. L'argomento viene ignorato con l'operazione \begin{figure}[!htb] \footnotesize \centering - \begin{minipage}[c]{\textwidth} + \begin{minipage}[c]{0.90\textwidth} \includestruct{listati/epoll_event.h} \end{minipage} \normalsize @@ -1695,7 +1747,7 @@ identificazione del file descriptor. \begin{table}[htb] \centering \footnotesize - \begin{tabular}[c]{|l|p{8cm}|} + \begin{tabular}[c]{|l|p{10cm}|} \hline \textbf{Valore} & \textbf{Significato} \\ \hline @@ -1724,7 +1776,8 @@ identificazione del file descriptor. \const{EPOLLET} & Imposta la notifica in modalità \textit{edge triggered} per il file descriptor associato.\\ \const{EPOLLONESHOT}& Imposta la modalità \textit{one-shot} per il file - descriptor associato.\footnotemark\\ + descriptor associato (questa modalità è disponibile + solo a partire dal kernel 2.6.2).\\ \hline \end{tabular} \caption{Costanti che identificano i bit del campo \param{events} di @@ -1736,9 +1789,6 @@ identificazione del file descriptor. ed è utile per riconoscere la chiusura di una connessione dall'altro capo quando si lavora in modalità \textit{edge triggered}.} -\footnotetext[48]{questa modalità è disponibile solo a partire dal kernel - 2.6.2.} - % TODO aggiunto EPOLLWAKEUP con il 3.5 @@ -1774,33 +1824,35 @@ non è necessario usare \const{EPOLL\_CTL\_DEL}. Infine una particolare modalità di notifica è quella impostata con \const{EPOLLONESHOT}: a causa dell'implementazione di \textit{epoll} infatti quando si è in modalità \textit{edge triggered} l'arrivo in rapida successione -di dati in blocchi separati\footnote{questo è tipico con i socket di rete, in - quanto i dati arrivano a pacchetti.} può causare una generazione di eventi -(ad esempio segnalazioni di dati in lettura disponibili) anche se la -condizione è già stata rilevata.\footnote{si avrebbe cioè una rottura della - logica \textit{edge triggered}.} +di dati in blocchi separati (questo è tipico con i socket di rete, in quanto i +dati arrivano a pacchetti) può causare una generazione di eventi (ad esempio +segnalazioni di dati in lettura disponibili) anche se la condizione è già +stata rilevata (si avrebbe cioè una rottura della logica \textit{edge + triggered}). Anche se la situazione è facile da gestire, la si può evitare utilizzando \const{EPOLLONESHOT} per impostare la modalità \textit{one-shot}, in cui la notifica di un evento viene effettuata una sola volta, dopo di che il file descriptor osservato, pur restando nella lista di osservazione, viene -automaticamente disattivato,\footnote{la cosa avviene contestualmente al - ritorno di \func{epoll\_wait} a causa dell'evento in questione.} e per -essere riutilizzato dovrà essere riabilitato esplicitamente con una successiva -chiamata con \const{EPOLL\_CTL\_MOD}. +automaticamente disattivato (la cosa avviene contestualmente al ritorno di +\func{epoll\_wait} a causa dell'evento in questione) e per essere riutilizzato +dovrà essere riabilitato esplicitamente con una successiva chiamata con +\const{EPOLL\_CTL\_MOD}. Una volta impostato l'insieme di file descriptor che si vogliono osservare con i relativi eventi, la funzione che consente di attendere l'occorrenza di uno di tali eventi è \funcd{epoll\_wait}, il cui prototipo è: -\begin{prototype}{sys/epoll.h} - {int epoll\_wait(int epfd, struct epoll\_event * events, int maxevents, int - timeout)} - - Attende che uno dei file descriptor osservati sia pronto. - - \bodydesc{La funzione restituisce il numero di file descriptor pronti in - caso di successo o $-1$ in caso di errore, nel qual caso \var{errno} - assumerà uno dei valori: + +\begin{funcproto}{ +\fhead{sys/epoll.h} +\fdecl{int epoll\_wait(int epfd, struct epoll\_event * events, int maxevents, + int timeout)} + +\fdesc{Attende che uno dei file descriptor osservati sia pronto.} +} + +{La funzione ritorna il numero di file descriptor pronti in caso di successo e + $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} \item[\errcode{EBADF}] il file descriptor \param{epfd} non è valido. \item[\errcode{EFAULT}] il puntatore \param{events} non è valido. @@ -1809,8 +1861,8 @@ di tali eventi è \funcd{epoll\_wait}, il cui prototipo è: \item[\errcode{EINVAL}] il file descriptor \param{epfd} non è stato ottenuto con \func{epoll\_create}, o \param{maxevents} non è maggiore di zero. \end{errlist} -} -\end{prototype} +} +\end{funcproto} La funzione si blocca in attesa di un evento per i file descriptor registrati nella lista di osservazione di \param{epfd} fino ad un tempo massimo @@ -1823,10 +1875,10 @@ con l'argomento \param{maxevents}. La funzione ritorna il numero di eventi rilevati, o un valore nullo qualora sia scaduto il tempo massimo impostato con \param{timeout}. Per quest'ultimo, oltre ad un numero di millisecondi, si può utilizzare il valore nullo, che -indica di non attendere e ritornare immediatamente,\footnote{anche in questo - caso il valore di ritorno sarà nullo.} o il valore $-1$, che indica -un'attesa indefinita. L'argomento \param{maxevents} dovrà invece essere sempre -un intero positivo. +indica di non attendere e ritornare immediatamente (anche in questo caso il +valore di ritorno sarà nullo) o il valore $-1$, che indica un'attesa +indefinita. L'argomento \param{maxevents} dovrà invece essere sempre un intero +positivo. Come accennato la funzione restituisce i suoi risultati nel vettore di strutture \struct{epoll\_event} puntato da \param{events}; in tal caso nel @@ -1835,8 +1887,8 @@ eventi accaduti, mentre nel campo \var{data} sarà restituito il valore che era stato impostato per il file descriptor per cui si è verificato l'evento quando questo era stato registrato con le operazioni \const{EPOLL\_CTL\_MOD} o \const{EPOLL\_CTL\_ADD}, in questo modo il campo \var{data} consente di -identificare il file descriptor.\footnote{ed è per questo che, come accennato, - è consuetudine usare per \var{data} il valore del file descriptor stesso.} +identificare il file descriptor, ed è per questo che, come accennato, è +consuetudine usare per \var{data} il valore del file descriptor stesso. Si ricordi che le occasioni per cui \func{epoll\_wait} ritorna dipendono da come si è impostata la modalità di osservazione (se \textit{level triggered} o @@ -1865,21 +1917,24 @@ per fare questo di nuovo è necessaria una variante della funzione di attesa che consenta di reimpostare all'uscita una \index{maschera~dei~segnali} maschera di segnali, analoga alle estensioni \func{pselect} e \func{ppoll} che abbiamo visto in precedenza per \func{select} e \func{poll}; in questo caso la -funzione si chiama \funcd{epoll\_pwait}\footnote{la funziona è stata +funzione si chiama \funcd{epoll\_pwait}\footnote{la funzione è stata introdotta a partire dal kernel 2.6.19, ed è come tutta l'interfaccia di \textit{epoll}, specifica di Linux.} ed il suo prototipo è: -\begin{prototype}{sys/epoll.h} - {int epoll\_pwait(int epfd, struct epoll\_event * events, int maxevents, + +\begin{funcproto}{ +\fhead{sys/epoll.h} +\fdecl{int epoll\_pwait(int epfd, struct epoll\_event * events, int maxevents, int timeout, const sigset\_t *sigmask)} - Attende che uno dei file descriptor osservati sia pronto, mascherando i - segnali. +\fdesc{Attende che uno dei file descriptor osservati sia pronto, mascherando + i segnali.} } - \bodydesc{La funzione restituisce il numero di file descriptor pronti in - caso di successo o $-1$ in caso di errore, nel qual caso \var{errno} - assumerà uno dei valori già visti con \funcd{epoll\_wait}. -} -\end{prototype} +{La funzione ritorna il numero di file descriptor pronti in caso di successo e + $-1$ per un errore, nel qual caso \var{errno} assumerà uno dei valori già + visti con \funcd{epoll\_wait}. + +} +\end{funcproto} La funzione è del tutto analoga \funcd{epoll\_wait}, soltanto che alla sua uscita viene ripristinata la \index{maschera~dei~segnali} maschera di segnali @@ -1927,15 +1982,15 @@ risposte, mentre con l'arrivo di un segnale si possono avere interruzioni asincrone in qualunque momento. Questo comporta la necessità di dover gestire, quando si deve tener conto di entrambi i tipi di eventi, le interruzioni delle funzioni di attesa sincrone, ed evitare possibili -\itindex{race~condition} \textit{race conditions}.\footnote{in sostanza se non - fossero per i segnali non ci sarebbe da doversi preoccupare, fintanto che si - effettuano operazioni all'interno di un processo, della non atomicità delle - \index{system~call~lente} \textit{system call} lente che vengono interrotte - e devono essere riavviate.} +\itindex{race~condition} \textit{race conditions}. In sostanza se non ci +fossero i segnali non ci sarebbe da preoccuparsi, fintanto che si effettuano +operazioni all'interno di un processo, della non atomicità delle +\index{system~call~lente} \textit{system call} lente che vengono interrotte e +devono essere riavviate. Abbiamo visto però in sez.~\ref{sec:sig_real_time} che insieme ai segnali \textit{real-time} sono state introdotte anche delle interfacce di gestione -sincrona dei segnali con la funzione \func{sigwait} e le sue affini. Queste +sincrona dei segnali, con la funzione \func{sigwait} e le sue affini. Queste funzioni consentono di gestire i segnali bloccando un processo fino alla avvenuta ricezione e disabilitando l'esecuzione asincrona rispetto al resto del programma del gestore del segnale. Questo consente di risolvere i problemi @@ -1950,9 +2005,9 @@ Per risolvere questo problema nello sviluppo del kernel si è pensato di introdurre un meccanismo alternativo per la notifica dei segnali (esteso anche ad altri eventi generici) che, ispirandosi di nuovo alla filosofia di Unix per cui tutto è un file, consentisse di eseguire la notifica con l'uso di -opportuni file descriptor.\footnote{ovviamente si tratta di una funzionalità - specifica di Linux, non presente in altri sistemi unix-like, e non prevista - da nessuno standard, per cui va evitata se si ha a cuore la portabilità.} +opportuni file descriptor. Ovviamente si tratta di una funzionalità specifica +di Linux, non presente in altri sistemi unix-like, e non prevista da nessuno +standard, per cui va evitata se si ha a cuore la portabilità. In sostanza, come per \func{sigwait}, si può disabilitare l'esecuzione di un gestore in occasione dell'arrivo di un segnale, e rilevarne l'avvenuta @@ -1964,10 +2019,10 @@ stesso modo di quelli associati a file o socket, per cui alla fine si potrà attendere in contemporanea sia l'arrivo del segnale che la disponibilità di accesso ai dati relativi a questi ultimi. -La funzione che permette di abilitare la ricezione dei segnali tramite file -descriptor è \funcd{signalfd},\footnote{in realtà quella riportata è - l'interfaccia alla funzione fornita dalle \acr{glibc}, esistono infatti due - versioni diverse della \textit{system call}; una prima versione, +La funzione di sistema che permette di abilitare la ricezione dei segnali +tramite file descriptor è \funcd{signalfd},\footnote{in realtà quella + riportata è l'interfaccia alla funzione fornita dalle \acr{glibc}, esistono + infatti due versioni diverse della \textit{system call}; una prima versione, \func{signalfd}, introdotta nel kernel 2.6.22 e disponibile con le \acr{glibc} 2.8 che non supporta l'argomento \texttt{flags}, ed una seconda versione, \funcm{signalfd4}, introdotta con il kernel 2.6.27 e che è quella @@ -1975,27 +2030,30 @@ descriptor è \funcd{signalfd},\footnote{in realtà quella riportata è argomento aggiuntivo \code{size\_t sizemask} che indica la dimensione della \index{maschera~dei~segnali} maschera dei segnali, il cui valore viene impostato automaticamente dalle \acr{glibc}.} il cui prototipo è: -\begin{prototype}{sys/signalfd.h} - {int signalfd(int fd, const sigset\_t *mask, int flags)} - Crea o modifica un file descriptor per la ricezione dei segnali. +\begin{funcproto}{ +\fhead{sys/signalfd.h} +\fdecl{int signalfd(int fd, const sigset\_t *mask, int flags)} - \bodydesc{La funzione restituisce un numero di file descriptor in caso di - successo o $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno - dei valori: +\fdesc{Crea o modifica un file descriptor per la ricezione dei segnali.} +} + +{La funzione ritorna un numero di file descriptor in caso di successo e $-1$ + per un errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} \item[\errcode{EBADF}] il valore \param{fd} non indica un file descriptor. \item[\errcode{EINVAL}] il file descriptor \param{fd} non è stato ottenuto con \func{signalfd} o il valore di \param{flags} non è valido. - \item[\errcode{ENOMEM}] non c'è memoria sufficiente per creare un nuovo file - descriptor di \func{signalfd}. \item[\errcode{ENODEV}] il kernel non può montare internamente il dispositivo per la gestione anonima degli \itindex{inode} \textit{inode} associati al file descriptor. + \item[\errcode{ENOMEM}] non c'è memoria sufficiente per creare un nuovo file + descriptor di \func{signalfd}. \end{errlist} - ed inoltre \errval{EMFILE} e \errval{ENFILE}. -} -\end{prototype} + ed inoltre \errval{EMFILE} e \errval{ENFILE} nel loro significato generico. + +} +\end{funcproto} La funzione consente di creare o modificare le caratteristiche di un file descriptor speciale su cui ricevere le notifiche della ricezione di @@ -2020,11 +2078,11 @@ loro inserimento nella maschera verrà ignorato senza generare errori. L'argomento \param{flags} consente di impostare direttamente in fase di creazione due flag per il file descriptor analoghi a quelli che si possono impostare con una creazione ordinaria con \func{open}, evitando una -impostazione successiva con \func{fcntl}.\footnote{questo è un argomento - aggiuntivo, introdotto con la versione fornita a partire dal kernel 2.6.27, - per kernel precedenti il valore deve essere nullo.} L'argomento deve essere -specificato come maschera binaria dei valori riportati in -tab.~\ref{tab:signalfd_flags}. +impostazione successiva con \func{fcntl}.\footnote{si ricordi che questo è un + argomento aggiuntivo, introdotto con la versione fornita a partire dal + kernel 2.6.27, per kernel precedenti il valore deve essere nullo.} +L'argomento deve essere specificato come maschera binaria dei valori riportati +in tab.~\ref{tab:signalfd_flags}. \begin{table}[htb] \centering @@ -2051,9 +2109,10 @@ ordinaria dei segnali indicati da \param{mask}; questa, se si vuole effettuare la ricezione tramite il file descriptor, dovrà essere disabilitata esplicitamente bloccando gli stessi segnali con \func{sigprocmask}, altrimenti verranno comunque eseguite le azioni di default (o un eventuale gestore -installato in precedenza).\footnote{il blocco non ha invece nessun effetto sul - file descriptor restituito da \func{signalfd}, dal quale sarà possibile - pertanto ricevere qualunque segnale, anche se questo risultasse bloccato.} +installato in precedenza). Il blocco non ha invece nessun effetto sul file +descriptor restituito da \func{signalfd}, dal quale sarà possibile pertanto +ricevere qualunque segnale, anche se questo risultasse bloccato. + Si tenga presente inoltre che la lettura di una struttura \struct{signalfd\_siginfo} relativa ad un segnale pendente è equivalente alla esecuzione di un gestore, vale a dire che una volta letta il segnale non sarà @@ -2072,12 +2131,12 @@ diversi. Inoltre è anche possibile tenere sotto osservazione lo stesso segnale con più file descriptor, anche se la pratica è sconsigliata; in tal caso la ricezione del segnale potrà essere effettuata con una lettura da uno qualunque dei file descriptor a cui è associato, ma questa potrà essere eseguita -soltanto una volta.\footnote{questo significa che tutti i file descriptor su - cui è presente lo stesso segnale risulteranno pronti in lettura per le - funzioni di \textit{I/O multiplexing}, ma una volta eseguita la lettura su - uno di essi il segnale sarà considerato ricevuto ed i relativi dati non - saranno più disponibili sugli altri file descriptor, che (a meno di una - ulteriore occorrenza del segnale nel frattempo) di non saranno più pronti.} +soltanto una volta. Questo significa che tutti i file descriptor su cui è +presente lo stesso segnale risulteranno pronti in lettura per le funzioni di +\textit{I/O multiplexing}, ma una volta eseguita la lettura su uno di essi il +segnale sarà considerato ricevuto ed i relativi dati non saranno più +disponibili sugli altri file descriptor, che (a meno di una ulteriore +occorrenza del segnale nel frattempo) di non saranno più pronti. Quando il file descriptor per la ricezione dei segnali non serve più potrà essere chiuso con \func{close} liberando tutte le risorse da esso allocate. In @@ -2114,7 +2173,7 @@ successivo con \func{fcntl}. \begin{figure}[!htb] \footnotesize \centering - \begin{minipage}[c]{\textwidth} + \begin{minipage}[c]{0.90\textwidth} \includestruct{listati/signalfd_siginfo.h} \end{minipage} \normalsize @@ -2133,6 +2192,17 @@ dimensione maggiore potranno essere letti in unica soluzione i dati relativi ad eventuali più segnali pendenti, fino al numero massimo di strutture \struct{signalfd\_siginfo} che possono rientrare nel buffer. +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{\codesamplewidth} + \includecodesample{listati/FifoReporter-init.c} + \end{minipage} + \normalsize + \caption{Sezione di inizializzazione del codice del programma + \file{FifoReporter.c}.} + \label{fig:fiforeporter_code_init} +\end{figure} + Il contenuto di \struct{signalfd\_siginfo} ricalca da vicino quella della analoga struttura \struct{siginfo\_t} (illustrata in fig.~\ref{fig:sig_siginfo_t}) usata dall'interfaccia ordinaria dei segnali, e @@ -2152,43 +2222,32 @@ codice completo si trova al solito nei sorgenti allegati alla guida (nel file In fig.~\ref{fig:fiforeporter_code_init} si è riportata la parte iniziale del programma in cui vengono effettuate le varie inizializzazioni necessarie per l'uso di \itindex{epoll} \textit{epoll} e \func{signalfd}, a partire -(\texttt{\small 12--16}) dalla definizione delle varie variabili e strutture +(\texttt{\small 12-16}) dalla definizione delle varie variabili e strutture necessarie. Al solito si è tralasciata la parte dedicata alla decodifica delle opzioni che consentono ad esempio di cambiare il nome del file associato alla fifo. -\begin{figure}[!htbp] - \footnotesize \centering - \begin{minipage}[c]{\codesamplewidth} - \includecodesample{listati/FifoReporter-init.c} - \end{minipage} - \normalsize - \caption{Sezione di inizializzazione del codice del programma - \file{FifoReporter.c}.} - \label{fig:fiforeporter_code_init} -\end{figure} - -Il primo passo (\texttt{\small 19--20}) è la creazione di un file descriptor +Il primo passo (\texttt{\small 19-20}) è la creazione di un file descriptor \texttt{epfd} di \itindex{epoll} \textit{epoll} con \func{epoll\_create} che è quello che useremo per il controllo degli altri. É poi necessario disabilitare la ricezione dei segnali (nel caso \signal{SIGINT}, \signal{SIGQUIT} e \signal{SIGTERM}) per i quali si vuole la notifica tramite -file descriptor. Per questo prima li si inseriscono (\texttt{\small 22--25}) +file descriptor. Per questo prima li si inseriscono (\texttt{\small 22-25}) in una \index{maschera~dei~segnali} maschera di segnali \texttt{sigmask} che useremo con (\texttt{\small 26}) \func{sigprocmask} per disabilitarli. Con la -stessa maschera si potrà per passare all'uso (\texttt{\small 28--29}) di +stessa maschera si potrà per passare all'uso (\texttt{\small 28-29}) di \func{signalfd} per abilitare la notifica sul file descriptor -\var{sigfd}. Questo poi (\texttt{\small 30--33}) dovrà essere aggiunto con +\var{sigfd}. Questo poi (\texttt{\small 30-33}) dovrà essere aggiunto con \func{epoll\_ctl} all'elenco di file descriptor controllati con \texttt{epfd}. -Occorrerà infine (\texttt{\small 35--38}) creare la \textit{named fifo} se -questa non esiste ed aprirla per la lettura (\texttt{\small 39--40}); una +Occorrerà infine (\texttt{\small 35-38}) creare la \textit{named fifo} se +questa non esiste ed aprirla per la lettura (\texttt{\small 39-40}); una volta fatto questo sarà necessario aggiungere il relativo file descriptor (\var{fifofd}) a quelli osservati da \itindex{epoll} \textit{epoll} in maniera del tutto analoga a quanto fatto con quello relativo alla notifica dei segnali. -\begin{figure}[!htbp] +\begin{figure}[!htb] \footnotesize \centering \begin{minipage}[c]{\codesamplewidth} \includecodesample{listati/FifoReporter-main.c} @@ -2199,118 +2258,117 @@ segnali. \end{figure} Una volta completata l'inizializzazione verrà eseguito indefinitamente il -ciclo principale del programma (\texttt{\small 2--45}) che si è riportato in +ciclo principale del programma (\texttt{\small 2-45}) che si è riportato in fig.~\ref{fig:fiforeporter_code_body}, fintanto che questo non riceva un segnale di \signal{SIGINT} (ad esempio con la pressione di \texttt{C-c}). Il -ciclo prevede che si attenda (\texttt{\small 2--3}) la presenza di un file -descriptor pronto in lettura con \func{epoll\_wait},\footnote{si ricordi che - entrambi i file descriptor \var{fifofd} e \var{sigfd} sono stati posti in - osservazioni per eventi di tipo \const{EPOLLIN}.} che si bloccherà fintanto -che non siano stati scritti dati sulla fifo o che non sia arrivato un -segnale.\footnote{per semplificare il codice non si è trattato il caso in cui - \func{epoll\_wait} viene interrotta da un segnale, assumendo che tutti - quelli che possano interessare siano stati predisposti per la notifica - tramite file descriptor, per gli altri si otterrà semplicemente l'uscita dal - programma.} +ciclo prevede che si attenda (\texttt{\small 2-3}) la presenza di un file +descriptor pronto in lettura con \func{epoll\_wait} (si ricordi che entrambi i +file descriptor \var{fifofd} e \var{sigfd} sono stati posti in osservazioni +per eventi di tipo \const{EPOLLIN}) che si bloccherà fintanto che non siano +stati scritti dati sulla fifo o che non sia arrivato un segnale.\footnote{per + semplificare il codice non si è trattato il caso in cui \func{epoll\_wait} + viene interrotta da un segnale, assumendo che tutti quelli che possano + interessare siano stati predisposti per la notifica tramite file descriptor, + per gli altri si otterrà semplicemente l'uscita dal programma.} Anche se in questo caso i file descriptor pronti possono essere al più due, si è comunque adottato un approccio generico in cui questi verranno letti -all'interno di un opportuno ciclo (\texttt{\small 5--44}) sul numero +all'interno di un opportuno ciclo (\texttt{\small 5-44}) sul numero restituito da \func{epoll\_wait}, esaminando i risultati presenti nel vettore \var{events} all'interno di una catena di condizionali alternativi sul valore -del file descriptor riconosciuto come pronto.\footnote{controllando cioè a - quale dei due file descriptor possibili corrisponde il campo relativo, - \var{events[i].data.fd}.} +del file descriptor riconosciuto come pronto, controllando cioè a quale dei +due file descriptor possibili corrisponde il campo relativo, +\var{events[i].data.fd}. -Il primo condizionale (\texttt{\small 6--24}) è relativo al caso che si sia +Il primo condizionale (\texttt{\small 6-24}) è relativo al caso che si sia ricevuto un segnale e che il file descriptor pronto corrisponda (\texttt{\small 6}) a \var{sigfd}. Dato che in generale si possono ricevere anche notifiche relativi a più di un singolo segnale, si è scelto di leggere una struttura \struct{signalfd\_siginfo} alla volta, eseguendo la lettura -all'interno di un ciclo (\texttt{\small 8--24}) che prosegue fintanto che vi +all'interno di un ciclo (\texttt{\small 8-24}) che prosegue fintanto che vi siano dati da leggere. -Per questo ad ogni lettura si esamina (\texttt{\small 9--14}) se il valore di +Per questo ad ogni lettura si esamina (\texttt{\small 9-14}) se il valore di ritorno della funzione \func{read} è negativo, uscendo dal programma (\texttt{\small 11}) in caso di errore reale, o terminando il ciclo (\texttt{\small 13}) con un \texttt{break} qualora si ottenga un errore di -\errcode{EAGAIN} per via dell'esaurimento dei dati.\footnote{si ricordi come - sia la fifo che il file descriptor per i segnali siano stati aperti in - modalità non-bloccante, come previsto per l’\textit{I/O multiplexing}, - pertanto ci si aspetta di ricevere un errore di \errcode{EAGAIN} quando non - vi saranno più dati da leggere.} +\errcode{EAGAIN} per via dell'esaurimento dei dati. Si ricordi infatti come +sia la fifo che il file descriptor per i segnali siano stati aperti in +modalità non-bloccante, come previsto per l’\textit{I/O multiplexing}, +pertanto ci si aspetta di ricevere un errore di \errcode{EAGAIN} quando non vi +saranno più dati da leggere. In presenza di dati invece il programma proseguirà l'esecuzione stampando -(\texttt{\small 19--20}) il nome del segnale ottenuto all'interno della -struttura \struct{signalfd\_siginfo} letta in \var{siginf}\footnote{per la - stampa si è usato il vettore \var{sig\_names} a ciascun elemento del quale - corrisponde il nome del segnale avente il numero corrispondente, la cui - definizione si è omessa dal codice di fig.~\ref{fig:fiforeporter_code_init} - per brevità.} ed il \textit{pid} del processo da cui lo ha ricevuto; inoltre -(\texttt{\small 21--24}) si controllerà anche se il segnale ricevuto è +(\texttt{\small 19-20}) il nome del segnale ottenuto all'interno della +struttura \struct{signalfd\_siginfo} letta in \var{siginf} ed il \textit{pid} +del processo da cui lo ha ricevuto;\footnote{per la stampa si è usato il + vettore \var{sig\_names} a ciascun elemento del quale corrisponde il nome + del segnale avente il numero corrispondente, la cui definizione si è omessa + dal codice di fig.~\ref{fig:fiforeporter_code_init} per brevità.} inoltre +(\texttt{\small 21-24}) si controllerà anche se il segnale ricevuto è \signal{SIGINT}, che si è preso come segnale da utilizzare per la terminazione del programma, che verrà eseguita dopo aver rimosso il file della \textit{name fifo}. -Il secondo condizionale (\texttt{\small 26--39}) è invece relativo al caso in +Il secondo condizionale (\texttt{\small 26-39}) è invece relativo al caso in cui ci siano dati pronti in lettura sulla fifo e che il file descriptor pronto corrisponda (\texttt{\small 26}) a \var{fifofd}. Di nuovo si effettueranno le -letture in un ciclo (\texttt{\small 28--39}) ripetendole fin tanto che la +letture in un ciclo (\texttt{\small 28-39}) ripetendole fin tanto che la funzione \func{read} non restituisce un errore di \errcode{EAGAIN} -(\texttt{\small 29--35}).\footnote{il procedimento è lo stesso adottato per il - file descriptor associato al segnale, in cui si esce dal programma in caso - di errore reale, in questo caso però alla fine dei dati prima di uscire si - stampa anche (\texttt{\small 32}) un messaggio di chiusura.} Se invece vi -sono dati validi letti dalla fifo si inserirà (\texttt{\small 36}) una -terminazione di stringa sul buffer e si stamperà il tutto (\texttt{\small - 37--38}) sullo \textit{standard output}. L'ultimo condizionale -(\texttt{\small 40--44}) è semplicemente una condizione di cattura per una +(\texttt{\small 29-35}). Il procedimento è lo stesso adottato per il file +descriptor associato al segnale, in cui si esce dal programma in caso di +errore reale, in questo caso però alla fine dei dati prima di uscire si stampa +anche (\texttt{\small 32}) un messaggio di chiusura. + +Se invece vi sono dati validi letti dalla fifo si inserirà (\texttt{\small + 36}) una terminazione di stringa sul buffer e si stamperà il tutto +(\texttt{\small 37-38}) sullo \textit{standard output}. L'ultimo condizionale +(\texttt{\small 40-44}) è semplicemente una condizione di cattura per una eventualità che comunque non dovrebbe mai verificarsi, e che porta alla uscita dal programma con una opportuna segnalazione di errore. A questo punto si potrà eseguire il comando lanciandolo su un terminale, ed osservarne le reazioni agli eventi generati da un altro terminale; lanciando il programma otterremo qualcosa del tipo: -\begin{Verbatim} -piccardi@hain:~/gapil/sources$ ./a.out +\begin{Console} +piccardi@hain:~/gapil/sources$ \textbf{./a.out} FifoReporter starting, pid 4568 -\end{Verbatim} +\end{Console} %$ e scrivendo qualcosa sull'altro terminale con: -\begin{Verbatim} -root@hain:~# echo prova > /tmp/reporter.fifo -\end{Verbatim} +\begin{Console} +root@hain:~# \textbf{echo prova > /tmp/reporter.fifo} +\end{Console} si otterrà: -\begin{Verbatim} +\begin{Console} Message from fifo: prova end message -\end{Verbatim} +\end{Console} mentre inviando un segnale: -\begin{Verbatim} -root@hain:~# kill 4568 -\end{Verbatim} +\begin{Console} +root@hain:~# \textbf{kill 4568} +\end{Console} si avrà: -\begin{Verbatim} +\begin{Console} Signal received: Got SIGTERM From pid 3361 -\end{Verbatim} +\end{Console} ed infine premendo \texttt{C-\bslash} sul terminale in cui è in esecuzione si vedrà: -\begin{Verbatim} -^\Signal received: +\begin{Console} +^\\Signal received: Got SIGQUIT From pid 0 -\end{Verbatim} +\end{Console} e si potrà far uscire il programma con \texttt{C-c} ottenendo: -\begin{Verbatim} +\begin{Console} ^CSignal received: Got SIGINT From pid 0 SIGINT means exit -\end{Verbatim} - +\end{Console} Lo stesso paradigma di notifica tramite file descriptor usato per i segnali è stato adottato anche per i timer. In questo caso, rispetto a quanto visto in @@ -2319,10 +2377,10 @@ file descriptor senza dover ricorrere ad altri meccanismi di notifica come un segnale o un \textit{thread}. Di nuovo questo ha il vantaggio di poter utilizzare le funzioni dell'\textit{I/O multiplexing} per attendere allo stesso tempo la disponibilità di dati o la ricezione della scadenza di un -timer.\footnote{in realtà per questo sarebbe già sufficiente \func{signalfd} - per ricevere i segnali associati ai timer, ma la nuova interfaccia - semplifica notevolmente la gestione e consente di fare tutto con una sola - \textit{system call}.} +timer. In realtà per questo sarebbe già sufficiente \func{signalfd} per +ricevere i segnali associati ai timer, ma la nuova interfaccia semplifica +notevolmente la gestione e consente di fare tutto con una sola \textit{system + call}. Le funzioni di questa nuova interfaccia ricalcano da vicino la struttura delle analoghe versioni ordinarie introdotte con lo standard POSIX.1-2001, che @@ -2332,30 +2390,33 @@ abbiamo già illustrato in sez.~\ref{sec:sig_timer_adv}.\footnote{questa reintrodotta in una forma considerata adeguata nel kernel 2.6.25, il supporto nelle \acr{glibc} è stato introdotto a partire dalla versione 2.8.6, la versione del kernel 2.6.22, presente solo su questo kernel, non è - supportata e non deve essere usata.} La prima funzione prevista, quella che -consente di creare un timer, è \funcd{timerfd\_create}, il cui prototipo è: -\begin{prototype}{sys/timerfd.h} - {int timerfd\_create(int clockid, int flags)} + supportata e non deve essere usata.} La prima funzione di sistema prevista, +quella che consente di creare un timer, è \funcd{timerfd\_create}, il cui +prototipo è: - Crea un timer associato ad un file descriptor per la notifica. +\begin{funcproto}{ +\fhead{sys/timerfd.h} +\fdecl{int timerfd\_create(int clockid, int flags)} - \bodydesc{La funzione restituisce un numero di file descriptor in caso di - successo o $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno - dei valori: +\fdesc{Crea un timer associato ad un file descriptor per la notifica.} +} + +{La funzione ritorna un numero di file descriptor in caso di successo e $-1$ + per un errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} \item[\errcode{EINVAL}] l'argomento \param{clockid} non è \const{CLOCK\_MONOTONIC} o \const{CLOCK\_REALTIME}, o l'argomento \param{flag} non è valido, o è diverso da zero per kernel precedenti il 2.6.27. - \item[\errcode{ENOMEM}] non c'è memoria sufficiente per creare un nuovo file - descriptor di \func{signalfd}. \item[\errcode{ENODEV}] il kernel non può montare internamente il dispositivo per la gestione anonima degli \itindex{inode} \textit{inode} associati al file descriptor. + \item[\errcode{ENOMEM}] non c'è memoria sufficiente per creare un nuovo file + descriptor di \func{signalfd}. \end{errlist} - ed inoltre \errval{EMFILE} e \errval{ENFILE}. -} -\end{prototype} + ed inoltre \errval{EMFILE} e \errval{ENFILE} nel loro significato generico. +} +\end{funcproto} La funzione prende come primo argomento un intero che indica il tipo di orologio a cui il timer deve fare riferimento, i valori sono gli stessi delle @@ -2393,41 +2454,43 @@ tab.~\ref{tab:timerfd_flags}. In caso di successo la funzione restituisce un file descriptor sul quale verranno notificate le scadenze dei timer. Come per quelli restituiti da \func{signalfd} anche questo file descriptor segue la semantica dei sistemi -unix-like, in particolare resta aperto attraverso una \func{exec},\footnote{a - meno che non si sia impostato il flag di \textit{close-on exec} con - \const{TFD\_CLOEXEC}.} e viene duplicato attraverso una \func{fork}; questa +unix-like, in particolare resta aperto attraverso una \func{exec} (a meno che +non si sia impostato il flag di \textit{close-on exec} con +\const{TFD\_CLOEXEC}) e viene duplicato attraverso una \func{fork}; questa ultima caratteristica comporta però che anche il figlio può utilizzare i dati di un timer creato nel padre, a differenza di quanto avviene invece con i -timer impostati con le funzioni ordinarie.\footnote{si ricordi infatti che, - come illustrato in sez.~\ref{sec:proc_fork}, allarmi, timer e segnali - pendenti nel padre vengono cancellati per il figlio dopo una \func{fork}.} +timer impostati con le funzioni ordinarie. Si ricordi infatti che, come +illustrato in sez.~\ref{sec:proc_fork}, allarmi, timer e segnali pendenti nel +padre vengono cancellati per il figlio dopo una \func{fork}. Una volta creato il timer con \func{timerfd\_create} per poterlo utilizzare occorre \textsl{armarlo} impostandone un tempo di scadenza ed una eventuale -periodicità di ripetizione, per farlo si usa la funzione omologa di -\func{timer\_settime} per la nuova interfaccia; questa è +periodicità di ripetizione, per farlo si usa una funzione di sistema omologa +di \func{timer\_settime} per la nuova interfaccia; questa è \funcd{timerfd\_settime} ed il suo prototipo è: -\begin{prototype}{sys/timerfd.h} - {int timerfd\_settime(int fd, int flags, - const struct itimerspec *new\_value, - struct itimerspec *old\_value)} - Crea un timer associato ad un file descriptor per la notifica. +\begin{funcproto}{ +\fhead{sys/timerfd.h} +\fdecl{int timerfd\_settime(int fd, int flags, + const struct itimerspec *new\_value,\\ +\phantom{int timerfd\_settime(}struct itimerspec *old\_value)} - \bodydesc{La funzione restituisce un numero di file descriptor in caso di - successo o $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno - dei valori: +\fdesc{Crea un timer associato ad un file descriptor per la notifica.} +} + +{La funzione ritorna un numero di file descriptor in caso di successo e $-1$ + per un errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} \item[\errcode{EBADF}] l'argomento \param{fd} non corrisponde ad un file descriptor. + \item[\errcode{EFAULT}] o \param{new\_value} o \param{old\_value} non sono + puntatori validi. \item[\errcode{EINVAL}] il file descriptor \param{fd} non è stato ottenuto con \func{timerfd\_create}, o i valori di \param{flag} o dei campi \var{tv\_nsec} in \param{new\_value} non sono validi. - \item[\errcode{EFAULT}] o \param{new\_value} o \param{old\_value} non sono - puntatori validi. \end{errlist} -} -\end{prototype} +} +\end{funcproto} In questo caso occorre indicare su quale timer si intende operare specificando come primo argomento il file descriptor ad esso associato, che deve essere @@ -2450,14 +2513,16 @@ valori possibili rispettivamente soltanto $0$ e L'ultima funzione prevista dalla nuova interfaccia è \funcd{timerfd\_gettime}, che è l'analoga di \func{timer\_gettime}, il suo prototipo è: -\begin{prototype}{sys/timerfd.h} - {int timerfd\_gettime(int fd, struct itimerspec *curr\_value)} - Crea un timer associato ad un file descriptor per la notifica. +\begin{funcproto}{ +\fhead{sys/timerfd.h} +\fdecl{int timerfd\_gettime(int fd, struct itimerspec *curr\_value)} - \bodydesc{La funzione restituisce un numero di file descriptor in caso di - successo o $-1$ in caso di errore, nel qual caso \var{errno} assumerà uno - dei valori: +\fdesc{Crea un timer associato ad un file descriptor per la notifica.} +} + +{La funzione ritorna un numero di file descriptor in caso di successo e $-1$ + per un errore, nel qual caso \var{errno} assumerà uno dei valori: \begin{errlist} \item[\errcode{EBADF}] l'argomento \param{fd} non corrisponde ad un file descriptor. @@ -2465,24 +2530,18 @@ che è l'analoga di \func{timer\_gettime}, il suo prototipo è: con \func{timerfd\_create}. \item[\errcode{EFAULT}] o \param{curr\_value} non è un puntatore valido. \end{errlist} -} -\end{prototype} - - - - +ed inoltre nel suo significato generico. + +} +\end{funcproto} Questo infatti diverrà pronto in lettura per tutte le varie funzioni dell'I/O multiplexing in presenza di una o più scadenze del timer ad esso associato. Inoltre sarà possibile ottenere il numero di volte che il timer è scaduto -dalla ultima impostazione - -che può essere -usato per leggere le notifiche delle scadenze dei timer. Queste possono essere -ottenute leggendo in maniera ordinaria il file descriptor con una \func{read}, - - +dalla ultima impostazione che può essere usato per leggere le notifiche delle +scadenze dei timer. Queste possono essere ottenute leggendo in maniera +ordinaria il file descriptor con una \func{read}, % TODO trattare qui eventfd, timerfd introdotte con il 2.6.22 @@ -3232,17 +3291,17 @@ funzioni di ausilio è riportato in fig.~\ref{fig:inotify_monitor_example}. \end{figure} Una volta completata la scansione delle opzioni il corpo principale del -programma inizia controllando (\texttt{\small 11--15}) che sia rimasto almeno +programma inizia controllando (\texttt{\small 11-15}) che sia rimasto almeno un argomento che indichi quale file o directory mettere sotto osservazione (e qualora questo non avvenga esce stampando la pagina di aiuto); dopo di che -passa (\texttt{\small 16--20}) all'inizializzazione di \textit{inotify} +passa (\texttt{\small 16-20}) all'inizializzazione di \textit{inotify} ottenendo con \func{inotify\_init} il relativo file descriptor (oppure usce in caso di errore). -Il passo successivo è aggiungere (\texttt{\small 21--30}) alla coda di +Il passo successivo è aggiungere (\texttt{\small 21-30}) alla coda di notifica gli opportuni osservatori per ciascuno dei file o directory indicati all'invocazione del comando; questo viene fatto eseguendo un ciclo -(\texttt{\small 22--29}) fintanto che la variabile \var{i}, inizializzata a +(\texttt{\small 22-29}) fintanto che la variabile \var{i}, inizializzata a zero (\texttt{\small 21}) all'inizio del ciclo, è minore del numero totale di argomenti rimasti. All'interno del ciclo si invoca (\texttt{\small 23}) \func{inotify\_add\_watch} per ciascuno degli argomenti, usando la maschera @@ -3251,7 +3310,7 @@ nella scansione delle opzioni), in caso di errore si esce dal programma altrimenti si incrementa l'indice (\texttt{\small 29}). Completa l'inizializzazione di \textit{inotify} inizia il ciclo principale -(\texttt{\small 32--56}) del programma, nel quale si resta in attesa degli +(\texttt{\small 32-56}) del programma, nel quale si resta in attesa degli eventi che si intendono osservare. Questo viene fatto eseguendo all'inizio del ciclo (\texttt{\small 33}) una \func{read} che si bloccherà fintanto che non si saranno verificati eventi. @@ -3262,13 +3321,13 @@ dimensioni adeguate, inizializzato in (\texttt{\small 7}) ad un valore di approssimativamente 512 eventi.\footnote{si ricordi che la quantità di dati restituita da \textit{inotify} è variabile a causa della diversa lunghezza del nome del file restituito insieme a \struct{inotify\_event}.} In caso di -errore di lettura (\texttt{\small 35--40}) il programma esce con un messaggio -di errore (\texttt{\small 37--39}), a meno che non si tratti di una +errore di lettura (\texttt{\small 35-40}) il programma esce con un messaggio +di errore (\texttt{\small 37-39}), a meno che non si tratti di una interruzione della \textit{system call}, nel qual caso (\texttt{\small 36}) si ripete la lettura. Se la lettura è andata a buon fine invece si esegue un ciclo (\texttt{\small - 43--52}) per leggere tutti gli eventi restituiti, al solito si inizializza + 43-52}) per leggere tutti gli eventi restituiti, al solito si inizializza l'indice \var{i} a zero (\texttt{\small 42}) e si ripetono le operazioni (\texttt{\small 43}) fintanto che esso non supera il numero di byte restituiti in lettura. Per ciascun evento all'interno del ciclo si assegna\footnote{si @@ -3281,7 +3340,7 @@ comando sfruttando il fatto che i \textit{watch descriptor} vengono assegnati in ordine progressivo crescente a partire da 1. Qualora sia presente il riferimento ad un nome di file associato all'evento lo -si stampa (\texttt{\small 47--49}); si noti come in questo caso si sia +si stampa (\texttt{\small 47-49}); si noti come in questo caso si sia utilizzato il valore del campo \var{event->len} e non al fatto che \var{event->name} riporti o meno un puntatore nullo.\footnote{l'interfaccia infatti, qualora il nome non sia presente, non avvalora il campo @@ -3298,15 +3357,16 @@ aggiornare l'indice \var{i} per farlo puntare all'evento successivo. Se adesso usiamo il programma per mettere sotto osservazione una directory, e da un altro terminale eseguiamo il comando \texttt{ls} otterremo qualcosa del tipo di: -\begin{verbatim} -piccardi@gethen:~/gapil/sources$ ./inotify_monitor -a /home/piccardi/gapil/ +\begin{Console} +piccardi@gethen:~/gapil/sources$ \textbf{./inotify_monitor -a /home/piccardi/gapil/} Watch descriptor 1 Observed event on /home/piccardi/gapil/ IN_OPEN, Watch descriptor 1 Observed event on /home/piccardi/gapil/ IN_CLOSE_NOWRITE, -\end{verbatim} +\end{Console} +%$ I lettori più accorti si saranno resi conto che nel ciclo di lettura degli eventi appena illustrato non viene trattato il caso particolare in cui la @@ -4915,10 +4975,10 @@ fig.~\ref{fig:splicecp_data_flux}. \end{figure} Una volta trattate le opzioni il programma verifica che restino -(\texttt{\small 13--16}) i due argomenti che indicano il file sorgente ed il +(\texttt{\small 13-16}) i due argomenti che indicano il file sorgente ed il file destinazione. Il passo successivo è aprire il file sorgente -(\texttt{\small 18--22}), quello di destinazione (\texttt{\small 23--27}) ed -infine (\texttt{\small 28--31}) la \textit{pipe} che verrà usata come buffer. +(\texttt{\small 18-22}), quello di destinazione (\texttt{\small 23-27}) ed +infine (\texttt{\small 28-31}) la \textit{pipe} che verrà usata come buffer. \begin{figure}[!htbp] \footnotesize \centering @@ -4931,8 +4991,8 @@ infine (\texttt{\small 28--31}) la \textit{pipe} che verrà usata come buffer. \label{fig:splice_example} \end{figure} -Il ciclo principale (\texttt{\small 33--58}) inizia con la lettura dal file -sorgente tramite la prima \func{splice} (\texttt{\small 34--35}), in questo +Il ciclo principale (\texttt{\small 33-58}) inizia con la lettura dal file +sorgente tramite la prima \func{splice} (\texttt{\small 34-35}), in questo caso si è usato come primo argomento il file descriptor del file sorgente e come terzo quello del capo in scrittura della \textit{pipe} (il funzionamento delle \textit{pipe} e l'uso della coppia di file descriptor ad esse associati @@ -4947,13 +5007,13 @@ valore di uscita in \var{nread} che indica quanti byte sono stati letti, se detto valore è nullo (\texttt{\small 36}) questo significa che si è giunti alla fine del file sorgente e pertanto l'operazione di copia è conclusa e si può uscire dal ciclo arrivando alla conclusione del programma (\texttt{\small - 59}). In caso di valore negativo (\texttt{\small 37--44}) c'è stato un + 59}). In caso di valore negativo (\texttt{\small 37-44}) c'è stato un errore ed allora si ripete la lettura (\texttt{\small 36}) se questo è dovuto ad una interruzione, o altrimenti si esce con un messaggio di errore -(\texttt{\small 41--43}). +(\texttt{\small 41-43}). Una volta completata con successo la lettura si avvia il ciclo di scrittura -(\texttt{\small 45--57}); questo inizia (\texttt{\small 46--47}) con la +(\texttt{\small 45-57}); questo inizia (\texttt{\small 46-47}) con la seconda \func{splice} che cerca di scrivere gli \var{nread} byte letti, si noti come in questo caso il primo argomento faccia di nuovo riferimento alla \textit{pipe} (in questo caso si usa il capo in lettura, per i dettagli si @@ -4963,7 +5023,7 @@ del file di destinazione. Di nuovo si controlla il numero di byte effettivamente scritti restituito in \var{nwrite} e in caso di errore al solito si ripete la scrittura se questo è dovuto a una interruzione o si esce con un messaggio negli altri casi -(\texttt{\small 48--55}). Infine si chiude il ciclo di scrittura sottraendo +(\texttt{\small 48-55}). Infine si chiude il ciclo di scrittura sottraendo (\texttt{\small 57}) il numero di byte scritti a quelli di cui è richiesta la scrittura,\footnote{in questa parte del ciclo \var{nread}, il cui valore iniziale è dato dai byte letti dalla precedente chiamata a \func{splice}, @@ -5104,25 +5164,25 @@ allegati alla guida. \label{fig:tee_example} \end{figure} -La prima parte del programma (\texttt{\small 10--35}) si cura semplicemente di -controllare (\texttt{\small 11--14}) che sia stato fornito almeno un argomento -(il nome del file su cui scrivere), di aprirlo ({\small 15--19}) e che sia lo -standard input (\texttt{\small 20--27}) che lo standard output (\texttt{\small - 28--35}) corrispondano ad una \textit{pipe}. +La prima parte del programma (\texttt{\small 10-35}) si cura semplicemente di +controllare (\texttt{\small 11-14}) che sia stato fornito almeno un argomento +(il nome del file su cui scrivere), di aprirlo ({\small 15-19}) e che sia lo +standard input (\texttt{\small 20-27}) che lo standard output (\texttt{\small + 28-35}) corrispondano ad una \textit{pipe}. -Il ciclo principale (\texttt{\small 37--58}) inizia con la chiamata a +Il ciclo principale (\texttt{\small 37-58}) inizia con la chiamata a \func{tee} che duplica il contenuto dello standard input sullo standard output (\texttt{\small 39}), questa parte è del tutto analoga ad una lettura ed infatti come nell'esempio di fig.~\ref{fig:splice_example} si controlla il valore di ritorno della funzione in \var{len}; se questo è nullo significa che non ci sono più dati da leggere e si chiude il ciclo (\texttt{\small 40}), se è negativo c'è stato un errore, ed allora si ripete la chiamata se questo è -dovuto ad una interruzione (\texttt{\small 42--44}) o si stampa un messaggio -di errore e si esce negli altri casi (\texttt{\small 44--47}). +dovuto ad una interruzione (\texttt{\small 42-44}) o si stampa un messaggio +di errore e si esce negli altri casi (\texttt{\small 44-47}). Una volta completata la copia dei dati sullo standard output si possono estrarre dalla standard input e scrivere sul file, di nuovo su usa un ciclo di -scrittura (\texttt{\small 50--58}) in cui si ripete una chiamata a +scrittura (\texttt{\small 50-58}) in cui si ripete una chiamata a \func{splice} (\texttt{\small 51}) fintanto che non si sono scritti tutti i \var{len} byte copiati in precedenza con \func{tee} (il funzionamento è identico all'analogo ciclo di scrittura del precedente esempio di