Completate: condivisione, operazioni atomiche sui file, dup ed iniziata
[gapil.git] / fileunix.tex
index c2412e8139397171bae5f769e764872b4626e44c..06efedab97d2b23bb7f2115ecd3a177e28fe245e 100644 (file)
@@ -212,7 +212,6 @@ di \secref{sec:file_std_descr}: se ad esempio si chiude lo standard input e si
 apre subito dopo un nuovo file questo diventerà il nuovo standard input (avrà
 cioè il file descriptor 0).
 
-
 \begin{table}[!htb]
   \centering
   \footnotesize
@@ -230,12 +229,8 @@ cio
     titolarità del file viste in \secref{sec:file_ownership}. Il parametro
     \var{mode} deve essere specificato. \\
     \macro{O\_EXCL} & usato in congiunzione con \macro{O\_CREAT} fa sì che
-    l'esistenza del file diventi un errore\footnote{la man page di \func{open}
-    segnala che questa opzione è difettosa su NFS, e che i programmi che la
-    usano per stabilire un file di lock possono incorrere in una race
-    condition.  Si consiglia come alternativa di usare un file con un nome
-    univoco e la funzione \func{link} per verificarne l'esistenza.} che fa
-    fallire \func{open} con \macro{EEXIST}. \\
+    l'esistenza del file diventi un errore\protect\footnotemark\ che fa fallire
+    \func{open} con \macro{EEXIST}. \\
     \macro{O\_NONBLOCK} & apre il file in modalità non bloccante. Questo
     valore specifica anche una modalità di operazione (vedi sotto), e 
     comporta che \func{open} ritorni immediatamente (torneremo su
@@ -257,12 +252,10 @@ cio
     opzione è ignorata. \\
     \macro{O\_DIRECTORY} & se \var{pathname} non è una directory la chiamata
     fallisce. Questo flag è specifico di Linux ed è stato introdotto con il
-    kernel 2.1.126 per evitare dei DoS\footnote{Denial of Service, si chiamano
-    così attacchi miranti ad impedire un servizio causando una qualche forma
-    di carico eccessivo per il sistema, che resta bloccato nelle risposte
-    all'attacco} quando \func{opendir} viene chiamata su una fifo o su un
-    device di unità a nastri, non deve essere utilizzato al di fuori
-    dell'implementazione di \func{opendir}. \\
+    kernel 2.1.126 per evitare dei DoS\protect\footnotemark\ quando 
+    \func{opendir} viene chiamata su una 
+    fifo o su un device di unità a nastri, non deve essere utilizzato al di 
+    fuori dell'implementazione di \func{opendir}. \\
     \macro{O\_LARGEFILE} & nel caso di sistemi a 32 bit che supportano file di
     grandi dimensioni consente di aprire file le cui dimensioni non possono
     essere rappresentate da numeri a 31 bit. \\
@@ -271,19 +264,14 @@ cio
     \macro{O\_APPEND} & il file viene aperto in append mode. Prima di ciascuna
     scrittura la posizione corrente viene sempre settata alla fine del
     file. Può causare corruzione del file con NFS se più di un processo scrive
-    allo stesso tempo\footnote{il problema è che NFS non supporta la scrittura
-    in append, ed il kernel deve simularla, ma questo comporta la possibilità
-    di una race condition}.\\
+    allo stesso tempo\footnotemark.\\
     \macro{O\_NONBLOCK} & il file viene aperto in modalità non bloccante per
     le operazioni di I/O: questo significa il fallimento di una \func{read} in
     assenza di dati da leggere e quello di una \func{write} in caso di 
     impossibilità di scrivere immediatamente. L'opzione è effettiva solo per
     le fifo e per alcuni file di dispositivo. \\
-    \macro{O\_NDELAY} & in Linux\footnote{l'opzione origina da SVr4, dove però
-    causava il ritorno da una \func{read} con un valore nullo e non con un
-    errore, questo introduce una ambiguità, dato che come vedremo in
-    \secref{sec:file_read} il ritorno di zero da parte di \func{read} ha il
-    significato di una end-of-file} è sinonimo di \macro{O\_NONBLOCK}.\\
+    \macro{O\_NDELAY} & in Linux\footnotemark\ è sinonimo di 
+    \macro{O\_NONBLOCK}.\\
     \macro{O\_ASYNC} & apre il file per l'input/output in modalità
     asincrona. Non è supportato in Linux. \\
     \macro{O\_SYNC} & apre il file per l'input/output sincrono, ogni
@@ -301,6 +289,26 @@ cio
   \label{tab:file_open_flags}
 \end{table}
 
+\footnotetext[2]{la man page di \func{open} segnala che questa opzione è
+  difettosa su NFS, e che i programmi che la usano per stabilire un file di
+  lock possono incorrere in una race condition.  Si consiglia come alternativa
+  di usare un file con un nome univoco e la funzione \func{link} per
+  verificarne l'esistenza.}  
+
+\footnotetext[3]{Denial of Service, si chiamano così attacchi miranti ad
+  impedire un servizio causando una qualche forma di carico eccessivo per il
+  sistema, che resta bloccato nelle risposte all'attacco.}
+
+\footnotetext[4]{il problema è che NFS non supporta la scrittura in append, ed
+  il kernel deve simularla, ma questo comporta la possibilità di una race
+  condition, vedi \secref{sec:file_atomic}.}
+
+\footnotetext[5]{l'opzione origina da SVr4, dove però causava il ritorno da
+  una \func{read} con un valore nullo e non con un errore, questo introduce
+  una ambiguità, dato che come vedremo in \secref{sec:file_read} il ritorno di
+  zero da parte di \func{read} ha il significato di una end-of-file.}
+
+
 Il nuovo file descriptor non è condiviso con nessun altro processo, (torneremo
 sulla condivisione dei file, in genere accessibile dopo una \func{fork}, in
 \secref{sec:file_sharing}). Il nuovo file descriptor è settato di default per
@@ -354,8 +362,8 @@ prototipo 
   Crea un nuovo file vuoto, con i permessi specificati da \var{mode}. É del
   tutto equivalente a \func{open(filedes, O\_CREAT|O\_WRONLY|O\_TRUNC, mode)}. 
 \end{prototype}
-
-adesso questa funzione resta solo per compatibilità con i vecchi programmi.
+\noindent adesso questa funzione resta solo per compatibilità con i vecchi 
+programmi.
 
 
 \subsection{La funzione \func{close}}
@@ -372,7 +380,7 @@ descriptor ritorna disponibile; il suo prototipo 
     \item \macro{EBADF}  \var{fd} non è un descrittore valido.
     \item \macro{EINTR} la funzione è stata interrotta da un segnale.
   \end{errlist}
-  ed \macro{EIO}.
+  ed inoltre \macro{EIO}.
 \end{prototype}
 
 La chiusura di un file rilascia ogni blocco (il \textit{file locking} è
@@ -426,7 +434,7 @@ un valore qualsiasi con la funzione \func{lseek}, il cui prototipo 
     \item \macro{ESPIPE} \var{fd} è una pipe, un socket o una fifo.
     \item \macro{EINVAL} \var{whence} non è un valore valido.
   \end{errlist}
-  e \macro{EBADF}.
+  ed inoltre \macro{EBADF}.
 \end{functions}
 
 La nuova posizione è settata usando il valore specificato da \var{offset},
@@ -457,9 +465,8 @@ Si tenga presente inoltre che usare \macro{SEEK\_END} non assicura affatto che
 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 settata in precedenza.
-Questa è una potenziale sorgente di \textit{race condition}, e quando si vuole
-essere sicuri di scrivere alla fine del file questo deve essere posto in
-modalità \macro{O\_APPEND}.
+(questa è una potenziale sorgente di \textit{race condition}, vedi
+\secref{sec:file_atomic}).
 
 Non tutti i file supportano la capacità di eseguire una \func{lseek}, in
 questo caso la funzione ritorna l'errore \macro{EPIPE}. Questo, oltre che per
@@ -672,8 +679,8 @@ stesso file, in particolare occorre tenere presente che:
   automaticamente con l'aggiornamento del campo \var{i\_size} nell'inode.
 \item se un file è in modalità \macro{O\_APPEND} tutte le volte che viene
   effettuata una scrittura la posizione corrente viene prima settata alla
-  dimensione corrente del file letta dall'inode. In questo modo il file viene
-  automaticamente esteso.
+  dimensione corrente del file letta dall'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 \var{file} della \textit{file table}, non c'è nessuna
   operazione sul file su disco. Quando la si usa per porsi alla fine del file
@@ -713,30 +720,174 @@ vengono toccati anche in caso di condivisione della voce della \textit{file
 
 
 
-
 \subsection{Operazioni atomiche coi file}
 \label{sec:file_atomic}
 
-cvs add 
+Come si è visto in un sistema unix è 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 \textit{file
+  locking}, che esamineremo in \secref{cha:file_advanced}).
+
+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 \secref{sec:file_lseek} settare la posizione alla fine
+del file e poi scrivere può condurre ad una \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 settata 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 system call in successione non è una operazione
+atomica; il problema è stato risolto introducendo la modalità
+\macro{O\_APPEND}, in questo caso infatti, come abbiamo visto, è 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 system call (la \func{write}) che non essendo interrompibile da un
+altro processo costituisce una operazione atomica.
+
+Un altro caso tipico in cui è necessaria l'atomicità è quello in cui si vuole
+creare un 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 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 i due flag \macro{O\_CREAT} e
+\macro{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
+\func{open}.
 
 
 
 \subsection{La funzioni \func{dup} e \func{dup2}}
 \label{sec:file_dup}
 
+Abbiamo già visto in \secref{sec:file_sharing} 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 \func{dup} il cui
+prototipo è:
+\begin{prototype}{unistd.h}{int dup(int oldfd)}
+  
+  La funzione crea una copia del file descriptor \param{oldfd}.
+  
+  La funzione ritorna il nuovo file descriptor in caso di successo e -1 in
+  caso di errore, nel qual caso \var{errno} viene settata ad uno dei valori:
+  \begin{errlist}
+  \item \macro{EBADF} \param{oldfd} non è un file aperto.
+  \item \macro{EMFILE} si è raggiunto il numero massimo consentito di file
+    descriptor aperti.
+  \end{errlist}
+\end{prototype}
 
+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 \figref{fig:file_dup}: l'effetto della funzione è
+semplicamente quello di copiare il valore nella struttura \var{file\_struct},
+cosicché anche il nuovo file descriptor fa riferirimento alla stessa voce
+nella \textit{file table}.
 
 \begin{figure}[htb]
-  \centering
-  \includegraphics[width=14cm]{img/filedup.eps}
+  \centering \includegraphics[width=14cm]{img/filedup.eps}
   \caption{Schema dell'accesso ai file duplicati}
   \label{fig:file_dup}
 \end{figure}
 
+In questo modo entrambi i file condivideranno eventuali lock, \textit{file
+  status flag}, e posizione corrente: se ad esempio \func{lseek} modifica la
+posizione su uno dei due file descriptor essa sarà modificata anche sull'altro
+(al solito viene modificato lo stesso campo nella voce della \textit{file
+  table} a cui entrambi fanno riferimento).
+
+L'unica differenza fra i due file descriptor è che ciascuno avrà il suo
+\textit{file descriptor flag}; nel caso di \func{dup} il flag di \textit{close
+  on exec} viene sempre cancellato nella copia.  
+
+Una diversa versione della funzione, \func{dup2} viene utilizzata per
+specificare esplicitamente il nuovo file descriptor; il suo prototipo è:
+\begin{prototype}{unistd.h}{int dup2(int oldfd, int newfd)}
+  
+  La funzione rende \param{newfd} una copia del file descriptor \param{oldfd}.
+  
+  La funzione ritorna il nuovo file descriptor in caso di successo e -1 in
+  caso di errore, nel qual caso \var{errno} viene settata ad uno dei valori:
+  \begin{errlist}
+  \item \macro{EBADF} \param{oldfd} non è un file aperto o \param{newfd} ha un
+    valore fuori dall'intervallo consentito per i file descriptor.
+  \item \macro{EMFILE} si è raggiunto il numero massimo consentito di file
+    descriptor aperti.
+  \end{errlist}
+\end{prototype}
+\noindent la funzione chiude il file descriptor \param{newfd} se è aperto.
+
+La duplicazione dei file descriptor può essere effettuata anche usando la
+funzione di controllo dei file \func{fnctl} (che esamineremo in
+\secref{sec:file_fcntl}) con il parametro \macro{F\_DUPFD}. 
+
+L'operazione ha la sintassi \func{fnctl(oldfd, F\_DUPFD, newfd)} e se si usa 0
+come valore per \param{newfd} diventa equivalente a \func{dup}. La sola
+differenza, a parte i codici di errore, è che \func{dup2} chiude il nuovo file
+se è già aperto mentre \func{fcntl} apre il primo disponibile con un valore
+superiore, per cui per poterla usare come \func{dup2} occorrerebbe prima
+effettare una \func{close}, perdendo l'atomicità dell'operazione.
+
+L'uso principale di queste funzioni è 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 su questo uso più avanti quando tratteremo le
+pipe.
+
 
 \subsection{La funzione \func{fcntl}}
 \label{sec:file_fcntl}
 
+Oltre alle operazioni base esaminate in \secref{sec:file_base_func} esistono
+tutta una serie di operazioni ausiliarie che è possibile eseguire su un file
+descriptor. Per queste operazioni di manipolazione delle varie proprietà di un
+file descriptor viene usata la funzione \func{fcntl} il cui prototipo è:
+\begin{functions}
+  \headdecl{unistd.h}
+  \headdecl{fcntl.h}
+  \funcdecl{int fcntl(int fd, int cmd)}
+  \funcdecl{int fcntl(int fd, int cmd, long arg)}
+  \funcdecl{int fcntl(int fd, int cmd, struct flock *lock)}
+  La funzione esegue una delle possibili operazioni specificate da \param{cmd}
+  sul file \param{fd}.
+  
+  La funzione ha valori di ritorno diversi a seconda dell'operazione. In caso
+  di errore il valore di ritorno è -1 e la variabile \var{errno} viene settata
+  ad un opportuno codice, quelli validi in generale sono:
+  \begin{errlist}
+  \item \macro{EBADF} \param{oldfd} non è un file aperto.
+  \end{errlist}
+\end{functions}
+
+Il comportamento di questa funzione è determinato dal valore del comando
+\param{cmd} che le viene fornito; in \secref{sec:file_dup} abbiamo incontrato
+un esempio, una lista dei possibili valori è riportata di seguito:
+\begin{basedescript}{\desclabelwidth{2.0cm}}
+\item[\macro{F\_DUPFD}] trova il primo file descriptor disponibile di valore
+  maggiore o uguale ad \param{arg} e ne fa una copia di \var{fd}. In caso di
+  successo ritorna il nuovo file descriptor. Gli errori possibili sono
+  \macro{EINVAL} se \param{arg} è negativo o maggiore del massimo consentito o
+  \macro{EMFILE} se il processo ha già raggiunto il massimo numero di
+  descrittori consentito.
+\item[\macro{F\_GETFD}] ritorna il valore dei \textit{file descriptor flag} di
+  \var{fd}, al momento è definito solo \macro{FD\_CLOEXEC}. Prova prov proep
+\item[\macro{F\_SETFD}] setta il valore dei \textit{file descriptor flag}
+  specificato da \param{arg}. 
+\end{basedescript}
+
 \subsection{La funzione \func{ioctl}}
 \label{sec:file_ioctl}