\subsection{L'architettura dei \textit{file descriptor}}
\label{sec:file_fd}
-\index{file!descriptor|(} Per poter accedere al contenuto di un file occorre
+\index{file!descriptor|(}
+
+Per poter accedere al contenuto di un file occorre
creare un canale di comunicazione con il kernel che renda possibile operare su
di esso (si ricordi quanto visto in sez.~\ref{sec:file_vfs_work}). Questo si
fa aprendo il file con la funzione \func{open} che provvederà a localizzare
In fig.~\ref{fig:file_proc_file} si è riportato uno schema in cui è illustrata
questa architettura, ed in cui si sono evidenziate le interrelazioni fra le
varie strutture di dati sulla quale essa è basata.
+Ritorneremo su questo schema più volte, dato che esso è fondamentale per
+capire i dettagli del funzionamento dell'interfaccia dei \textit{file
+ descriptor}.
+\index{file!descriptor|)}
+
\begin{figure}[htb]
\centering
\includegraphics[width=13cm]{img/procfile}
l'interfaccia dei \textit{file descriptor}.}
\label{fig:file_proc_file}
\end{figure}
-Ritorneremo su questo schema più volte, dato che esso è fondamentale per
-capire i dettagli del funzionamento dell'interfaccia dei \textit{file
- descriptor}.
-\index{file!descriptor|)}
-
stato chiuso nessuno in precedenza).
In tutti i sistemi unix-like esiste una convenzione generale per cui ogni
-processo viene lanciato con almeno tre file aperti. Questi, per quanto appena
-detto, avranno come \textit{file descriptor}\index{file!descriptor} i valori
-0, 1 e 2. Benché questa sia soltanto una convenzione, essa è seguita dalla
-gran parte delle applicazioni, e non aderirvi potrebbe portare a gravi
-problemi di interoperabilità.
+processo viene lanciato dalla shell con almeno tre file aperti. Questi, per
+quanto appena detto, avranno come \textit{file
+ descriptor}\index{file!descriptor} i valori 0, 1 e 2. Benché questa sia
+soltanto una convenzione, essa è seguita dalla gran parte delle applicazioni,
+e non aderirvi potrebbe portare a gravi problemi di interoperabilità.
Il primo file è sempre associato al cosiddetto \textit{standard input}; è cioè
-il file da cui il processo si aspetta di ricevere i dati in ingresso (nel caso
-della shell, è associato all'input del terminale, cioè alla lettura della
-tastiera). Il secondo file è il cosiddetto \textit{standard output}, cioè
-quello su cui ci si aspetta debbano essere inviati i dati in uscita (sempre
-nel caso della shell, è associato all'output del terminale, cioè alla
-scrittura sullo schermo). Il terzo è lo \textit{standard error}, su cui viene
-inviata l'uscita relativa agli errori, ed è anch'esso associato al terminale.
-Lo standard POSIX.1 provvede, al posto di questi valori numerici, tre costanti
-simboliche, definite in tab.~\ref{tab:file_std_files}.
+il file da cui il processo si aspetta di ricevere i dati in ingresso. Il
+secondo file è il cosiddetto \textit{standard output}, cioè quello su cui ci
+si aspetta debbano essere inviati i dati in uscita. Il terzo è lo
+\textit{standard error}, su cui viene inviata l'uscita relativa agli errori.
+Nel caso della shell tutti questi file sono associati al terminale di
+controllo, e corrispondono quindi alla lettura della tastiera per l'ingresso e
+alla scrittura sul terminale per l'uscita. Lo standard POSIX.1 provvede, al
+posto dei valori numerici, tre costanti simboliche, definite in
+tab.~\ref{tab:file_std_files}.
\begin{table}[htb]
\centering
L'interfaccia standard Unix per l'input/output sui file è basata su cinque
funzioni fondamentali: \func{open}, \func{read}, \func{write}, \func{lseek} e
\func{close}, usate rispettivamente per aprire, leggere, scrivere, spostarsi e
-chiudere un file.
-
-La gran parte delle operazioni sui file si effettua attraverso queste cinque
-funzioni, esse vengono chiamate anche funzioni di I/O non bufferizzato dato
-che effettuano le operazioni di lettura e scrittura usando direttamente le
-system call del kernel.
+chiudere un file. La gran parte delle operazioni sui file si effettua
+attraverso queste cinque funzioni, esse vengono chiamate anche funzioni di I/O
+non bufferizzato dato che effettuano le operazioni di lettura e scrittura
+usando direttamente le system call del kernel.
\subsection{La funzione \func{open}}
\label{sec:file_open}
La funzione \funcd{open} è la funzione fondamentale per accedere ai file, ed è
-quella che crea l'associazione fra un
-\index{\textit{pathname}}\textit{pathname} ed un file descriptor, il suo
-prototipo è:
+quella che crea l'associazione fra un \itindex{pathname}\textit{pathname} ed
+un file descriptor, il suo prototipo è:
\begin{functions}
\headdecl{sys/types.h}
\headdecl{sys/stat.h}
La funzione apre il file, usando il primo file descriptor libero, e crea
l'opportuna voce (cioè la struttura \struct{file}) nella \textit{file table}.
-Viene usato sempre il file descriptor con il valore più basso.
+Viene sempre restituito come valore di ritorno il file descriptor con il
+valore più basso disponibile.
\begin{table}[!htb]
\centering
\textbf{Flag} & \textbf{Descrizione} \\
\hline
\hline % modalità di accesso al file
- \const{O\_RDONLY} & apre il file in sola lettura. \\
- \const{O\_WRONLY} & apre il file in sola scrittura. \\
- \const{O\_RDWR} & apre il file in lettura/scrittura. \\
+ \const{O\_RDONLY} & apre il file in sola lettura, le \acr{glibc}
+ definiscono anche \const{O\_READ} come sinonimo. \\
+ \const{O\_WRONLY} & apre il file in sola scrittura, le \acr{glibc}
+ definiscono anche \const{O\_WRITE} come sinonimo. \\
+ \const{O\_RDWR} & apre il file sia in lettura che in scrittura. \\
\hline % modalità di apertura del file
\hline
\const{O\_CREAT} & se il file non esiste verrà creato, con le regole di
titolarità del file viste in
- sez.~\ref{sec:file_ownership}. L'argomento
- \param{mode} deve essere specificato. \\
+ sez.~\ref{sec:file_ownership}. Con questa opzione
+ l'argomento \param{mode} deve essere specificato. \\
\const{O\_EXCL} & usato in congiunzione con \const{O\_CREAT} fa sì che
- l'esistenza del file diventi un
+ la precedente esistenza del file diventi un
errore\protect\footnotemark\ che fa fallire
\func{open} con \errcode{EEXIST}. \\
\const{O\_NONBLOCK}& apre il file in modalità non bloccante. Questo
\hline % modalità di operazione col file
\const{O\_APPEND} & il file viene aperto in append mode. Prima di ciascuna
scrittura la posizione corrente viene sempre impostata
- alla fine del file. Può causare corruzione del file
- con NFS se più di un processo scrive allo stesso
- tempo.\footnotemark\\
+ alla fine del file. Con NFS si può avere una
+ corruzione del file se più di un processo scrive allo
+ stesso tempo.\footnotemark\\
\const{O\_NONBLOCK}&il file viene aperto in modalità non bloccante per
le operazioni di I/O (che tratteremo in
sez.~\ref{sec:file_noblocking}): questo significa il
\const{O\_SYNC} & apre il file per l'input/output sincrono: ogni
\func{write} bloccherà fino al completamento della
scrittura di tutti i dati sull'hardware sottostante.\\
- \const{O\_FSYNC} & sinonimo di \const{O\_SYNC}. \\
+ \const{O\_FSYNC} & sinonimo di \const{O\_SYNC}, usato da BSD. \\
+ \const{O\_DSYNC} & richiede una variante di I/O sincorno definita nello
+ standard POSIX; definita a partire dal kernel 2.1.130
+ come sinonimo di \const{O\_SYNC}. \\
+ \const{O\_RSYNC} & richiede una variante di I/O sincorno definita nello
+ standard POSIX; definita a partire dal kernel 2.1.130
+ come sinonimo di \const{O\_SYNC}. \\
\const{O\_NOATIME}& blocca l'aggiornamento dei tempi di accesso dei
file (vedi sez.~\ref{sec:file_file_times}). Per molti
filesystem questa funzionalità non è disponibile per
- il singolo file ma come opzione in fase di montaggio.\\
+ il singolo file ma come opzione generale da
+ specificare in fase di montaggio.\\
+ \const{O\_DIRECT} & esegue l'I/O direttamente dai buffer in user space, ed
+ in maniera sincrona, in modo da scavalcare i
+ meccanismi di caching del kernel. In gebere questo
+ peggiora le prestazioni tranne per casi speciali in
+ cui sono le applicazioni\footnotemark a gestire il
+ caching. Per i kernel della serie 2.4 si deve
+ garantire che i buffer in user space siano allineati
+ alle dimensioni dei blocchi del filesystem; per il
+ kernel 2.6 basta che siano allineati a multipli di 512
+ byte.\\
\hline
\end{tabular}
\caption{Valori e significato dei vari bit del \textit{file status flag}.}
\footnotetext[2]{la pagina di manuale di \func{open} segnala che questa
opzione è difettosa su NFS, e che i programmi che la usano per stabilire un
- \textsl{file di lock}\index{file!di lock} possono incorrere in una race
- condition\index{\textit{race~condition}}. Si consiglia come alternativa di
- usare un file con un nome univoco e la funzione \func{link} per verificarne
- l'esistenza (vedi sez.~\ref{sec:ipc_file_lock}).}
+ \textsl{file di lock}\index{file!di lock} possono incorrere in una
+ \textit{race condition}\itindex{race~condition}. Si consiglia come
+ alternativa di usare un file con un nome univoco e la funzione \func{link}
+ per verificarne l'esistenza (vedi sez.~\ref{sec:ipc_file_lock}).}
\footnotetext[3]{\textit{Denial of Service}\index{DoS}, si chiamano così
attacchi miranti ad impedire un servizio causando una qualche forma di
un'ambiguità, dato che come vedremo in sez.~\ref{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
+\footnotetext[6]{l'opzione è stata introdotta dalla SGI in IRIX, e serve
+ sostanzialmente a permettere ad alcuni programmi (in genere database) la
+ gestione diretta della bufferizzazione dell'I/O in quanto essi sono in grado
+ di ottimizzarla al meglio per le loro prestazioni; l'opzione è presente
+ anche in FreeBSD, senza limiti di allineamento dei buffer. In Linux è stata
+ introdotta con il kernel 2.4.10, le versioni precedenti la ignorano.}
+
+
+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
visti in sez.~\ref{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 sez.~\ref{sec:file_sharing}) ed è
-impostato per restare aperto attraverso una \func{exec} (come accennato in
-sez.~\ref{sec:proc_exec}); l'offset è impostato all'inizio del file.
+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
+sez.~\ref{sec:file_sharing}) ed è impostato per restare aperto attraverso una
+\func{exec} (come accennato in sez.~\ref{sec:proc_exec}); l'offset è impostato
+all'inizio del file.
L'argomento \param{mode} indica i permessi con cui il file viene creato; i
valori possibili sono gli stessi già visti in sez.~\ref{sec:file_perm_overview}
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 \textit{race condition}
-\index{\textit{race~condition}}, vedi sez.~\ref{sec:file_atomic}).
+(questa è una potenziale sorgente di \itindex{race~condition}\textit{race
+ condition}, vedi sez.~\ref{sec:file_atomic}).
Non tutti i file supportano la capacità di eseguire una \func{lseek}, in
questo caso la funzione ritorna l'errore \errcode{EPIPE}. Questo, oltre che per
Lo stesso comportamento avviene caso di lettura dalla rete (cioè su un
socket\index{socket}, come vedremo in sez.~\ref{sec:sock_io_behav}), o per la
lettura da certi file di dispositivo, come le unità a nastro, che
-restituiscono sempre i dati ad un singolo blocco alla volta.
-
-In realtà anche le due condizioni segnalate dagli errori \errcode{EINTR} e
-\errcode{EAGAIN} non sono errori. La prima si verifica quando la \func{read} è
-bloccata in attesa di dati in ingresso e viene interrotta da un segnale; in
-tal caso l'azione da intraprendere è quella di rieseguire la funzione.
-Torneremo in dettaglio sull'argomento in sez.~\ref{sec:sig_gen_beha}. La
-seconda si verifica quando il file è in modalità non bloccante (vedi
-sez.~\ref{sec:file_noblocking}) e non ci sono dati in ingresso: la funzione
-allora ritorna immediatamente con un errore \errcode{EAGAIN}\footnote{BSD usa
- per questo errore la costante \errcode{EWOULDBLOCK}, in Linux, con le
- \acr{glibc}, questa è sinonima di \errcode{EAGAIN}.} che indica soltanto che
-occorrerà provare a ripetere la lettura.
+restituiscono sempre i dati ad un singolo blocco alla volta, o come le linee
+seriali, che restituiscono solo i dati ricevuti fino al momento della lettura.
+
+Infine anche le due condizioni segnalate dagli errori \errcode{EINTR} ed
+\errcode{EAGAIN} non sono propriamente degli errori. La prima si verifica
+quando la \func{read} è bloccata in attesa di dati in ingresso e viene
+interrotta da un segnale; in tal caso l'azione da intraprendere è quella di
+rieseguire la funzione. Torneremo in dettaglio sull'argomento in
+sez.~\ref{sec:sig_gen_beha}. La seconda si verifica quando il file è aperto
+in modalità non bloccante (vedi sez.~\ref{sec:file_noblocking}) e non ci sono
+dati in ingresso: la funzione allora ritorna immediatamente con un errore
+\errcode{EAGAIN}\footnote{BSD usa per questo errore la costante
+ \errcode{EWOULDBLOCK}, in Linux, con le \acr{glibc}, questa è sinonima di
+ \errcode{EAGAIN}.} che indica soltanto che non essendoci al momento dati
+disponibili occorre provare a ripetere la lettura in un secondo tempo.
La funzione \func{read} è una delle system call fondamentali, esistenti fin
dagli albori di Unix, ma nella seconda versione delle \textit{Single Unix
in caso di errore, nel qual caso \var{errno} assumerà i valori già visti per
\func{read} e \func{lseek}.}
\end{prototype}
-\noindent che però diventa accessibile solo con la definizione della macro:
+
+La funzione prende esattamente gli stessi argomenti di \func{read} con lo
+stesso significato, a cui si aggiunge l'argomento \func{offset} che indica una
+posizione sul file. Indetico è il comportamento ed il valore di ritorno. La
+funzione serve quando si vogliono leggere dati dal file senza modificare la
+posizione corrente.
+
+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
+\param{offset} fa sempre riferimento all'inizio del file.
+
+La funzione \func{pread} è disponibile anche in Linux, però diventa
+accessibile solo attivando il supporto delle estensioni previste dalle
+\textit{Single Unix Specification} con la definizione della macro:
\begin{verbatim}
#define _XOPEN_SOURCE 500
\end{verbatim}
+e si ricordi di definire questa macro prima dell'inclusione del file di
+dichiarazioni \file{unistd.h}.
-Questa funzione serve quando si vogliono leggere dati dal file senza
-modificare la posizione corrente. È 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
-\param{offset} fa sempre riferimento all'inizio del file.
\subsection{La funzione \func{write}}
\label{sec:file_write}
-Una volta che un file è stato aperto (con il permesso in scrittura) su può
+Una volta che un file è stato aperto (con il permesso in scrittura) si può
scrivere su di esso utilizzando la funzione \funcd{write}, il cui prototipo è:
\begin{prototype}{unistd.h}{ssize\_t write(int fd, void * buf, size\_t count)}
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 \textit{race
- condition}\index{\textit{race~condition}}: infatti può succedere che un
+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
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 race
-condition\index{\textit{race~condition}} da parte di un altro processo che
-crea lo stesso file fra il controllo e la creazione.
+di nuovo avremmo la possibilità di una \textit{race
+ condition}\itindex{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
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}\index{\textit{close-on-exec}}
-(vedi sez.~\ref{sec:proc_exec} e sez.~\ref{sec:file_fcntl}) viene sempre
-cancellato nella copia.
+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};
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
+qual è il valore di file descriptor che si vuole avere come duplicato; il suo
prototipo è:
\begin{prototype}{unistd.h}{int dup2(int oldfd, int newfd)}
allo stesso valore per il file descriptor).
La duplicazione dei file descriptor può essere effettuata anche usando la
-funzione di controllo dei file \func{fnctl} (che esamineremo in
+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{fnctl(oldfd, F\_DUPFD, newfd)} e se si usa 0 come valore per
+la sintassi \code{fcntl(oldfd, F\_DUPFD, newfd)} e se si usa 0 come valore per
\param{newfd} diventa equivalente a \func{dup}.
La sola differenza fra le due funzioni\footnote{a parte la sintassi ed i
massimo numero di descrittori consentito.
\item[\const{F\_SETFD}] imposta il valore del \textit{file descriptor flag} al
valore specificato con \param{arg}. Al momento l'unico bit usato è quello di
- \textit{close-on-exec}\index{\textit{close-on-exec}}, identificato dalla
- costante \const{FD\_CLOEXEC}, che serve a richiedere che il file venga
- chiuso nella esecuzione di una \func{exec} (vedi sez.~\ref{sec:proc_exec}).
- Ritorna un valore nullo in caso di successo e -1 in caso di errore.
+ \textit{close-on-exec}\itindex{close-on-exec}, identificato dalla costante
+ \const{FD\_CLOEXEC}, che serve a richiedere che il file venga chiuso nella
+ esecuzione di una \func{exec} (vedi sez.~\ref{sec:proc_exec}). Ritorna un
+ valore nullo in caso di successo e -1 in caso di errore.
\item[\const{F\_GETFD}] ritorna il valore del \textit{file descriptor flag} di
\param{fd} o -1 in caso di errore; se \const{FD\_CLOEXEC} è impostato i file
descriptor aperti vengono chiusi attraverso una \func{exec} altrimenti (il
specifiche di ogni dispositivo particolare, usando come riferimento il solito
file descriptor. Il prototipo di questa funzione è:
\begin{prototype}{sys/ioctl.h}{int ioctl(int fd, int request, ...)}
- Manipola il dispositivo sottostante, usando il parametro \param{request} per
- specificare l'operazione richiesta ed il terzo parametro (usualmente di tipo
+ Manipola il dispositivo sottostante, usando l'argomento \param{request} per
+ specificare l'operazione richiesta ed il terzo argomento (usualmente di tipo
\param{char * argp} o \param{int argp}) per il trasferimento
dell'informazione necessaria.
qui riportiamo solo i valori di alcuni comandi che sono definiti per ogni
file:
\begin{basedescript}{\desclabelwidth{2.0cm}}
-\item[\const{FIOCLEX}] Imposta il bit di \textit{close on exec}.
-\item[\const{FIONCLEX}] Cancella il bit di \textit{close on exec}.
+\item[\const{FIOCLEX}] Imposta il flag di
+ \textit{close-on-exec}\itindex{close-on-exec}.
+\item[\const{FIONCLEX}] Cancella il flag di
+ \textit{close-on-exec}\itindex{close-on-exec}.
\item[\const{FIOASYNC}] Abilita l'I/O asincrono.
\item[\const{FIONBIO}] Abilita l'I/O in modalità non bloccante.
\end{basedescript}