Completato I/O binario
[gapil.git] / fileunix.tex
index a8a522867180c1d4e03c4531a9a13149115322ca..17a68ed645e5a2fc4ad033a422e7ecaf9b40f105 100644 (file)
@@ -1,12 +1,12 @@
-\chapter{L'interfaccia unix di I/O con i file}
+\chapter{I file: l'interfaccia standard unix}
 \label{cha:file_unix_interface}
 
 Esamineremo in questo capitolo la prima delle due interfacce di programmazione
 per i file, quella dei \textit{file descriptor}, nativa di unix. Questa è
-l'interfaccia di basso livello, che non prevede funzioni evolute come la
-bufferizzazione o funzioni di lettura o scrittura formattata, ma è su questa
-che è costruita anche l'interfaccia standard dei file definita dallo standard
-ANSI C.
+l'interfaccia di basso livello provvista direttamente dalle system call, che
+non prevede funzionalità evolute come la bufferizzazione o funzioni di lettura
+o scrittura formattata, e sulla quale è costruita anche l'interfaccia definita
+dallo standard ANSI C che affronteremo in \capref{cha:files_std_interface}.
 
 
 
@@ -205,12 +205,7 @@ prototipo 
 
 La funzione apre il file, usando il primo file descriptor libero, e crea
 l'opportuna voce (cioè la struttura \var{file}) nella file table.  Viene usato
-sempre il file descriptor con il valore più basso. Questa caratteristica
-permette di prevedere qual'è il valore che si otterrà, e viene talvolta usata
-da alcune applicazioni per sostituire i file corrispondenti ai file standard
-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).
+sempre il file descriptor con il valore più basso. 
 
 \begin{table}[!htb]
   \centering
@@ -308,6 +303,13 @@ cio
   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.}
 
+Questa caratteristica permette di prevedere qual'è il valore del file
+descriptor che si otterrà al ritorno di \func{open}, e viene talvolta usata da
+alcune applicazioni per sostituire i file corrispondenti ai file standard 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).
+
 
 Il nuovo file descriptor non è condiviso con nessun altro processo, (torneremo
 sulla condivisione dei file, in genere accessibile dopo una \func{fork}, in
@@ -351,7 +353,9 @@ secondo le tre modalit
 ciascuno di questi bit, dette costanti possono essere combinate fra di loro
 con un OR aritmetico per costruire il valore (in forma di maschera binaria)
 del parametro \var{flags} da passare alla \func{open} per specificarne il
-comportamento.
+comportamento. I due flag \macro{O\_NOFOLLOW} e \macro{O\_DIRECTORY} sono
+estensioni specifiche di Linux, e deve essere usata definita la macro
+\macro{\_GNU\_SOURCE} per poterli usare.
 
 Nelle prime versioni di unix i flag specificabili per \func{open} erano solo
 quelli relativi alle modalità di accesso del file.  Per questo motivo per
@@ -390,7 +394,7 @@ aperto, tutte le risorse nella file table vengono rilasciate. Infine se il
 file descriptor era l'ultimo riferimento ad un file su disco quest'ultimo
 viene cancellato.
 
-Si ricordi che quando un processo termina anche tutti i sui file descriptor
+Si ricordi che quando un processo termina anche tutti i suoi file descriptor
 vengono chiusi, molti programmi sfruttano questa caratteristica e non usano
 esplicitamente \func{close}. In genere comunque chiudere un file senza
 controllarne lo stato di uscita è errore; infatti molti filesystem
@@ -404,9 +408,10 @@ quote su disco.
 In ogni caso una \func{close} andata a buon fine non garantisce che i dati
 siano stati effettivamente scritti su disco, perché il kernel può decidere di
 ottimizzare l'accesso a disco ritardandone la scrittura. L'uso della funzione
-\func{sync} effettua esplicitamente il \emph{flush} dei dati, ma anche in
-questo caso resta l'incertezza dovuta al comportamento dell'hardware (che a
-sua volta può introdurre ottimizzazioni dell'accesso al disco).
+\func{sync} (vedi \secref{sec:file_sync}) effettua esplicitamente il
+\emph{flush} dei dati, ma anche in questo caso resta l'incertezza dovuta al
+comportamento dell'hardware (che a sua volta può introdurre ottimizzazioni
+dell'accesso al disco).
 
 
 \subsection{La funzione \func{lseek}}
@@ -472,7 +477,7 @@ Non tutti i file supportano la capacit
 questo caso la funzione ritorna l'errore \macro{EPIPE}. Questo, oltre che per
 i tre casi citati nel prototipo, vale anche per tutti quei dispositivi che non
 supportano questa funzione, come ad esempio per le \acr{tty}\footnote{altri
-  sistemi, usando \macro{SEEK\_SET} in questo caso ritornano il numero di
+  sistemi, usando \macro{SEEK\_SET}, in questo caso ritornano il numero di
   caratteri che vi sono stati scritti}. Lo standard POSIX però non specifica
 niente al proposito. Infine alcuni device, ad esempio \file{/dev/null}, non
 causano un errore ma restituiscono un valore indefinito.
@@ -481,8 +486,9 @@ causano un errore ma restituiscono un valore indefinito.
 \subsection{La funzione \func{read}}
 \label{sec:file_read}
 
-Per leggere da un file precedentemente aperto, si può la funzione \func{read},
-il cui prototipo è:
+
+Una volta che un file è stato aperto su possono leggere i dati che contiene
+utilizzando la funzione \func{read}, il cui prototipo è:
 \begin{prototype}{unistd.h}{ssize\_t read(int fd, void * buf, size\_t count)}
   
   La funzione cerca di leggere \var{count} byte dal file \var{fd} al buffer
@@ -567,17 +573,18 @@ per \func{read} e \func{lseek}.
 \end{prototype}
 
 Questa funzione serve quando si vogliono leggere dati dal file senza
-modificarne la posizione corrente. È equivalente alla esecuzione di una
-\func{read} e una \func{lseek}, ma dato che la posizione sul file può essere
-condivisa fra vari processi (vedi \secref{sec:file_sharing}), essa permette di
-eseguire l'operazione atomicamente. Il valore di \var{offset} fa riferimento
-all'inizio del file.
+modificarne la posizione corrente. È sostanzialmente equivalente alla
+esecuzione di una \func{read} e una \func{lseek}, ma dato che la posizione sul
+file può essere condivisa fra vari processi (vedi \secref{sec:file_sharing}),
+essa permette di eseguire l'operazione atomicamente. Il valore di \var{offset}
+fa sempre riferimento all'inizio del file.
 
 
 \subsection{La funzione \func{write}}
 \label{sec:file_write}
 
-Per scrivere su un file si usa la funzione \func{write}, il cui prototipo è:
+Una volta che un file è stato aperto su può scrivere su di esso utilizzando la
+funzione \func{write}, il cui prototipo è:
 \begin{prototype}{unistd.h}{ssize\_t write(int fd, void * buf, size\_t count)}
   
   La funzione scrive \var{count} byte dal buffer \var{buf} sul file \var{fd}.
@@ -767,6 +774,71 @@ assenza, diventa atomica essendo svolta tutta all'interno di una singola
 \func{open}.
 
 
+\subsection{La funzioni \func{sync} e \func{fsync}}
+\label{sec:file_sync}
+
+Come accennato in \secref{sec:file_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 da la garanzia assoluta che i dati siano integri dopo la chiamata,
+  l'hardware dei dischi è in genere dotato di un suo meccanismo interno che
+  può ritardare ulteriormente la scrittura effettiva.}. La prima di queste
+funzioni è \func{sync} il cui prototipo è:
+\begin{prototype}{unistd.h}{int sync(void)}
+  
+  La funzione sincronizza buffer della cache dei file col disco.
+  
+  La funzione ritorna sempre zero.
+\end{prototype}
+\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 per l'update dei dati è ogni 30 secondi, ma in Linux era
+di 5 secondi; con le nuove versioni poi, è il kernel che si occupa
+direttamente di tutto quanto.
+
+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 \func{fsync} e \func{fdatasync}, i cui prototipi sono:
+\begin{functions}
+  \headdecl{unistd.h}
+  \funcdecl{int fsync(int fd)}
+  Sincronizza dati e metadati del file \param{fd}
+  \funcdecl{int fdatasync(int fd)}
+  Sincronizza i dati del file \param{fd}.
+  
+  La funzione ritorna 0 in caso di successo e -1 in caso di errore, nel qual
+  caso i codici restituiti in \var{errno} sono:
+  \begin{errlist}
+  \item \macro{EINVAL} \param{fd} è un file speciale che non supporta la
+    sincronizzazione.
+  \end{errlist}
+  ed inoltre \macro{EBADF}, \macro{EROFS} e \macro{EIO}.
+\end{functions}
+
+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 metadata dell'inode (i dati
+di \var{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{La funzioni \func{dup} e \func{dup2}}
 \label{sec:file_dup}
@@ -860,7 +932,7 @@ file descriptor viene usata la funzione \func{fcntl} il cui prototipo 
   \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)}
+  \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}.
   
@@ -893,13 +965,96 @@ valori 
 \item[\macro{F\_GETFL}] ritorna il valore del \textit{file status flag},
   permette cioè di rileggere quei bit settati da \func{open} all'apertura del
   file che vengono memorizzati (quelli riportati nella prima e terza sezione
-  di \tabref{tab:file_open_flags}).
+  di \tabref{tab:file_open_flags}). 
 \item[\macro{F\_SETFL}] setta il \textit{file status flag} al valore
   specificato da \param{arg}, possono essere settati solo i bit riportati
   nella terza sezione di \tabref{tab:file_open_flags} (da verificare).
+\item[\macro{F\_GETLK}] se un file lock è attivo restituisce nella struttura
+  \param{lock} la struttura \type{flock} che impedisce l'acquisizione del
+  blocco, altrimenti setta il campo \var{l\_type} a \macro{F\_UNLCK} (per i
+  dettagli sul \textit{file locking} vedi \secref{sec:file_locking}).
+\item[\macro{F\_SETLK}] richiede il file lock specificato da \param{lock} se
+  \var{l\_type} è \macro{F\_RDLCK} o \macro{F\_WRLLCK} o lo rilascia se
+  \var{l\_type} è \macro{F\_UNLCK}. Se il lock è tenuto da qualcun'altro
+  ritorna immediatamente restituendo -1 e setta \var{errno} a \macro{EACCES} o
+  \macro{EAGAIN} (per i dettagli sul \textit{file locking} vedi
+  \secref{sec:file_locking}).
+\item[\macro{F\_SETLKW}] identica a \macro{F\_SETLK} eccetto per il fatto che
+  la funzione non ritorna subito ma attende che il blocco sia rilasciato. Se
+  l'attesa viene interrotta da un segnale la funzione restituisce -1 e setta
+  \var{errno} a \macro{EINTR} (per i dettagli sul \textit{file locking} vedi
+  \secref{sec:file_locking}).
+\item[\macro{F\_GETOWN}] restituisce il \acr{pid} del processo o il process
+  group che è preposto alla ricezione dei segnali \macro{SIGIO} e
+  \macro{SIGURG} per gli eventi associati al file descriptor \var{fd}. Il
+  process group è restituito come valore negativo.
+\item[\macro{F\_SETOWN}] setta il processo o process group che riceverà i
+  segnali \macro{SIGIO} e \macro{SIGURG} per gli eventi associati al file
+  descriptor \var{fd}.  I process group sono settati usando valori negativi.
+\item[\macro{F\_GETSIG}] restituisce il segnale mandato quando ci sono dati
+  disponibili in input sul file descriptor. Il valore 0 indica il default (che
+  è \macro{SIGIO}), un valore diverso da zero indica il segnale richiesto,
+  (che può essere lo stesso \macro{SIGIO}), nel qual caso al manipolatore del
+  segnale, se installato con \macro{SA\_SIGINFO}, vengono rese disponibili
+  informazioni ulteriori informazioni.
+\item[\macro{F\_SETSIG}] setta il segnale da inviare quando diventa possibile
+  effettuare I/O sul file descriptor. Il valore zero indica il default
+  (\macro{SIGIO}), ogni altro valore permette di rendere disponibile al
+  manipolatore del segnale ulteriori informazioni se si è usata
+  \macro{SA\_SIGINFO}.
 \end{basedescript}
 
+La maggior parte delle funzionalità di \func{fcntl} sono troppo avanzate per
+poter essere affrontate in dettaglio a questo punto; saranno riprese più
+avanti quando affronteremo le problematiche ad esse relative.
+
+Per determinare le modalità di accesso inoltre è necessario estrarre i bit di
+accesso (ottenuti con il comando \macro{F\_GETFL}); infatti la definizione
+corrente non assegna bit separati a \macro{O\_RDONLY}, \macro{O\_WRONLY} e
+\macro{O\_RDWR}\footnote{posti rispettivamente ai valori 0, 1 e 2}, per cui il
+valore si ottiene eseguendo un AND binario del valore di ritorno di
+\func{fcntl} con la maschera \macro{O\_ACCMODE} anch'essa definita in
+\file{fcntl.h}.
+
+
 
 \subsection{La funzione \func{ioctl}}
 \label{sec:file_ioctl}
 
+Benché il concetto di \textit{everything is a file} si sia dimostratato molto
+valido anche per l'interazione con i più vari dispositivi, con cui si può
+interagire con le stesse funzioni usate per i normali file di dati,
+esisteranno sempre caratteristiche peculiari, specifiche dell'hardware e della
+funzionalità che ciascuno di essi provvede, che non possono venire comprese in
+questa interfaccia astratta (un caso tipico è il settaggio della velocità di
+una porta seriale, o le dimensioni di un framebuffer).
+
+Per questo motivo l'architettura del sistema ha previsto l'esistenza di una
+funzione speciale, \func{ioctl}, con cui poter compiere operazioni specifiche
+per ogni singolo dispositivo.  Il prototipo di questa funzione è:
+
+\begin{prototype}{sys/ioctl.h}{int ioctl(int fd, int request, ...)}
+  
+  La funzione manipola il sottostante dispositivo, usando il parametro
+  \param{request} per specificare l'operazione richiesta e il terzo parametro
+  (che usualmente è di tipo \param{char * argp}) per passare o ricevere
+  l'informazione necessaria al dispositivo.
+  
+  La funzione nella maggior parte dei casi ritorna 0, alcune operazioni usano
+  però il valore di ritorno per restituire informazioni. In caso di errore
+  viene sempre restituito -1 e \var{errno} viene settata ad uno dei valori
+  seguenti:
+  \begin{errlist}
+  \item \macro{ENOTTY} il file \param{fd} non è associato con un device.
+  \item \macro{EINVAL} gli argomenti \param{request} o \param{argp} non sono
+    validi.
+  \end{errlist}
+  ed inoltre \macro{EBADF} e \macro{EFAULT}.
+\end{prototype}
+
+La funzione serve in sostanza per fare tutte quelle operazioni che non si
+adattano all'architettura di I/O di unix e che non è possibile effettuare con
+le funzioni esaminate finora. Per questo motivo non è possibile fare altro che
+una descrizione generica; torneremo ad esaminarla in seguito, quando si
+tratterà di applicarla ad alcune problematiche specifiche.
+