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 \textit{deadlock}\index{\textit{deadlock}}.
+potrebbe addirittura arrivare ad un \textit{deadlock}\itindex{deadlock}.
Abbiamo già accennato in sez.~\ref{sec:file_open} che è possibile prevenire
questo tipo di comportamento delle funzioni di I/O aprendo un file in
restituendo l'errore \errcode{EAGAIN}. L'utilizzo di questa modalità di I/O
permette di risolvere il problema controllando a turno i vari file descriptor,
in un ciclo in cui si ripete l'accesso fintanto che esso non viene garantito.
-Ovviamente questa tecnica, detta \textit{polling}\index{\textit{polling}}, è
+Ovviamente questa tecnica, detta \textit{polling}\itindex{polling}, è
estremamente inefficiente: si tiene costantemente impiegata la CPU solo per
eseguire in continuazione delle system call che nella gran parte dei casi
falliranno.
\param{exceptfds}), non diventa attivo, per un tempo massimo specificato da
\param{timeout}.
-\index{\textit{file~descriptor~set}|(}
+\itindbeg{file~descriptor~set}
+
Per specificare quali file descriptor si intende \textsl{selezionare}, la
funzione usa un particolare oggetto, il \textit{file descriptor set},
identificato dal tipo \type{fd\_set}, che serve ad identificare un insieme di
file descriptor, in maniera analoga a come un
-\index{\textit{signal~set}}\textit{signal set} (vedi
-sez.~\ref{sec:sig_sigset}) identifica un insieme di segnali. Per la
-manipolazione di questi \textit{file descriptor set} si possono usare delle
-opportune macro di preprocessore:
+\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{FD\_ZERO(fd\_set *set)}
+ \funcdecl{void \macro{FD\_ZERO}(fd\_set *set)}
Inizializza l'insieme (vuoto).
- \funcdecl{FD\_SET(int fd, fd\_set *set)}
+ \funcdecl{void \macro{FD\_SET}(int fd, fd\_set *set)}
Inserisce il file descriptor \param{fd} nell'insieme.
- \funcdecl{FD\_CLR(int fd, fd\_set *set)}
+ \funcdecl{void \macro{FD\_CLR}(int fd, fd\_set *set)}
Rimuove il file descriptor \param{fd} nell'insieme.
- \funcdecl{FD\_ISSET(int fd, fd\_set *set)}
+ \funcdecl{int \macro{FD\_ISSET}(int fd, fd\_set *set)}
Controlla se il file descriptor \param{fd} è nell'insieme.
\end{functions}
Dato che in genere non si tengono mai sotto controllo fino a
\const{FD\_SETSIZE} file contemporaneamente la funzione richiede di
-specificare qual'è il numero massimo dei file descriptor indicati nei tre
+specificare qual è il numero massimo dei 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{n}, che
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.
-\index{\textit{file~descriptor~set}|)}
+\itindend{file~descriptor~set}
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 \const{FD\_ISSET}. Se invece si
+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
funzione.
L'uso di \param{sigmask} è stato introdotto allo scopo di prevenire possibili
-race condition\index{\textit{race~condition}} quando ci si deve porre in
+\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 variabile globale e controllare questa
nel corpo principale del programma; abbiamo visto in
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
alla gestione dati con un ciclo del tipo:
-\includecodesnip{listati/select_race.c} qui però emerge una race
-condition,\index{\textit{race~condition}} perché se il segnale arriva prima
-della chiamata a \func{select}, questa non verrà interrotta, e la ricezione
-del segnale non sarà rilevata.
+\includecodesnip{listati/select_race.c} qui però emerge una \textit{race
+ condition},\itindex{race~condition} perché se il segnale arriva prima della
+chiamata a \func{select}, questa non verrà interrotta, e la ricezione del
+segnale non sarà rilevata.
Per questo è stata introdotta \func{pselect} che attraverso l'argomento
\param{sigmask} permette di riabilitare la ricezione il segnale
contestualmente all'esecuzione della funzione,\footnote{in Linux però non è
presente la relativa system call, e la funzione è implementata nelle
\acr{glibc} attraverso \func{select} (vedi \texttt{man select\_tut}) per cui
- la possibilità di race condition permane; esiste però una soluzione,
- chiamata \index{\textit{self-pipe trick}}\textit{self-pipe trick}, che
- consiste nell'aprire una pipe (vedi sez.~\ref{sec:ipc_pipes}) ed usare
- \func{select} sul capo in lettura della stessa, e indicare l'arrivo di un
- segnale scrivendo sul capo in scrittura all'interno del manipolatore; in
- questo modo anche se il segnale va perso prima della chiamata di
- \func{select} questa lo riconoscerà comunque dalla presenza di dati sulla
- pipe.} ribloccandolo non appena essa ritorna, così che il precedente codice
-potrebbe essere riscritto nel seguente modo:
-\includecodesnip{listati/pselect_norace.c} in questo caso utilizzando
-\var{oldmask} durante l'esecuzione di \func{pselect} la ricezione del segnale
-sarà abilitata, ed in caso di interruzione si potranno eseguire le relative
-operazioni.
+ la possibilità di \itindex{race~condition}\textit{race condition} permane;
+ esiste però una soluzione, chiamata \itindex{self-pipe trick}
+ \textit{self-pipe trick}, che consiste nell'aprire una pipe (vedi
+ sez.~\ref{sec:ipc_pipes}) ed usare \func{select} sul capo in lettura della
+ stessa, e indicare l'arrivo di un segnale scrivendo sul capo in scrittura
+ all'interno del manipolatore; in questo modo anche se il segnale va perso
+ prima della chiamata di \func{select} questa lo riconoscerà comunque dalla
+ presenza di dati sulla pipe.} ribloccandolo non appena essa ritorna, così
+che il precedente codice potrebbe essere riscritto nel seguente modo:
+\includecodesnip{listati/pselect_norace.c}
+in questo caso utilizzando \var{oldmask} durante l'esecuzione di
+\func{pselect} la ricezione del segnale sarà abilitata, ed in caso di
+interruzione si potranno eseguire le relative operazioni.
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 (come \macro{POLLRDNORM} e
-\macro{POLLRDBAND}) sono utilizzabili soltanto qualora si sia definita la
+costanti relative ai diversi tipi di dati (come \const{POLLRDNORM} e
+\const{POLLRDBAND}) 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 \file{sys/poll.h} non è
sufficiente.}
Tuttavia con l'implementazione classica dei segnali questa modalità di I/O
presenta notevoli problemi, dato che non è possibile determinare, quando i
-file descriptor sono più di uno, qual'è quello responsabile dell'emissione del
+file descriptor sono più di uno, qual è quello responsabile dell'emissione del
segnale. Inoltre dato che i segnali normali non si accodano (si ricordi quanto
illustrato in sez.~\ref{sec:sig_notification}), in presenza di più file
descriptor attivi contemporaneamente, più segnali emessi nello stesso momento
Oltre alle operazioni di lettura e scrittura l'interfaccia POSIX.1b mette a
disposizione un'altra operazione, quella di sincronizzazione dell'I/O,
-compiuta dalla funzione \func{aio\_fsync}, che ha lo stesso effetto della
+compiuta dalla funzione \funcd{aio\_fsync}, che ha lo stesso effetto della
analoga \func{fsync}, ma viene eseguita in maniera asincrona; il suo prototipo
è:
\begin{prototype}{aio.h}
\end{errlist}
ed inoltre \errval{EISDIR}, \errval{ENOMEM}, \errval{EFAULT} (se non sono
stato allocati correttamente i buffer specificati nei campi
- \func{iov\_base}), più tutti gli ulteriori errori che potrebbero avere le
+ \var{iov\_base}), più tutti gli ulteriori errori che potrebbero avere le
usuali funzioni di lettura e scrittura eseguite su \param{fd}.}
\end{functions}
\subsection{File mappati in memoria}
\label{sec:file_memory_map}
+\itindbeg{memory~mapping}
Una modalità alternativa di I/O, che usa una interfaccia completamente diversa
rispetto a quella classica vista in cap.~\ref{cha:file_unix_interface}, è il
cosiddetto \textit{memory-mapped I/O}, che, attraverso il meccanismo della
\label{fig:file_mmap_layout}
\end{figure}
-L'uso del \textit{memory-mappung} comporta una notevole semplificazione delle
+L'uso del \textit{memory-mapping} comporta una notevole semplificazione delle
operazioni di I/O, in quanto non sarà più necessario utilizzare dei buffer
intermedi su cui appoggiare i dati da traferire, poiché questi potranno essere
acceduti direttamente nella sezione di memoria mappata; inoltre questa
riportati in tab.~\ref{tab:file_mmap_flag}; il valore specificato deve essere
compatibile con la modalità di accesso con cui si è aperto il file.
-L'argomento \param{flags} specifica infine qual'è il tipo di oggetto mappato,
+L'argomento \param{flags} specifica infine qual è il tipo di oggetto mappato,
le opzioni relative alle modalità con cui è effettuata la mappatura e alle
modalità con cui le modifiche alla memoria mappata vengono condivise o
mantenute private al processo che le ha effettuate. Deve essere specificato
visibili agli altri processi che mappano lo stesso
file.\footnotemark Il file su disco però non sarà
aggiornato fino alla chiamata di \func{msync} o
- \func{unmap}), e solo allora le modifiche saranno
+ \func{munmap}), e solo allora le modifiche saranno
visibili per l'I/O convenzionale. Incompatibile
con \const{MAP\_PRIVATE}. \\
\const{MAP\_PRIVATE} & I cambiamenti sulla memoria mappata non vengono
privata cui solo il processo chiamante ha
accesso. Le modifiche sono mantenute attraverso
il meccanismo del \textit{copy on
- write}\index{\textit{copy~on~write}} e
+ write}\itindex{copy~on~write} e
salvate su swap in caso di necessità. Non è
specificato se i cambiamenti sul file originale
vengano riportati sulla regione
\const{MAP\_EXECUTABLE}& Ignorato. \\
\const{MAP\_NORESERVE} & Si usa con \const{MAP\_PRIVATE}. Non riserva
delle pagine di swap ad uso del meccanismo del
- \textit{copy on
- write}\index{\textit{copy~on~write}}
+ \textit{copy on write}\itindex{copy~on~write}
per mantenere le
modifiche fatte alla regione mappata, in
questo caso dopo una scrittura, se non c'è più
Alla conclusione del processo, ogni pagina mappata verrà automaticamente
rilasciata, mentre la chiusura del file descriptor usato per effettuare la
mappatura in memoria non ha alcun effetto sulla stessa.
+\itindend{memory~mapping}
\section{Il file locking}
in \textit{append mode}, quando più processi scrivono contemporaneamente sullo
stesso file non è possibile determinare la sequenza in cui essi opereranno.
-Questo causa la possibilità di race condition\index{\textit{race~condition}};
-in generale le situazioni più comuni sono due: l'interazione fra un processo
-che scrive e altri che leggono, in cui questi ultimi possono leggere
-informazioni scritte solo in maniera parziale o incompleta; o quella in cui
-diversi processi scrivono, mescolando in maniera imprevedibile il loro output
-sul file.
+Questo causa la possibilità di una \textit{race condition}
+\itindex{race~condition}; in generale le situazioni più comuni sono due:
+l'interazione fra un processo che scrive e altri che leggono, in cui questi
+ultimi possono leggere informazioni scritte solo in maniera parziale o
+incompleta; o quella in cui diversi processi scrivono, mescolando in maniera
+imprevedibile il loro output sul file.
In tutti questi casi il \textit{file locking} è la tecnica che permette di
-evitare le race condition\index{\textit{race~condition}}, attraverso una serie
-di funzioni che permettono di bloccare l'accesso al file da parte di altri
-processi, così da evitare le sovrapposizioni, e garantire la atomicità delle
-operazioni di scrittura.
+evitare le \textit{race condition}\itindex{race~condition}, attraverso una
+serie di funzioni che permettono di bloccare l'accesso al file da parte di
+altri processi, così da evitare le sovrapposizioni, e garantire la atomicità
+delle operazioni di scrittura.
perciò le informazioni relative agli eventuali \textit{file lock} sono
mantenute a livello di inode\index{inode},\footnote{in particolare, come
accennato in fig.~\ref{fig:file_flock_struct}, i \textit{file lock} sono
- mantenuti un una \textit{linked list}\index{linked list} di strutture
- \struct{file\_lock}. La lista è referenziata dall'indirizzo di partenza
- mantenuto dal campo \var{i\_flock} della struttura \struct{inode} (per le
- definizioni esatte si faccia riferimento al file \file{fs.h} nei sorgenti
- del kernel). Un bit del campo \var{fl\_flags} di specifica se si tratta di
- un lock in semantica BSD (\const{FL\_FLOCK}) o POSIX (\const{FL\_POSIX}).}
-dato che questo è l'unico riferimento in comune che possono avere due processi
+ mantenuti un una \textit{linked~list} di strutture \struct{file\_lock}. La
+ lista è referenziata dall'indirizzo di partenza mantenuto dal campo
+ \var{i\_flock} della struttura \struct{inode} (per le definizioni esatte si
+ faccia riferimento al file \file{fs.h} nei sorgenti del kernel). Un bit del
+ campo \var{fl\_flags} di specifica se si tratta di un lock in semantica BSD
+ (\const{FL\_FLOCK}) o POSIX (\const{FL\_POSIX}).} \itindex{linked~list} dato
+che questo è l'unico riferimento in comune che possono avere due processi
diversi che aprono lo stesso file.
\begin{figure}[htb]
\item[\errcode{EDEADLK}] Si è richiesto un lock su una regione bloccata da
un altro processo che è a sua volta in attesa dello sblocco di un lock
mantenuto dal processo corrente; si avrebbe pertanto un
- \textit{deadlock}\index{\textit{deadlock}}. Non è garantito che il
- sistema riconosca sempre questa situazione.
+ \textit{deadlock}\itindex{deadlock}. Non è garantito che il sistema
+ riconosca sempre questa situazione.
\item[\errcode{EINTR}] La funzione è stata interrotta da un segnale prima
di poter acquisire un lock.
\end{errlist}
\begin{figure}[htb]
\centering \includegraphics[width=9cm]{img/file_lock_dead}
- \caption{Schema di una situazione di
- \textit{deadlock}\index{\textit{deadlock}}.}
+ \caption{Schema di una situazione di \textit{deadlock}\itindex{deadlock}.}
\label{fig:file_flock_dead}
\end{figure}
del processo 2; il processo 1 si bloccherà fintanto che il processo 2 non
rilasci il blocco. Ma cosa accade se il processo 2 nel frattempo tenta a sua
volta di ottenere un lock sulla regione A? Questa è una tipica situazione che
-porta ad un \textit{deadlock}\index{\textit{deadlock}}, dato che a quel punto
-anche il processo 2 si bloccherebbe, e niente potrebbe sbloccare l'altro
-processo. Per questo motivo il kernel si incarica di rilevare situazioni di
-questo tipo, ed impedirle restituendo un errore di \errcode{EDEADLK} alla
-funzione che cerca di acquisire un lock che porterebbe ad un
-\textit{deadlock}.
+porta ad un \textit{deadlock}\itindex{deadlock}, dato che a quel punto anche
+il processo 2 si bloccherebbe, e niente potrebbe sbloccare l'altro processo.
+Per questo motivo il kernel si incarica di rilevare situazioni di questo tipo,
+ed impedirle restituendo un errore di \errcode{EDEADLK} alla funzione che
+cerca di acquisire un lock che porterebbe ad un \textit{deadlock}.
\begin{figure}[!bht]
\centering \includegraphics[width=13cm]{img/file_posix_lock}
voce nella file table, ma con il valore del \acr{pid} del processo.
Quando si richiede un lock il kernel effettua una scansione di tutti i lock
-presenti sul file\footnote{scandisce cioè la linked list delle strutture
+presenti sul file\footnote{scandisce cioè la
+ \itindex{linked~list}\textit{linked list} delle strutture
\struct{file\_lock}, scartando automaticamente quelle per cui
\var{fl\_flags} non è \const{FL\_POSIX}, così che le due interfacce restano
ben separate.} per verificare se la regione richiesta non si sovrappone ad
lock (bloccante o meno), a seconda dell'opzione \cmd{-b}.
Il programma inizia col controllare (\texttt{\small 11--14}) che venga passato
-un parametro (il file da bloccare), che sia stato scelto (\texttt{\small
+un argomento (il file da bloccare), che sia stato scelto (\texttt{\small
15--18}) il tipo di lock, 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