\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 provvista direttamente dalle system call, che
\section{L'architettura di base}
\label{sec:file_base_arch}
-In questa sezione faremo una breve introduzione sulla architettura su cui è
+In questa sezione faremo una breve introduzione sull'architettura su cui è
basata dell'interfaccia dei \textit{file descriptor}, che, sia pure con
differenze nella realizzazione pratica, resta sostanzialmente la stessa in
tutte le implementazione di un sistema unix-like.
\centering
\includegraphics[width=13cm]{img/procfile}
\caption{Schema della architettura dell'accesso ai file attraverso
- l'interfaccia dei \textit{file descriptor}}
+ l'interfaccia dei \textit{file descriptor}.}
\label{fig:file_proc_file}
\end{figure}
Ritorneremo su questo schema più volte, dato che esso è fondamentale per
\hline % modalità di accesso al file
\macro{O\_RDONLY} & apre il file in sola lettura. \\
\macro{O\_WRONLY} & apre il file in sola scrittura. \\
- \macro{O\_RDWR} & apre il file lettura/scrittura. \\
+ \macro{O\_RDWR} & apre il file in lettura/scrittura. \\
\hline % modalità di apertura del file
\hline
\macro{O\_CREAT} & se il file non esiste verrà creato, con le regole di
\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
- questo in \secref{sec:file_noblocking}). \\
+ comporta che \func{open} ritorni immediatamente (l'opzione ha senso
+ solo per le fifo, torneremo questo in \secref{sec:ipc_named_pipe}). \\
\macro{O\_NOCTTY} & se \var{pathname} si riferisce ad un device di
terminale, questo non diventerà il terminale di controllo, anche se il
processo non ne ha ancora uno (si veda \secref{sec:sess_xxx}). \\
file. Può causare corruzione del file con NFS se più di un processo scrive
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. \\
+ le operazioni di I/O (che tratteremo in \secref{sec:file_noblocking}):
+ questo significa il fallimento di \func{read} in assenza di dati da
+ leggere e quello di \func{write} in caso di impossibilità di scrivere
+ immediatamente. Questa modalità ha senso solo per le fifo e per alcuni
+ file di dispositivo. \\
\macro{O\_NDELAY} & in Linux\footnotemark\ è sinonimo di
\macro{O\_NONBLOCK}.\\
- \macro{O\_ASYNC} & apre il file per l'input/output in modalità
- asincrona. Quando è settato viene generato un segnale di \macro{SIGIO}
- tutte le volte che è disponibile dell'input sul file. \\
+ \macro{O\_ASYNC} & apre il file per l'I/O in modalità
+ asincrona (vedi \secref{sec:file_asyncronous_io}). Quando è settato viene
+ generato il segnale \macro{SIGIO} tutte le volte che sono disponibili
+ dati in input sul file. \\
\macro{O\_SYNC} & apre il file per l'input/output sincrono, ogni
\func{write} bloccherà fino al completamento della scrittura di tutti dati
sul sull'hardware sottostante.\\
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[3]{\textit{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
sulla condivisione dei file, in genere accessibile dopo una \func{fork}, in
\secref{sec:file_sharing}). Il nuovo file descriptor è settato di default per
restare aperto attraverso una \func{exec} (come accennato in
-\secref{sec:proc_exec}) ed l'offset è settato all'inizio del file.
+\secref{sec:proc_exec}) e l'offset è settato all'inizio del file.
L'argomento \param{mode} specifica i permessi con cui il file viene
eventualmente creato; i valori possibili sono gli stessi già visti in
Con i \textsl{file regolari} questa è l'unica situazione in cui si può avere
un numero di byte letti inferiore a quello richiesto, ma questo non è vero
quando si legge da un terminale, da una fifo o da una pipe. In tal caso
-infatti, se non ci sono dati in ingresso, la \func{read} si blocca e ritorna
-solo quando ne arrivano; se il numero di byte richiesti eccede quelli
-disponibili la funzione ritorna comunque, ma con un numero di byte inferiore a
-quelli richiesti.
+infatti, se non ci sono dati in ingresso, la \func{read} si blocca (a meno di
+non aver selezionato la modalità non bloccante, vedi
+\secref{sec:file_noblocking}) e ritorna solo quando ne arrivano; se il numero
+di byte richiesti eccede quelli disponibili la funzione ritorna comunque, ma
+con un numero di byte inferiore a quelli richiesti.
Lo stesso comportamento avviene caso di lettura dalla rete (cioè su un socket,
-come vedremo in \secref{sec:sock_io_behav}), o per certi dispositivi, come le
-unità a nastro, che restituiscono un singolo blocco di dati alla volta.
+come vedremo in \secref{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 \macro{EINTR} e
\macro{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 prendere è quella di rieseguire la funzione. Torneremo
-sull'argomento in \secref{sec:signal_xxx}.
-
-La seconda si verifica quando il file è in modalità non bloccante e non ci
-sono dati in ingresso: la funzione allora ritorna immediatamente con un errore
-\macro{EAGAIN}\footnote{sotto BSD questo per questo errore viene usata la
- costante \macro{EWOULDBLOCK}, in GNU/Linux questa è sinonima di
- \macro{EAGAIN}.} che nel caso indica soltanto che occorrerà provare a
-ripetere la lettura.
-
-
-Nella seconda versione delle \textit{Single Unix
+tal caso l'azione da prendere è quella di rieseguire la funzione. Torneremo in
+dettaglio sull'argomento in \secref{sec:sig_gen_beha}.
+
+La seconda si verifica quando il file è in modalità non bloccante (vedi
+\secref{sec:file_noblocking}) e non ci sono dati in ingresso: la funzione
+allora ritorna immediatamente con un errore \macro{EAGAIN}\footnote{sotto BSD
+ questo per questo errore viene usata la costante \macro{EWOULDBLOCK}, in
+ Linux, con le glibc, questa è sinonima di \macro{EAGAIN}.} che nel caso
+indica soltanto che occorrerà provare a ripetere la lettura.
+
+La funzione \func{read} è una delle system call fondamentali, esistenti fin
+dagli albori di Unix, ma nella seconda versione delle \textit{Single Unix
Specification}\footnote{questa funzione, e l'analoga \func{pwrite} sono
state aggiunte nel kernel 2.1.60, il supporto nelle \acr{glibc}, compresa
l'emulazione per i vecchi kernel che non hanno la system call, è stato
- aggiunto con la versione 2.1.} (quello che viene chiamato normalmente Unix98,
-vedi \secref{sec:intro_opengroup}) è stata introdotta la definizione di
-un'altra funzione di lettura, \func{pread}, che diventa accessibile con la
-definizione:
-\begin{verbatim}
- #define _XOPEN_SOURCE 500
-\end{verbatim}
-il prototipo di questa funzione è:
+ aggiunto con la versione 2.1, in versioni precedenti sia del kernel che
+ delle librerie la funzione non è disponibile.} (quello che viene chiamato
+normalmente Unix98, vedi \secref{sec:intro_opengroup}) è stata introdotta la
+definizione di un'altra funzione di lettura, \func{pread}, il cui prototipo è:
\begin{prototype}{unistd.h}
{ssize\_t pread(int fd, void * buf, size\_t count, off\_t offset)}
-
+
Cerca di leggere \var{count} byte dal file \var{fd}, a partire dalla posizione
\var{offset}, nel buffer \var{buf}.
in caso di errore, nel qual caso \var{errno} viene settata secondo i valori
già visti per \func{read} e \func{lseek}.}
\end{prototype}
+\noindent che però diventa accessibile solo con la definizione della macro:
+\begin{verbatim}
+ #define _XOPEN_SOURCE 500
+\end{verbatim}
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 permette di eseguire l'operazione
+\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 \secref{sec:file_sharing}). Il valore di
\var{offset} fa sempre riferimento all'inizio del file.
può fare riferimento a \figref{fig:file_dup}: l'effetto della funzione è
semplicemente quello di copiare il valore nella struttura \var{file\_struct},
cosicché anche il nuovo file descriptor fa riferimento alla stessa voce
-nella \textit{file table}.
+nella \textit{file table}; per questo si dice che il nuovo file descriptor è
+\textsl{duplicato}, da cui il nome della funzione.
\begin{figure}[htb]
\centering \includegraphics[width=13cm]{img/filedup}
\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 è:
+Si noti che per quanto illustrato in\figref{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} 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 \secref{sec:ipc_pipe_use},
+quando tratteremo le pipe). Per fare questo in genere occorre prima chiudere
+il file che si vuole sostituire, cossicché 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, \func{dup2}, che permette di specificare esplicitamente 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)}
Rende \param{newfd} una copia del file descriptor \param{oldfd}.
descriptor aperti.
\end{errlist}}
\end{prototype}
-\noindent la funzione chiude il file descriptor \param{newfd} se è aperto.
+\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.
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}.
+\secref{sec:file_fcntl}) con il parametro \macro{F\_DUPFD}.
L'operazione ha la sintassi \code{fnctl(oldfd, F\_DUPFD, newfd)} e se si usa 0
come valore per \param{newfd} diventa equivalente a \func{dup}. La sola
superiore, per cui per poterla usare come \func{dup2} occorrerebbe prima
effettuare 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 in \secref{sec:ipc_pipes} quando
-tratteremo le pipe.
-
\subsection{La funzione \func{fcntl}}
\label{sec:file_fcntl}
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).
+ nella terza sezione di \tabref{tab:file_open_flags}.\footnote{NdA 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
\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\_GETSIG}] restituisce il valore del segnale mandato quando ci
+ sono dati disponibili in input su un file descriptor aperto o settato in I/O
+ asincrono. Il valore 0 indica il valore default (che è \macro{SIGIO}), un
+ valore diverso da zero indica il segnale richiesto, (che può essere lo
+ stesso \macro{SIGIO}).
\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}.
+ effettuare I/O sul file descriptor in caso di I/O asincrono. Il valore zero
+ indica di usare il segnale di default, \macro{SIGIO}. Un altro valore
+ (compreso lo stesso \macro{SIGIO}) specifica il segnale voluto; l'uso di un
+ valore diverso da zero permette inoltre, se si è installato il manipolatore
+ del segnale come \var{sa\_sigaction} usando \macro{SA\_SIGINFO}, (vedi
+ \secref{sec:sig_sigaction}), di rendere disponibili al manipolatore
+ informazioni ulteriori informazioni riguardo il file che ha generato il
+ segnale attraverso i valori restituiti in \var{siginfo\_t} (come vedremo in
+ \secref{sec:file_asyncronous_io}).
\end{basedescript}
La maggior parte delle funzionalità di \func{fcntl} sono troppo avanzate per
\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 e il terzo parametro (usualmente di tipo
- \param{char * argp}) per il trasferimento dell'informazione necessaria.
+ \param{char * argp} o \param{int argp}) per il trasferimento
+ dell'informazione necessaria.
\bodydesc{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{ENOTTY}] il file \param{fd} non è associato con un device, o la
+ richiesta non è applicabile all'oggetto a cui fa riferimento \param{fd}.
\item[\macro{EINVAL}] gli argomenti \param{request} o \param{argp} non sono
validi.
\end{errlist}
La funzione serve in sostanza per fare tutte quelle operazioni che non si
adattano al design dell'architettura dei file e che non è possibile effettuare
-con le funzioni esaminate finora. Per questo motivo non è possibile fare altro
-che darne una descrizione generica; torneremo ad esaminarla in seguito, quando
-si tratterà di applicarla ad alcune problematiche specifiche.
+con le funzioni esaminate finora. Esse vengono selezionate attraverso il
+valore di \param{request} e gli eventuali risultati possono essere restituiti
+sia attraverso il valore di ritorno che attraverso il terzo argomento
+\param{argp}. Sono esempi delle operazioni gestite con una \func{ioctl}:
+\begin{itemize*}
+\item il cambiamento dei font di un terminale.
+\item l'esecuzione di una traccia audio di un CDROM.
+\item i comandi di avanti veloce e riavvolgimento di un nastro.
+\item il comando di espulsione di un dispositivo rimovibile.
+\item il settaggio della velocità trasmissione di una linea seriale.
+\item il settaggio della frequenza e della durata dei suoni emessi dallo
+ speaker.
+\end{itemize*}
+
+In generale ogni dispositivo ha un suo insieme di possibili diverse operazioni
+effettuabili attraverso \func{ioctl}, che sono definite nell'header file
+\file{sys/ioctl.h}, e devono essere usate solo sui dispositivi cui fanno
+riferimento. Infatti anche se in genere i valori di \param{request} sono
+opportunamente differenziati a seconda del dispositivo\footnote{il kernel usa
+ un apposito \textit{magic number} per distinguere ciascun dispositivo nella
+ definizione delle macro da usare per \param{request}, in modo da essere
+ sicuri che essi siano sempre diversi, ed il loro uso causi al più un errore.
+ Si veda il capitolo quinto di \cite{LinDevDri} per una trattazione
+ dettagliata dell'argomento.} in alcuni casi, relativi a valori assegnati
+prima che questa differenziazione diventasse pratica corrente si potrebbe
+avere
+
+Per questo motivo non è possibile fare altro che darne una descrizione
+generica; torneremo ad esaminare in seguito quelle relative ad alcuni casi
+specifici (ad esempio la gestione dei terminali è effettuata attraverso
+\func{ioctl} in quasi tutte le implementazioni di Unix), qui riportiamo solo i
+valori che sono definiti per ogni file:
+\begin{basedescript}{\desclabelwidth{2.0cm}}
+\item[\macro{FIOCLEX}] Setta il bit di \textit{close on exec}.
+\item[\macro{FIONCLEX}] Cancella il bit di \textit{close on exec}.
+\item[\macro{FIOASYNC}] Abilita l'I/O asincrono.
+\item[\macro{FIONBIO}] Abilità l'I/O in modalità non bloccante.
+\end{basedescript}
+relativi ad operazioni comunque eseguibili anche attraverso \func{fcntl}.
%%% Local Variables: