Al momento dell'apertura il nuovo file descriptor non è condiviso con nessun
altro processo (torneremo sul significato della condivisione dei file
descriptor, che in genere si ottiene dopo una \func{fork}, in
-sez.~\ref{sec:file_sharing}) ed è impostato, come accennato in
+sez.~\ref{sec:file_shared_access}) ed è impostato, come accennato in
sez.~\ref{sec:proc_exec}, per restare aperto attraverso una
\func{exec}. Inoltre la posizione sul file, il cosiddetto \textit{offset}, è
impostata all'inizio del file. Una volta aperto un file si potrà operare su di
eventuale blocco (il \textit{file locking} \itindex{file~locking} è trattato
in sez.~\ref{sec:file_locking}) che il processo poteva avere acquisito su di
esso. Se \param{fd} è l'ultimo riferimento (di eventuali copie, vedi
-sez.~\ref{sec:file_sharing} e \ref{sec:file_dup}) ad un file aperto, tutte le
-risorse nella \itindex{file~table} \textit{file table} vengono
+sez.~\ref{sec:file_shared_access} e \ref{sec:file_dup}) ad un file aperto,
+tutte le risorse nella \itindex{file~table} \textit{file table} vengono
rilasciate. Infine se il file descriptor era l'ultimo riferimento ad un file
su disco quest'ultimo viene cancellato.
aperto anche da un altro processo che vi ha scritto, la fine del file può
essersi spostata, ma noi scriveremo alla posizione impostata in precedenza
(questa è una potenziale sorgente di \itindex{race~condition} \textit{race
- condition}, vedi sez.~\ref{sec:file_atomic}).
+ condition}, vedi sez.~\ref{sec:file_shared_access}).
Non tutti i file supportano la capacità di eseguire una \func{lseek}, in
questo caso la funzione ritorna l'errore \errcode{ESPIPE}. Questo, oltre che
da una \func{lseek} che riporti al valore precedente la posizione corrente sul
file, ma permette di eseguire l'operazione atomicamente. Questo può essere
importante quando la posizione sul file viene condivisa da processi diversi
-(vedi sez.~\ref{sec:file_sharing}). Il valore di
+(vedi sez.~\ref{sec:file_shared_access}). Il valore di
\param{offset} fa sempre riferimento all'inizio del file.
La funzione \func{pread} è disponibile anche in Linux, però diventa
dell'argomento sarà comunque affrontato in cap.~\ref{cha:file_advanced}).
-\subsection{La condivisione dei files}
-\label{sec:file_sharing}
+\subsection{La gestione dell'accesso concorrente ai files}
+\label{sec:file_shared_access}
In sez.~\ref{sec:file_fd} abbiamo descritto brevemente l'architettura
dell'interfaccia con i file da parte di un processo, mostrando in
fig.~\ref{fig:file_proc_file} le principali strutture usate dal kernel;
esamineremo ora in dettaglio le conseguenze che questa architettura ha nei
-confronti dell'accesso allo stesso file da parte di processi diversi.
+confronti dell'accesso concorrente allo stesso file da parte di processi
+diversi.
\begin{figure}[!htb]
\centering
azione simultanea sullo stesso file, in particolare occorre tenere presente
che:
\begin{itemize}
-\item ciascun processo può scrivere indipendentemente; dopo ciascuna
- \func{write} la posizione corrente sarà cambiata solo nel processo. Se la
- scrittura eccede la dimensione corrente del file questo verrà esteso
- automaticamente con l'aggiornamento del campo \var{i\_size} \itindex{inode}
- nell'\textit{inode}.
+\item ciascun processo può scrivere indipendentemente, dopo ciascuna
+ \func{write} la posizione corrente sarà cambiata solo nel processo
+ scrivente. Se la scrittura eccede la dimensione corrente del file questo
+ verrà esteso automaticamente con l'aggiornamento del campo \var{i\_size}
+ della struttura \kstruct{inode}.
\item se un file è in modalità \itindex{append~mode} \const{O\_APPEND} tutte
le volte che viene effettuata una scrittura la posizione corrente viene
- prima impostata alla dimensione corrente del file letta \itindex{inode}
- dall'\textit{inode}. Dopo la scrittura il file viene automaticamente esteso.
+ prima impostata alla dimensione corrente del file letta dalla struttura
+ \kstruct{inode}. Dopo la scrittura il file viene automaticamente esteso.
\item l'effetto di \func{lseek} è solo quello di cambiare il campo
\var{f\_pos} nella struttura \kstruct{file} della \itindex{file~table}
\textit{file table}, non c'è nessuna operazione sul file su disco. Quando la
si usa per porsi alla fine del file la posizione viene impostata leggendo la
- dimensione corrente \itindex{inode} dall'\textit{inode}.
+ dimensione corrente dalla struttura \kstruct{inode}.
\end{itemize}
\begin{figure}[!htb]
\end{figure}
Il secondo caso è quello in cui due file descriptor di due processi diversi
-puntino alla stessa voce nella \itindex{file~table} \textit{file table};
-questo è ad esempio il caso dei file aperti che vengono ereditati dal processo
+puntino alla stessa voce nella \itindex{file~table} \textit{file table}.
+Questo è ad esempio il caso dei file aperti che vengono ereditati dal processo
figlio all'esecuzione di una \func{fork} (si ricordi quanto detto in
sez.~\ref{sec:proc_fork}). La situazione è illustrata in
fig.~\ref{fig:file_acc_child}; dato che il processo figlio riceve una copia
dello spazio di indirizzi del padre, riceverà anche una copia di
-\kstruct{file\_struct} e relativa tabella dei file aperti.
-
-In questo modo padre e figlio avranno gli stessi file descriptor che faranno
-riferimento alla stessa voce nella \textit{file table}, condividendo così la
-posizione corrente sul file. Questo ha le conseguenze descritte a suo tempo in
-sez.~\ref{sec:proc_fork}: in caso di scrittura contemporanea la posizione
-corrente nel file varierà per entrambi i processi (in quanto verrà modificato
-\var{f\_pos} che è lo stesso per entrambi).
-
-Si noti inoltre che anche i \itindex{file~status~flag} flag di stato del file
-(quelli impostati dall'argomento \param{flag} di \func{open}) essendo tenuti
-nella voce della \textit{file table}, vengono in questo caso condivisi. Ai
-file però sono associati anche altri flag, dei quali l'unico usato al momento
-è \const{FD\_CLOEXEC}, detti \itindex{file~descriptor~flags} \textit{file
- descriptor flags}. Questi ultimi sono tenuti invece in
-\kstruct{file\_struct}, e perciò sono specifici di ciascun processo e non
+\kstruct{file\_struct} e della relativa tabella dei file aperti.
+
+Questo significa che il figlio avrà gli stessi file aperti del padre, in
+quanto la sua \kstruct{file\_struct}, pur essendo allocata in maniera
+indipendente, contiene gli stessi valori di quella del padre e quindi i suoi
+file descriptor faranno riferimento alla stessa voce nella
+\itindex{file~table} \textit{file table}, condividendo così la posizione
+corrente sul file. Questo ha le conseguenze descritte a suo tempo in
+sez.~\ref{sec:proc_fork}: in caso di scrittura o lettura da parte di uno dei
+due processi, la posizione corrente nel file varierà per entrambi, in quanto
+verrà modificato il campo \var{f\_pos} della struttura \kstruct{file}, che è
+la stessa per entrambi. Questo consente una sorta di
+``\textsl{sincronizzazione}'' automatica della posizione sul file fra padre e
+figlio che occorre tenere presente.
+
+Si noti inoltre che in questo caso anche i \itindex{file~status~flag} flag di
+stato del file, essendo mantenuti nella struttura \kstruct{file} della
+\textit{file table}, vengono condivisi, per cui una modifica degli stessi con
+\func{fcntl} (vedi sez.~\ref{sec:file_fcntl}) si applicherebbe a tutti
+processi che condividono la voce nella \itindex{file~table} \textit{file
+ table}. Ai file però sono associati anche altri flag, dei quali l'unico
+usato al momento è \const{FD\_CLOEXEC}, detti \itindex{file~descriptor~flags}
+\textit{file descriptor flags}; questi invece sono mantenuti in
+\kstruct{file\_struct}, e perciò sono locali per ciascun processo e non
vengono modificati dalle azioni degli altri anche in caso di condivisione
-della stessa voce della \textit{file table}.
-
-
-
-\subsection{Operazioni atomiche con i file}
-\label{sec:file_atomic}
-
-Come si è visto in un sistema unix-like è sempre possibile per più processi
-accedere in contemporanea allo stesso file, e che le operazioni di lettura e
-scrittura possono essere fatte da ogni processo in maniera autonoma in base
-ad una posizione corrente nel file che è locale a ciascuno di essi.
-
-Se dal punto di vista della lettura dei dati questo non comporta nessun
-problema, quando si andrà a scrivere le operazioni potranno mescolarsi in
-maniera imprevedibile. Il sistema però fornisce in alcuni casi la possibilità
-di eseguire alcune operazioni di scrittura in maniera coordinata anche senza
-utilizzare meccanismi di sincronizzazione più complessi (come il
-\itindex{file~locking} \textit{file locking}, che esamineremo in
-sez.~\ref{sec:file_locking}).
+della stessa voce della \itindex{file~table} \textit{file table}.
+
+Si tenga presente dunque che in un sistema unix-like è sempre possibile per
+più processi accedere in contemporanea allo stesso file e che non esistono, a
+differenza di altri sistemi operativi, dei meccanismi di blocco o di
+restrizione dell'accesso impliciti se più processi vogliono accedere allo
+stesso file. Questo significa che le operazioni di lettura e scrittura vengono
+sempre fatte da ogni processo in maniera autonoma, utilizzando una posizione
+corrente nel file che normalmente (a meno di non trovarsi nella situazione di
+fig.~\ref{fig:file_acc_child}) è locale a ciascuno di essi.
+
+Dal punto di vista della lettura dei dati questo comporta la possibilità di
+poter leggere dati non coerenti in caso di scrittura contemporanea da parte di
+un altro processo. Dal punto di vista della scrittura invece si potranno avere
+sovrapposizioni imprevedibili quando due processi scrivono nella stessa
+sezione di file, dato che ciascuno lo farà in maniera indipendente. Il
+sistema però fornisce in alcuni casi la possibilità di eseguire alcune
+operazioni di scrittura in maniera coordinata anche senza utilizzare dei
+meccanismi di sincronizzazione espliciti come il \itindex{file~locking}
+\textit{file locking}, che esamineremo in sez.~\ref{sec:file_locking}.
Un caso tipico di necessità di accesso condiviso in scrittura è quello in cui
vari processi devono scrivere alla fine di un file (ad esempio un file di
log). Come accennato in sez.~\ref{sec:file_lseek} impostare la posizione alla
fine del file e poi scrivere può condurre ad una \itindex{race~condition}
-\textit{race condition}: infatti può succedere che un secondo processo scriva
-alla fine del file fra la \func{lseek} e la \func{write}; in questo caso, come
-abbiamo appena visto, il file sarà esteso, ma il nostro primo processo avrà
-ancora la posizione corrente impostata con la \func{lseek} che non corrisponde
-più alla fine del file, e la successiva \func{write} sovrascriverà i dati del
-secondo processo.
-
-Il problema è che usare due \textit{system call} in successione non è
-un'operazione atomica; il problema è stato risolto introducendo la modalità
-\itindex{append~mode} \const{O\_APPEND}. In questo caso infatti, come abbiamo
-descritto in precedenza, è il kernel che aggiorna automaticamente la posizione
-alla fine del file prima di effettuare la scrittura, e poi estende il file.
-Tutto questo avviene all'interno di una singola \textit{system call} (la
-\func{write}) che non essendo interrompibile da un altro processo costituisce
-un'operazione atomica.
-
-Un altro caso tipico in cui è necessaria l'atomicità è quello in cui si vuole
-creare un \textsl{file di lock} \index{file!di lock}, bloccandosi se il file
-esiste. In questo caso la sequenza logica porterebbe a verificare prima
-l'esistenza del file con una \func{stat} per poi crearlo con una \func{creat};
-di nuovo avremmo la possibilità di una \itindex{race~condition} \textit{race
- condition} da parte di un altro processo che crea lo stesso file fra il
-controllo e la creazione.
-
-Per questo motivo sono stati introdotti per \func{open} i due flag
-\const{O\_CREAT} e \const{O\_EXCL}. In questo modo l'operazione di controllo
-dell'esistenza del file (con relativa uscita dalla funzione con un errore) e
-creazione in caso di assenza, diventa atomica essendo svolta tutta all'interno
-di una singola \textit{system call} (per i dettagli sull'uso di questa
-caratteristica si veda sez.~\ref{sec:ipc_file_lock}).
-
-
-\subsection{Le funzioni \func{sync} e \func{fsync}}
-\label{sec:file_sync}
-
-% TODO, aggiungere syncfs, introdotta con il 2.6.39
-
-Come accennato in sez.~\ref{sec:file_open_close} tutte le operazioni di
-scrittura sono in genere bufferizzate dal kernel, che provvede ad effettuarle
-in maniera asincrona (ad esempio accorpando gli accessi alla stessa zona del
-disco) in un secondo tempo rispetto al momento della esecuzione della
-\func{write}.
-
-Per questo motivo, quando è necessaria una sincronizzazione dei dati, il
-sistema mette a disposizione delle funzioni che provvedono a forzare lo
-scarico dei dati dai buffer del kernel.\footnote{come già accennato neanche
- questo dà la garanzia assoluta che i dati siano integri dopo la chiamata,
- l'hardware dei dischi è in genere dotato di un suo meccanismo interno di
- ottimizzazione per l'accesso al disco che può ritardare ulteriormente la
- scrittura effettiva.} La prima di queste funzioni è \funcd{sync} il cui
-prototipo è:
-
-\begin{funcproto}{
-\fhead{unistd.h}
-\fdecl{int sync(void)}
-\fdesc{Sincronizza il buffer della cache dei file col disco.}
-}
-
-{La funzione ritorna sempre $0$ ed ha sempre successo.}
-\end{funcproto}
-
-\noindent i vari standard prevedono che la funzione si limiti a far partire
-le operazioni, ritornando immediatamente; in Linux (dal kernel 1.3.20) invece
-la funzione aspetta la conclusione delle operazioni di sincronizzazione del
-kernel.
-
-La funzione viene usata dal comando \cmd{sync} quando si vuole forzare
-esplicitamente lo scarico dei dati su disco, o dal demone di sistema
-\cmd{update} che esegue lo scarico dei dati ad intervalli di tempo fissi: il
-valore tradizionale, usato da BSD, per l'update dei dati è ogni 30 secondi, ma
-in Linux il valore utilizzato è di 5 secondi; con le nuove versioni\footnote{a
- partire dal kernel 2.2.8} poi, è il kernel che si occupa direttamente di
-tutto quanto attraverso il demone interno \cmd{bdflush}, il cui comportamento
-può essere controllato attraverso il file \sysctlfile{vm/bdflush} (per
-il significato dei valori si può leggere la documentazione allegata al kernel
-in \file{Documentation/sysctl/vm.txt}).
-
-Quando si vogliono scaricare soltanto i dati di un file (ad esempio essere
-sicuri che i dati di un database sono stati registrati su disco) si possono
-usare le due funzioni \funcd{fsync} e \funcd{fdatasync}, i cui prototipi sono:
-
-\begin{funcproto}{
-\fhead{unistd.h}
-\fdecl{int fsync(int fd)}
-\fdesc{Sincronizza dati e metadati di un file.}
-\fdecl{int fdatasync(int fd)}
-\fdesc{Sincronizza i dati di un file.}
-}
-
-{Le funzioni ritornano $0$ in caso di successo e $-1$ per un errore, nel qual
- caso \var{errno} assumerà uno dei valori:
- \begin{errlist}
- \item[\errcode{EINVAL}] \param{fd} è un \index{file!speciali} file speciale
- che non supporta la sincronizzazione.
- \end{errlist}
- ed inoltre \errval{EBADF}, \errval{EROFS} e \errval{EIO} nel loro
- significato generico.}
-\end{funcproto}
-
-Entrambe le funzioni forzano la sincronizzazione col disco di tutti i dati del
-file specificato, ed attendono fino alla conclusione delle operazioni;
-\func{fsync} forza anche la sincronizzazione dei meta-dati del file (che
-riguardano sia le modifiche alle tabelle di allocazione dei settori, che gli
-altri dati contenuti \itindex{inode} nell'\textit{inode} che si leggono con
-\func{fstat}, come i tempi del file).
-
-Si tenga presente che questo non comporta la sincronizzazione della
-directory che contiene il file (e scrittura della relativa voce su
-disco) che deve essere effettuata esplicitamente.\footnote{in realtà per
- il filesystem \acr{ext2}, quando lo si monta con l'opzione \cmd{sync},
- il kernel provvede anche alla sincronizzazione automatica delle voci
- delle directory.}
-
-
-\subsection{Le funzioni \func{dup} e \func{dup2}}
+\textit{race condition}l infatti può succedere che un secondo processo scriva
+alla fine del file fra la \func{lseek} e la \func{write}. In questo caso, come
+abbiamo appena visto, il file sarà esteso, ma il primo processo, che avrà la
+posizione corrente che aveva impostato con la \func{lseek} che non corrisponde
+più alla fine del file, e la sua successiva \func{write} sovrascriverà i dati
+del secondo processo.
+
+Il problema deriva dal fatto che usare due \textit{system call} in successione
+non è mai un'operazione atomica dato che il kernel può interrompere
+l'esecuzione del processo fra le due. Nel caso specifico il problema è stato
+risolto introducendo la modalità di scrittura \itindex{append~mode} in
+\textit{append}, attivabile con il flag \const{O\_APPEND}. In questo caso
+infatti, come abbiamo illustrato in sez.~\ref{sec:file_open_close}, è il
+kernel che aggiorna automaticamente la posizione alla fine del file prima di
+effettuare la scrittura, e poi estende il file. Tutto questo avviene
+all'interno di una singola \textit{system call}, la \func{write}, che non
+essendo interrompibile da un altro processo realizza un'operazione atomica.
+
+
+\subsection{La duplicazione dei file descriptor}
\label{sec:file_dup}
-Abbiamo già visto in sez.~\ref{sec:file_sharing} come un processo figlio
+Abbiamo già visto in sez.~\ref{sec:file_shared_access} come un processo figlio
condivida gli stessi file descriptor del padre; è possibile però ottenere un
comportamento analogo all'interno di uno stesso processo \textit{duplicando}
-un file descriptor. Per far questo si usa la funzione \funcd{dup} il cui
-prototipo è:
+un file descriptor. Per far questo si usa la funzione di sistema \funcd{dup},
+il cui prototipo è:
\begin{funcproto}{
\fhead{unistd.h}
La funzione ritorna, come \func{open}, il primo file descriptor libero. Il
file descriptor è una copia esatta del precedente ed entrambi possono essere
interscambiati nell'uso. Per capire meglio il funzionamento della funzione si
-può fare riferimento a fig.~\ref{fig:file_dup}: l'effetto della funzione è
-semplicemente quello di copiare il valore nella struttura
-\kstruct{file\_struct}, cosicché anche il nuovo file descriptor fa riferimento
-alla stessa voce nella \textit{file table}; per questo si dice che il nuovo
-file descriptor è \textsl{duplicato}, da cui il nome della funzione.
+può fare riferimento a fig.~\ref{fig:file_dup}. L'effetto della funzione è
+semplicemente quello di copiare il valore di un certo file descriptor in
+un altro all'interno della struttura \kstruct{file\_struct}, cosicché anche
+questo faccia riferimento alla stessa voce nella \textit{file table}. Per
+questo motivo si dice che il nuovo file descriptor è ``\textsl{duplicato}'',
+da cui il nome della funzione.
\begin{figure}[!htb]
\centering \includegraphics[width=12cm]{img/filedup}
\end{figure}
Si noti che per quanto illustrato in fig.~\ref{fig:file_dup} i file descriptor
-duplicati condivideranno eventuali lock, \textit{file status flag}, e
-posizione corrente. Se ad esempio si esegue una \func{lseek} per modificare la
-posizione su uno dei due file descriptor, essa risulterà modificata anche
-sull'altro (dato che quello che viene modificato è lo stesso campo nella voce
-della \textit{file table} a cui entrambi fanno riferimento). L'unica
-differenza fra due file descriptor duplicati è che ciascuno avrà il suo
-\textit{file descriptor flag}; a questo proposito va specificato che nel caso
-di \func{dup} il flag di \textit{close-on-exec} \itindex{close-on-exec} (vedi
-sez.~\ref{sec:proc_exec} e sez.~\ref{sec:file_fcntl}) viene sempre cancellato
-nella copia.
-
-L'uso principale di questa funzione è per la redirezione dell'input e
-dell'output fra l'esecuzione di una \func{fork} e la successiva \func{exec};
-diventa così possibile associare un file (o una pipe) allo standard input o
-allo standard output (torneremo sull'argomento in sez.~\ref{sec:ipc_pipe_use},
-quando tratteremo le pipe). Per fare questo in genere occorre prima chiudere
-il file che si vuole sostituire, cosicché il suo file descriptor possa esser
-restituito alla chiamata di \func{dup}, come primo file descriptor
-disponibile.
-
-Dato che questa è l'operazione più comune, è prevista una diversa versione
-della funzione, \funcd{dup2}, che permette di specificare esplicitamente
-qual è il valore di file descriptor che si vuole avere come duplicato; il suo
-prototipo è:
+duplicati condivideranno eventuali lock (vedi sez.~\ref{sec:file_locking}),
+\itindex{file~status~flag} i flag di stato, e la posizione corrente sul
+file. Se ad esempio si esegue una \func{lseek} per modificare la posizione su
+uno dei due file descriptor, essa risulterà modificata anche sull'altro, dato
+che quello che viene modificato è lo stesso campo nella voce della
+\textit{file table} a cui entrambi fanno riferimento.
+
+L'unica differenza fra due file descriptor duplicati è che ciascuno avrà un
+suo \textit{file descriptor flag} indipendente. A questo proposito deve essere
+tenuto presente che nel caso in cui si usi \func{dup} per duplicare un file
+descriptor, se questo ha il flag di \textit{close-on-exec}
+\itindex{close-on-exec} attivo (vedi sez.~\ref{sec:proc_exec} e
+sez.~\ref{sec:file_fcntl}), questo verrà cancellato nel file descriptor
+restituito come copia.
+
+L'uso principale di questa funzione è nella shell per la redirezione dei file
+standard di tab.~\ref{tab:file_std_files} fra l'esecuzione di una \func{fork}
+e la successiva \func{exec}. Diventa così possibile associare un file (o una
+pipe) allo \itindex{standard~input} \textit{standard input} o allo
+\itindex{standard~output} \textit{standard output} (vedremo un esempio in
+sez.~\ref{sec:ipc_pipe_use}, quando tratteremo le pipe).
+
+Ci si può chiedere perché non sia in questo caso sufficiente chiudere il file
+standard che si vuole redirigere e poi aprire direttamente con \func{open} il
+file vi si vuole far corrispondere, invece di duplicare un file descriptor che
+si è già aperto. La risposta sta nel fatto che il file che si vuole redirigere
+non è detto sia un file regolare, ma potrebbe essere, come accennato, anche
+una fifo o un socket, oppure potrebbe essere un file associato ad un file
+descriptor che si è ereditato già aperto (ad esempio attraverso un'altra
+\func{exec}) da un processo antenato del padre, del quale non si conosce il
+nome. Operando direttamente con i file descriptor \func{dup} consente di
+ignorare le origini del file descriptor che si duplica e funziona in maniera
+generica indipendentemente dall'oggetto a cui questo fa riferimento.
+
+Per ottenere la redirezione occorre pertanto disporre del file descriptor
+associato al file che si vuole usare e chiudere il file descriptor che si
+vuole sostituire, cosicché esso possa esser restituito alla successiva
+chiamata di \func{dup} come primo file descriptor disponibile. Dato che
+questa è l'operazione più comune, è prevista un'altra funzione di sistema,
+\funcd{dup2}, che permette di specificare esplicitamente qual è il numero di
+file descriptor che si vuole ottenere come duplicato; il suo prototipo è:
\begin{funcproto}{
\fhead{unistd.h}
\begin{errlist}
\item[\errcode{EBADF}] \param{oldfd} non è un file aperto o \param{newfd} ha
un valore fuori dall'intervallo consentito per i file descriptor.
+ \item[\errcode{EBUSY}] si è rilevata la possibilità di una
+ \itindex{race~condition} \textit{race condition}.
+ \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale.
\item[\errcode{EMFILE}] si è raggiunto il numero massimo consentito di file
descriptor aperti.
\end{errlist}
}
\end{funcproto}
-
-\noindent e qualora il file descriptor \param{newfd} sia già aperto (come
-avviene ad esempio nel caso della duplicazione di uno dei file standard) esso
-sarà prima chiuso e poi duplicato (così che il file duplicato sarà connesso
-allo stesso valore per il file descriptor).
+La funzione duplica il file descriptor \param{oldfd} su un altro file
+descriptor di valore \param{newfd}. Qualora il file descriptor \param{newfd}
+sia già aperto, come avviene ad esempio nel caso della duplicazione di uno dei
+file standard di tab.~\ref{tab:file_std_files}, esso sarà prima chiuso e poi
+duplicato. Se \param{newfd} è uguale a \param{oldfd} la funzione non fa nulla
+e si limita a restituire \param{newfd}.
+
+L'uso di \func{dup2} ha vari vantaggi rispetto alla combinazione di
+\func{close} e \func{dup}; anzitutto se \param{oldfd} è uguale \param{newfd}
+questo verrebbe chiuso e \func{dup} fallirebbe, ma soprattutto l'operazione è
+atomica e consente di evitare una \itindex{race~condition} \textit{race
+ condition} in cui dopo la chiusura del file si potrebbe avere la ricezione
+di un segnale il cui gestore (vedi sez.~\ref{sec:sig_signal_handler}) potrebbe
+a sua volta aprire un file, per cui alla fine \func{dup} restituirebbe un file
+descriptor diverso da quello voluto.
+
+Con Linux inoltre la funzione prevede la possibilità di restituire l'errore
+\errcode{EBUSY}, che non è previsto dallo standard, quando viene rilevata la
+possibilità di una \itindex{race~condition} \textit{race condition} interna in
+cui si cerca di duplicare un file descriptor che è stato allocato ma per il
+quale non sono state completate le operazioni di apertura.\footnote{la
+ condizione è abbastanza peculiare e non attinente al tipo di utilizzo
+ indicato, quanto piuttosto ad un eventuale tentativo di duplicare file
+ descriptor non ancora aperti, la condizione di errore non è prevista dallo
+ standard, ma in condizioni simili FreeBSD risponde con un errore di
+ \errval{EBADF}, mentre OpenBSD elimina la possibilità di una \textit{race
+ condition} al costo di una perdita di prestazioni.} In tal caso occorre
+ritentare l'operazione.
La duplicazione dei file descriptor può essere effettuata anche usando la
funzione di controllo dei file \func{fcntl} (che esamineremo in
sez.~\ref{sec:file_fcntl}) con il parametro \const{F\_DUPFD}. L'operazione ha
la sintassi \code{fcntl(oldfd, F\_DUPFD, newfd)} e se si usa 0 come valore per
-\param{newfd} diventa equivalente a \func{dup}.
+\param{newfd} diventa equivalente a \func{dup}. La sola differenza fra le due
+funzioni (a parte la sintassi ed i diversi codici di errore) è che \func{dup2}
+chiude il file descriptor \param{newfd} se questo è già aperto, garantendo che
+la duplicazione sia effettuata esattamente su di esso, invece \func{fcntl}
+restituisce il primo file descriptor libero di valore uguale o maggiore
+di \param{newfd}, per cui se \param{newfd} è aperto la duplicazione avverrà su
+un altro file descriptor.
+
+Su Linux inoltre è presente una terza funzione di sistema non
+standard,\footnote{la funzione è stata introdotta con il kernel 2.6.27 e resa
+ disponibile con la \acr{glibc} 2.9.} \funcd{dup3}, che consente di duplicare
+un file descriptor reimpostandone i flag, per usarla occorre definire la macro
+\macro{\_GNU\_SOURCE} ed il suo prototipo è:
+
+\begin{funcproto}{
+\fhead{unistd.h}
+\fdecl{int dup3(int oldfd, int newfd, int flags)}
+\fdesc{Duplica un file descriptor su un altro.}
+}
+
+{La funzione ritorna il nuovo file descriptor in caso di successo e $-1$ per
+ un errore, nel qual caso \var{errno} assumerà gli stessi valori di
+ \func{dup2} più \errcode{EINVAL} qualora \param{flags} contenga un valore
+ non valido o \param{newfd} sia uguale a \param{oldfd}.
+}
+\end{funcproto}
+
+La funzione è identica a \func{dup2} ma prevede la possibilità di mantenere il
+flag di \textit{close-on-exec} \itindex{close-on-exec} sul nuovo
+file descriptor specificando \const{O\_CLOEXEC} in \param{flags} (che è l'unico
+flag usabile in questo caso). Inoltre rileva esplicitamente la possibile
+coincidenza fra \param{newfd} e \param{oldfd}, fallendo con un errore di
+\errval{EINVAL}.
+
+
+\subsection{Le funzioni di sincronizzazione dei dati}
+\label{sec:file_sync}
+
+Come accennato in sez.~\ref{sec:file_open_close} tutte le operazioni di
+scrittura sono in genere bufferizzate dal kernel, che provvede ad effettuarle
+in maniera asincrona, ad esempio accorpando gli accessi alla stessa zona del
+disco, in un secondo tempo rispetto al momento della esecuzione della
+\func{write}.
-La sola differenza fra le due funzioni\footnote{a parte la sintassi ed i
- diversi codici di errore.} è che \func{dup2} chiude il file descriptor
-\param{newfd} se questo è già aperto, garantendo che la duplicazione sia
-effettuata esattamente su di esso, invece \func{fcntl} restituisce il primo
-file descriptor libero di valore uguale o maggiore di \param{newfd} (e se
-\param{newfd} è aperto la duplicazione avverrà su un altro file descriptor).
+Per questo motivo quando è necessaria una sincronizzazione dei dati il sistema
+mette a disposizione delle funzioni che provvedono a forzare lo scarico dei
+dati dai buffer del kernel. La prima di queste funzioni di sistema è
+\funcd{sync}, il cui prototipo è:\footnote{questo è il prototipo usato a
+ partire dalla \acr{glibc} 2.2.2 seguendo gli standard, in precedenza la
+ funzione era definita come \code{int sync(void)} e ritornava sempre $0$.}
+\begin{funcproto}{
+\fhead{unistd.h}
+\fdecl{void sync(void)}
+\fdesc{Sincronizza il buffer della cache dei file col disco.}
+}
+
+{La funzione non ritorna nulla e non prevede condizioni di errore.}
+\end{funcproto}
+
+I vari standard prevedono che la funzione si limiti a far partire le
+operazioni, ritornando immediatamente; con Linux fin dal kernel 1.3.20 invece
+la funzione aspetta la conclusione delle operazioni di sincronizzazione. Si
+tenga presente comunque che questo non dà la garanzia assoluta che i dati
+siano integri dopo la chiamata, l'hardware dei dischi è in genere dotato di un
+suo meccanismo interno di bufferizzazione che può ritardare ulteriormente la
+scrittura effettiva.
+
+La funzione viene usata dal comando \cmd{sync} quando si vuole forzare
+esplicitamente lo scarico dei dati su disco, un tempo era invocata da un
+apposito demone di sistema (in genere chiamato \cmd{update}) che eseguiva lo
+scarico dei dati ad intervalli di tempo fissi, ad esempio il valore
+tradizionale usato da BSD era di 30 secondi, mentre su Linux il valore
+utilizzato è di 5 secondi.
+
+Con le nuove versioni del kernel le operazioni di scarico periodico dei dati
+su disco vengono gestite direttamente dal sistema della memoria virtuale,
+attraverso opportuni \textit{task} interni al kernel il cui comportamento può
+essere controllato attraverso il file \sysctlfile{vm/bdflush}. Per il
+significato dei valori si può leggere la documentazione allegata ai sorgenti
+del kernel nel file \file{Documentation/sysctl/vm.txt}. Si tenga presente che
+la funzione di sistema \funcm{bdflush} che un tempo veniva usata per queste
+impostazioni è deprecata e causa semplicemente la stampa di un messaggio nei
+log del kernel, pertanto non la prenderemo in esame.
+
+Quando si vogliono scaricare soltanto i dati di un singolo file (ad esempio
+essere sicuri che i dati di un database sono stati registrati su disco) si
+possono usare le due funzioni di sistema \funcd{fsync} e \funcd{fdatasync}, i
+cui prototipi sono:
+
+\begin{funcproto}{
+\fhead{unistd.h}
+\fdecl{int fsync(int fd)}
+\fdesc{Sincronizza dati e metadati di un file.}
+\fdecl{int fdatasync(int fd)}
+\fdesc{Sincronizza i dati di un file.}
+}
+
+{Le funzioni ritornano $0$ in caso di successo e $-1$ per un errore, nel qual
+ caso \var{errno} assumerà uno dei valori:
+ \begin{errlist}
+ \item[\errcode{EINVAL}] \param{fd} è un \index{file!speciali} file speciale
+ che non supporta la sincronizzazione.
+ \end{errlist}
+ ed inoltre \errval{EBADF}, \errval{EROFS} e \errval{EIO} nel loro
+ significato generico.}
+\end{funcproto}
+
+Entrambe le funzioni forzano la sincronizzazione col disco di tutti i dati del
+file specificato, ed attendono fino alla conclusione delle operazioni;
+\func{fsync} forza anche la sincronizzazione dei meta-dati del file (che
+riguardano sia le modifiche alle tabelle di allocazione dei settori, che gli
+altri dati contenuti \itindex{inode} nell'\textit{inode} che si leggono con
+\func{fstat}, come i tempi del file).
+
+Si tenga presente che questo non comporta la sincronizzazione della
+directory che contiene il file (e scrittura della relativa voce su
+disco) che deve essere effettuata esplicitamente.\footnote{in realtà per
+ il filesystem \acr{ext2}, quando lo si monta con l'opzione \cmd{sync},
+ il kernel provvede anche alla sincronizzazione automatica delle voci
+ delle directory.}
+
+
+
+% TODO, aggiungere syncfs, introdotta con il 2.6.39
\subsection{Le funzioni \func{openat}, \func{mkdirat} e affini}
(il comportamento predefinito) restano aperti.
\item[\const{F\_GETFL}] ritorna il valore del \textit{file status flag} in
caso di successo o $-1$ in caso di errore; permette cioè di rileggere quei
- bit impostati da \func{open} all'apertura del file che vengono memorizzati
- (quelli riportati nella prima e terza sezione di
- tab.~\ref{tab:file_open_flags}).
-% TODO toglire riferimeto a tabella flag e mettere altro
-
+ bit impostati da \func{open} all'apertura del file che vengono memorizzati,
+ quelli riportati in tab.~\ref{tab:open_access_mode_flag} e
+ tab.~\ref{tab:open_operation_flag}).
\item[\const{F\_SETFL}] imposta il \textit{file status flag} al valore
specificato da \param{arg}, ritorna un valore nullo in caso di successo o
- $-1$ in caso di errore. Possono essere impostati solo i bit riportati nella
- terza sezione di tab.~\ref{tab:file_open_flags}.\footnote{la pagina di
- manuale riporta come impostabili solo \const{O\_APPEND},
- \const{O\_NONBLOCK} e \const{O\_ASYNC}.}
-% TODO toglire riferimeto a tabella flag e mettere altro
-
+ $-1$ in caso di errore. Possono essere impostati solo i bit riportati in
+ tab.~\ref{tab:open_operation_flag}.\footnote{la pagina di manuale riporta
+ come impostabili solo \const{O\_APPEND}, \const{O\_NONBLOCK} e
+ \const{O\_ASYNC}.}
\item[\const{F\_GETLK}] richiede un controllo sul file lock specificato da
\param{lock}, sovrascrivendo la struttura da esso puntata con il risultato;
ritorna un valore nullo in caso di successo o $-1$ in caso di errore. Questa
scrittura (sia per quel che riguarda la bufferizzazione, che le
formattazioni), i \textit{file stream} restano del tutto equivalenti ai file
descriptor (sui quali sono basati), ed in particolare continua a valere quanto
-visto in sez.~\ref{sec:file_sharing} a proposito dell'accesso condiviso ed in
-sez.~\ref{sec:file_access_control} per il controllo di accesso.
+visto in sez.~\ref{sec:file_shared_access} a proposito dell'accesso
+concorrente ed in sez.~\ref{sec:file_access_control} per il controllo di
+accesso.
\itindend{file~stream}
% LocalWords: locking fsetlocking type Virtual operation dentry unistd sys AT
% LocalWords: modification hole functions FSETSIG pathname EEXIST CREAT EINTR
% LocalWords: EISDIR EFBIG EOVERFLOW ELOOP NOFOLLOW ENODEV ENOENT ENOTDIR fork
-
-%%% Local Variables:
-%%% mode: latex
-%%% TeX-master: "gapil"
-%%% End:
-% LocalWords: ENXIO NONBLOCK WRONLY EPERM NOATIME ETXTBSY EWOULDBLOCK EACCES% LocalWords: EFAULT
% LocalWords: EMFILE ENAMETOOLONG ENFILE ENOMEM ENOSPC EROFS exec access RDWR
% LocalWords: RDONLY ioctl AND ACCMODE creation Denial Service DoS opendir NFS
% LocalWords: SOURCE LARGEFILE BITS NOCTTY TRUNC SHLOCK shared EXLOCK race SGI
% LocalWords: sigaction SIGINFO siginfo SETLEASE lease GETLEASE NOTIFY request
% LocalWords: everything framebuffer ENOTTY argp CDROM lsattr chattr magic
% LocalWords: number FIOCLEX FIONCLEX FIOASYNC FIONBIO FIOSETOWN FIOGETOWN
-% LocalWords: FIONREAD epoll FIOQSIZE side effects SAFE BYCALLER QUERY
+% LocalWords: FIONREAD epoll FIOQSIZE side effects SAFE BYCALLER QUERY EACCES
+% LocalWords: EBUSY OpenBSD
+% LocalWords: ENXIO NONBLOCK WRONLY EPERM NOATIME ETXTBSY EWOULDBLOCK
+% LocalWords: EFAULT
+
+%%% Local Variables:
+%%% mode: latex
+%%% TeX-master: "gapil"
+%%% End: