Altro accorpamento di sezioni e risistemazione dei riferimenti,
authorSimone Piccardi <piccardi@gnulinux.it>
Sun, 5 Feb 2012 17:47:35 +0000 (17:47 +0000)
committerSimone Piccardi <piccardi@gnulinux.it>
Sun, 5 Feb 2012 17:47:35 +0000 (17:47 +0000)
Completato tutto fino a dupN.

fileadv.tex
fileio.tex
img/filedup.dia
img/filemultacc.dia
img/fileshar.dia
ipc.tex
prochand.tex
tcpsock.tex

index 1de6aeb86eac23b1e881d49ddeeabf3c5100a196..a4ca6e28ae80d4cd30656cb521cd36fa588be2c8 100644 (file)
@@ -26,12 +26,12 @@ controllo più dettagliato delle modalità di I/O.
 
 \itindbeg{file~locking}
 
-In sez.~\ref{sec:file_sharing} abbiamo preso in esame le modalità in cui un
-sistema unix-like gestisce la condivisione dei file da parte di processi
-diversi. In quell'occasione si è visto come, con l'eccezione dei file aperti
-in \itindex{append~mode} \textit{append mode}, quando più processi scrivono
-contemporaneamente sullo stesso file non è possibile determinare la sequenza
-in cui essi opereranno.
+In sez.~\ref{sec:file_shared_access} abbiamo preso in esame le modalità in cui
+un sistema unix-like gestisce l'accesso concorrente ai file da parte di
+processi diversi. In quell'occasione si è visto come, con l'eccezione dei file
+aperti in \itindex{append~mode} \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 una \itindex{race~condition} \textit{race
   condition}; in generale le situazioni più comuni sono due: l'interazione fra
@@ -260,8 +260,8 @@ Questa struttura prevede che, quando si richiede la rimozione di un
 file descriptor che fa riferimento ad una voce nella \itindex{file~table}
 \textit{file table} corrispondente a quella registrata nel blocco.  Allora se
 ricordiamo quanto visto in sez.~\ref{sec:file_dup} e
-sez.~\ref{sec:file_sharing}, e cioè che i file descriptor duplicati e quelli
-ereditati in un processo figlio puntano sempre alla stessa voce nella
+sez.~\ref{sec:file_shared_access}, e cioè che i file descriptor duplicati e
+quelli ereditati in un processo figlio puntano sempre alla stessa voce nella
 \itindex{file~table} \textit{file table}, si può capire immediatamente quali
 sono le conseguenze nei confronti delle funzioni \func{dup} e \func{fork}.
 
index 481d210f17324b3e8729761824a10a8262bf0c14..d631d4060f4b5613a5e58304b79084a6fbcbbcaa 100644 (file)
@@ -294,7 +294,7 @@ nuovo file questo diventerà il nuovo \itindex{standard~input} \textit{standard
 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
@@ -699,8 +699,8 @@ La funzione chiude il file descriptor \param{fd}. La chiusura rilascia ogni
 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.
 
@@ -818,7 +818,7 @@ la successiva scrittura avvenga alla fine del file, infatti se questo è stato
 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
@@ -1024,7 +1024,7 @@ L'uso di \func{pread} è equivalente all'esecuzione di una \func{read} seguita
 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
@@ -1115,14 +1115,15 @@ permettono di eseguire alcune operazioni avanzate con i file (il grosso
 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
@@ -1147,20 +1148,20 @@ vengono mantenute nella sua voce della \itindex{file~table} \textit{file
 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]
@@ -1171,175 +1172,89 @@ che:
 \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}
@@ -1360,11 +1275,12 @@ prototipo è:
 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}
@@ -1373,30 +1289,47 @@ file descriptor è \textsl{duplicato}, da cui il nome della funzione.
 \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}
@@ -1409,31 +1342,173 @@ prototipo è:
   \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}
@@ -1775,19 +1850,15 @@ per \var{cmd} è riportata di seguito:
   (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
@@ -2119,8 +2190,9 @@ A parte i dettagli legati alla gestione delle operazioni di lettura e
 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}
 
@@ -3867,12 +3939,6 @@ con uno dei valori \const{FSETLOCKING\_INTERNAL} o
 % 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
@@ -3890,4 +3956,12 @@ con uno dei valori \const{FSETLOCKING\_INTERNAL} o
 % 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: 
index 14771736bb47757c734a092e6a9c69b15f3ca072..7230f1a05040d61542534bde191e2cd7bd84beda 100644 (file)
Binary files a/img/filedup.dia and b/img/filedup.dia differ
index 5393ff1af27681f77df448c0038b3a613d777679..25911095b6ec3b8194bfa91f19a8b0499420c106 100644 (file)
Binary files a/img/filemultacc.dia and b/img/filemultacc.dia differ
index 077ceb01c4b0919a5a403b8ed2f364e1ed4704a9..01d2f4ad6ba661b0a4fe8ba4622e214448fbaf92 100644 (file)
Binary files a/img/fileshar.dia and b/img/fileshar.dia differ
diff --git a/ipc.tex b/ipc.tex
index 59c7f833eeb5fb0d0905ed7d249bca27bfddb326..1d085684f79bb80d538b6d9ea37875f3a46202ac 100644 (file)
--- a/ipc.tex
+++ b/ipc.tex
@@ -82,7 +82,7 @@ indicano la direzione del flusso dei dati.
 \end{figure}
 
 Chiaramente creare una pipe all'interno di un singolo processo non serve a
-niente; se però ricordiamo quanto esposto in sez.~\ref{sec:file_sharing}
+niente; se però ricordiamo quanto esposto in sez.~\ref{sec:file_shared_access}
 riguardo al comportamento dei file descriptor nei processi figli, è immediato
 capire come una pipe possa diventare un meccanismo di intercomunicazione. Un
 processo figlio infatti condivide gli stessi file descriptor del padre,
index 182ad5f29263bbc507b3a6c5aa88855cb8a22245..fa6e1d50969b63087422d86a5b12a7a3865e21e7 100644 (file)
@@ -549,10 +549,10 @@ ultimo) troveremo anche l'output completo del padre.
 L'esempio ci mostra un altro aspetto fondamentale dell'interazione con i file,
 valido anche per l'esempio precedente, ma meno evidente: il fatto cioè che non
 solo processi diversi possono scrivere in contemporanea sullo stesso file
-(l'argomento della condivisione dei file è trattato in dettaglio in
-sez.~\ref{sec:file_sharing}), ma anche che, a differenza di quanto avviene per
-le variabili in memoria, la posizione corrente sul file è condivisa fra il
-padre e tutti i processi figli. 
+(l'argomento dell'accesso concorrente ai file è trattato in dettaglio in
+sez.~\ref{sec:file_shared_access}), ma anche che, a differenza di quanto
+avviene per le variabili in memoria, la posizione corrente sul file è
+condivisa fra il padre e tutti i processi figli.
 
 Quello che succede è che quando lo \textit{standard output}\footnote{si chiama
   così il file su cui un programma scrive i suoi dati in uscita, tratteremo
@@ -563,8 +563,9 @@ processi figli tutti i \textit{file descriptor} (vedi sez.~\ref{sec:file_fd})
 dei file aperti nel processo padre (allo stesso modo in cui lo fa la funzione
 \func{dup}, trattata in sez.~\ref{sec:file_dup}), il che comporta che padre e
 figli condividono le stesse voci della \itindex{file~table} \textit{file
-  table} (tratteremo in dettagli questi termini in
-sez.~\ref{sec:file_sharing}) fra cui c'è anche la posizione corrente nel file.
+  table} (tratteremo in dettaglio questi termini in
+sez.~\ref{sec:file_shared_access}) fra cui c'è anche la posizione corrente nel
+file.
 
 In questo modo se un processo scrive su un file aggiornerà la posizione
 corrente sulla \itindex{file~table} \textit{file table}, e tutti gli altri
@@ -4148,11 +4149,11 @@ Nel caso dell'interazione fra processi la situazione è molto più semplice, ed
 occorre preoccuparsi della atomicità delle operazioni solo quando si ha a che
 fare con meccanismi di intercomunicazione (che esamineremo in dettaglio in
 cap.~\ref{cha:IPC}) o nelle operazioni con i file (vedremo alcuni esempi in
-sez.~\ref{sec:file_atomic}). In questi casi in genere l'uso delle appropriate
-funzioni di libreria per compiere le operazioni necessarie è garanzia
-sufficiente di atomicità in quanto le \textit{system call} con cui esse sono
-realizzate non possono essere interrotte (o subire interferenze pericolose) da
-altri processi.
+sez.~\ref{sec:file_shared_access}). In questi casi in genere l'uso delle
+appropriate funzioni di libreria per compiere le operazioni necessarie è
+garanzia sufficiente di atomicità in quanto le \textit{system call} con cui
+esse sono realizzate non possono essere interrotte (o subire interferenze
+pericolose) da altri processi.
 
 Nel caso dei segnali invece la situazione è molto più delicata, in quanto lo
 stesso processo, e pure alcune \textit{system call}, possono essere interrotti
index fb63b0b7ee85f78ca2a12907062c648eb8383557..4c60c0a7fe5e6b97299063780373ced4e606a378 100644 (file)
@@ -1241,9 +1241,9 @@ Come per tutti i file descriptor anche per i socket viene mantenuto un numero
 di riferimenti, per cui se più di un processo ha lo stesso socket aperto
 l'emissione del FIN e la sequenza di chiusura di TCP non viene innescata
 fintanto che il numero di riferimenti non si annulla, questo si applica, come
-visto in sez.~\ref{sec:file_sharing}, sia ai file descriptor duplicati che a
-quelli ereditati dagli eventuali processi figli, ed è il comportamento che ci
-si aspetta in una qualunque applicazione client/server.
+visto in sez.~\ref{sec:file_shared_access}, sia ai file descriptor duplicati
+che a quelli ereditati dagli eventuali processi figli, ed è il comportamento
+che ci si aspetta in una qualunque applicazione client/server.
 
 Per attivare immediatamente l'emissione del FIN e la sequenza di chiusura
 descritta in sez.~\ref{sec:TCP_conn_term}, si può invece usare la funzione