X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=fileunix.tex;h=95d27afda8f56b4f02831a705daf1b7a017634da;hp=aa851175ee59559893424114d62c7ed4e935c079;hb=5638dceb842da9dff14b0c08bcc0e4e65fb79c44;hpb=a93a0bac951085a52c4b33c42a607fc81f6868d2 diff --git a/fileunix.tex b/fileunix.tex index aa85117..95d27af 100644 --- a/fileunix.tex +++ b/fileunix.tex @@ -1,55 +1,331 @@ -\chapter{I files: l'interfaccia I/O di unix} +\chapter{L'interfaccia unix di I/O con i file} \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. -\section{I file descriptors} -\label{sec:fileunix_fd} + +\section{L'architettura di base} +\label{sec:file_base_arch} + +In questa sezione faremo una breve introduzione sulla architettura su cui è +basata dell'interfaccia dei \textit{file descriptor}, che, sia pure con +differenze di implementazione, è comune ad ogni implementazione di unix. +Vedremo cosa comporti questa architettura in caso di accesso contemporaneo ai +file da parte di più processi. + + +\subsection{L'architettura dei \textit{file descriptors}} +\label{sec:file_fd} + +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 \secref{sec:file_vfs_work}). Questo si fa aprendo il file con +la funzione \func{open} che provvederà a localizzare l'inode del file e +inizializzare le funzioni che il VFS mette a disposizione (riportate in +\tabref{tab:file_file_operations}). Una volta terminate le operazioni, il file +dovrà essere chiuso, e questo chiuderà il canale di comunicazione impedendo +ogni ulteriore operazione. + +All'interno di ogni processo i file aperti sono identificati da un intero non +negativo, chiamato appunto \textit{file descriptors}, quando un file viene +aperto la funzione restituisce il file descriptor, e tutte le successive +operazioni devono passare il \textit{file descriptors} come argomento. + +Per capire come funziona il meccanismo occorre spiegare a grandi linee come è +che il kernel gestisce l'interazione fra processi e file. Il kernel mantiene +sempre un elenco dei processi attivi nella cosiddetta \textit{process table} +ed un elenco dei file aperti nella \textit{file table}. + +La \textit{process table} è una tabella che contiene una voce per ciascun +processo attivo nel sistema. In Linux la tabella è costituita da strutture di +tipo \var{task\_struct} nelle quali sono raccolte tutte le informazioni +relative ad un singolo processo; fra queste informazioni c'è anche il +puntatore ad una ulteriore struttura di tipo \var{files\_struct} in cui sono +contenute le informazioni relative ai file che il processo ha aperto, ed in +particolare: +\begin{itemize*} +\item i flag relativi ai file descriptor. +\item il numero di file aperti. +\item una tabella che contiene un puntatore alla relativa voce nella + \textit{file table} per ogni file aperto. +\end{itemize*} +il \textit{file descriptor} in sostanza è l'intero positivo che indicizza +questa tabella. + +La \textit{file table} è una tabella che contiene una voce per ciascun file +che è stato aperto nel sistema. In Linux è costituita da strutture di tipo +\var{file}; in ciascuna di esse sono tenute varie informazioni relative al +file, fra cui: +\begin{itemize*} +\item lo stato del file (nel campo \var{f\_flags}). +\item il valore della posizione corrente (l'\textit{offset}) nel file. +\item un puntatore all'inode\footnote{nel kernel 2.4.x si è in realtà passati + ad un puntatore ad una struttura \var{dentry} che punta a sua volta + all'inode passando per la nuova struttura del VFS} del file. +%\item un puntatore alla tabella delle funzioni \footnote{la struttura +% \var{f\_op} descritta in \secref{sec:file_vfs_work}} che si possono usare +% sul file. +\end{itemize*} + +In \figref{fig:file_proc_file} si è riportato uno schema in cui è illustrata +questa architettura, in cui si sono evidenziate le interrelazioni fra le varie +strutture di dati sulla quale essa è basata. +\begin{figure}[htb] + \centering + \includegraphics[width=14cm]{img/procfile.eps} + \caption{Schema della architettura dell'accesso ai file attraverso + l'interfaccia dei \textit{file descroptor}} + \label{fig:file_proc_file} +\end{figure} +Ritorneremo su questo schema più volte, dato che esso è fondamentale per +capire i dettagli del funzionamento delle dell'interfaccia dei \textit{file + descriptor}. + + +\subsection{I file standard} +\label{sec:file_std_descr} + +Come accennato i \textit{file descriptor} non sono altro che un indice nella +tabella dei file aperti di ciascun processo; per questo motivo essi vengono +assegnati in successione tutte le volte che si apre un nuovo file (se non se +ne è 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 +dicevamo prima, avranno come \textit{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 a quello che viene chiamato \textit{standard + input}, è cioè il file da cui il processo si aspetta di ricevere i dati in +ingresso (nel caso della shell, è associato alla lettura della tastiera); il +secondo file è il cosiddetto \textit{standard output}, cioè il file su cui ci +si aspetta debbano essere inviati i dati in uscita (sempre nel caso della +shell, è il terminale su cui si sta scrivendo), il terzo è lo \textit{standard + error}, su cui viene inviato l'output relativo agli errori. +Lo standard POSIX.1 provvede tre costanti simboliche, definite nell'header +\file{unistd.h}, al posto di questi valori numerici: +\begin{table}[htb] + \centering + \footnotesize + \begin{tabular}[c]{|l|l|} + \hline + \textbf{Costante} & \textbf{Significato} \\ + \hline + \hline + \macro{STDIN\_FILENO} & \textit{file descriptor} dello \textit{standard + input} \\ + \macro{STDOUT\_FILENO} & \textit{file descriptor} dello \textit{standard + output} \\ + \macro{STDERR\_FILENO} & \textit{file descriptor} dello \textit{standard + error}\\ + \hline + \end{tabular} + \caption{Costanti definite in \file{unistd.h} per i file standard aperti + alla creazione di ogni processo.} + \label{tab:file_std_files} +\end{table} + +In \curfig\ si è utilizzata questa situazione come esempio, facendo +riferimento ad un programma in cui lo \textit{standard input} è associato ad +un file mentre lo \textit{standard output} e lo \textit{standard error} sono +entrambi associati ad un altro file (e quindi utilizzano lo stesso inode). + +Nelle vecchie versioni di unix (ed anche in Linux fino al kernel 2.0.x) il +numero di file aperti era anche soggetto ad un limite massimo dato dalle +dimensioni del vettore di puntatori con cui era realizzata la tabella dei file +descriptor dentro \var{file\_struct}; questo limite intrinseco non sussiste +più, dato che si è passati ad una linked list, restano i limiti imposti +dall'amministratore (vedi \secref{sec:sys_limits}). + \section{Le funzioni base} -\label{sec:fileunix_base_func} +\label{sec:file_base_func} + +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. + + +\subsection{La funzione \func{open}} +\label{sec:file_open} + +La funzione \func{open} è la funzione fondamentale per accedere ai file, ed è +quella che crea l'associazione fra un pathname ed un file descriptor; il suo +prototipo è: + +\begin{functions} + \headdecl{sys/types.h} + \headdecl{sys/stat.h} + \headdecl{fcntl.h} + \funcdecl{int open(const char *pathname, int flags)} + \funcdecl{int open(const char *pathname, int flags, mode\_t mode)} + Apre il file indicato da \var{pathname} nella modalità indicata da + \var{flags}, e, nel caso il file sia creato, con gli eventuali permessi + specificati da \var{mode}. + + La funzione ritorna il file descriptor in caso di successo e -1 in caso di + errore. In questo caso la variabile \var{errno} viene settata ad uno dei + valori: + + \begin{errlist} + \item \macro{EEXIST} \var{pathname} esiste e si è specificato + \macro{O\_CREAT} e \macro{O\_EXCL}. + \item \macro{EISDIR} \var{pathname} indica una directory e si è tentato + l'accesso in scrittura. + \item \macro{ENOTDIR} si è specificato \macro{O\_DIRECTORY} e \var{pathname} + non è una directory. + \item \macro{ENXIO} si sono settati \macro{O\_NOBLOCK} o \macro{O\_WRONLY} + ed il file è una fifo che non viene letta da nessun processo o + \var{pathname} è un file di dispositivo ma il dispositivo è assente. + \item \macro{ENODEV} \var{pathname} si riferisce a un file di dispositivo + che non esiste. + \item \macro{ETXTBSY} si è cercato di accedere in scrittura all'immagine di + un programma in esecuzione. + \item \macro{ELOOP} si sono incotrati troppi link simbolici nel risolvere + pathname o si è indicato \macro{O\_NOFOLLOW} e \var{pathname} è un link + simbolico. + \end{errlist} + ed inoltre \macro{EACCES}, \macro{ENAMETOOLONG}, \macro{ENOENT}, + \macro{EROFS}, \macro{EFAULT}, \macro{ENOSPC}, \macro{ENOMEM}, + \macro{EMFILE} e \macro{ENFILE}. +\end{functions} -L'interfaccia standard unix per l'input/output sui file è su cinque funzioni -\texttt{open}, \texttt{read}, \texttt{write}, \texttt{lseek}, \texttt{close} +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 caratteritica +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). +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 +restare aperto attraverso una \func{exec} (come accennato in +\secref{sec:proc_exec}) ed l'offset è settato all'inizio del file. -\subsection{La funzione \texttt{open}} -\label{sec:fileunix_open} +La funzione prevede diverse modalità di apertura di un file, che vengono +specificate dal parametro \var{flags}, questo parametro viene settato come +maschera binaria attraverso OR aritmetico delle costanti che identificano i +vari flag. Questi ultimi sono poi divisibili in tre categorie principali: +\begin{itemize} +\item \textsl{le modalità di accesso}, che specificano con quale modalità si + accede al file: lettura, scrittura o lettura/scrittura. Uno di questi + valori deve essere sempre specificato, vengono settati alla chiamata da + \func{open}, e possono essere riletti con una \func{fcntl}, ma non + modificati. +\item \textsl{le modalità di apertura}, che permettono di specificare alcuni + dei modi di funzionamento di \func{open}. Hanno effetto solo al momento + della chiamata e non sono memorizzati nè possono essere riletti. +\item \textsl{le modalità di operazione}, che permettono di specificare alcuni + effetti del comportamento delle operazioni sul file (come la \func{read} o + la \func{write}). Sono settati alla chiamata da \func{open}, ma possono + essere riletti e modificati con una una \func{fcntl}. +\end{itemize} -\subsection{La funzione \texttt{creat}} -\label{sec:fileunix_creat} +In \ntab\ si sono riportate, come definite in \file{fcntl.h}, le costanti che +identificano i vari flag di stato (ed i relativi valori numerici), da usare +per specificare il valore di \var{flags}, alcuni di questi poi andranno a +costituire lo stato del file (il cosiddetto \textit{file status flag}), tenuto +nel campo \var{f\_flags} di \var{file}. -\subsection{La funzione \texttt{close}} -\label{sec:fileunix_close} +\begin{table}[htb] + \centering + \begin{tabular}[c]{|l|p{10cm}|} + \hline + \textbf{Flag} & \textbf{Descrizione} \\ + \hline + \hline % modailtà di accesso + \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\\ + \hline % modalita di apertura + \macro{O_CREAT} & \\ + \macro{O_EXCL} & \\ + \macro{O_NOCTTY} & \\ + \macro{O_NONBLOCK} & \\ + \macro{O_SHLOCK} & \\ + \macro{O_EXLOCK} & \\ + \macro{O_TRUNC} & \\ + \macro{O_NOFOLLOW} & \\ + \macro{O_DIRECTORY} & \\ + \macro{O_LARGEFILE} & \\ + \hline % modalità di operazione + \macro{O_APPEND} & \\ + \macro{O_NONBLOCK} & \\ + \macro{O_NDELAY} & sinonimo di \macro{O_NONBLOCK}\\ + \macro{O_ASYNC} & \\ + \macro{O_FSYNC} & \\ + \macro{O_SYNC} & \\ + \macro{O_NOATIME} & \\ + \hline + \end{tabular} + \caption{Costanti che identificano i vari flag di stato del file specificati + alla sua aprertura tramite il parametro \var{flags} di \func{open}.} + \label{tab:file_open_flags} +\end{table} -\subsection{La funzione \texttt{lseek}} -\label{sec:fileunix_lseek} -\subsection{La funzione \texttt{read}} -\label{sec:fileunix_read} +\subsection{La funzione \func{creat}} +\label{sec:file_creat} -\subsection{La funzione \texttt{write}} -\label{sec:fileunix_write} +\subsection{La funzione \func{close}} +\label{sec:file_close} -\section{La condivisione dei files} -\label{sec:fileunix_sharing} +\subsection{La funzione \func{lseek}} +\label{sec:file_lseek} +\subsection{La funzione \func{read}} +\label{sec:file_read} + +\subsection{La funzione \func{write}} +\label{sec:file_write} -\subsection{Operazioni atomiche} -\label{sec:fileunix_atomic} \section{Funzioni avanzate} -\label{sec:fileunix_adv_func} +\label{sec:file_adv_func} + +\subsection{La condivisione dei files} +\label{sec:file_sharing} + + +Si noti che i flag di stato del file, quelli settati dal parametro \var{flag} +di \func{open}, essendo tenuti nella vode sulla file table, vengono condivisi, +ai file sono però associati anche altri flag, (tenuti invece nella struttura +\var{file\_struct} interna alla process table) che sono unici per ciascun file +descriptor, e sono pertanto detti \textit{file descriptor flags} (l'unico +usato al momento è \macro{FD\_CLOEXEC}). + + +\subsection{Operazioni atomiche coi file} +\label{sec:file_atomic} + + +\subsection{La funzioni \func{dup} e \func{dup2}} +\label{sec:file_dup} -\subsection{La funzioni \texttt{dup} e \texttt{dup2}} -\label{sec:fileunix_dup} +\subsection{La funzione \func{fcntl}} +\label{sec:file_fcntl} -\subsection{La funzione \texttt{fcntl}} -\label{sec:fileunix_fcntl} +\subsection{La funzione \func{ioctl}} +\label{sec:file_ioctl} -\subsection{La funzione \texttt{fcntl}} -\label{sec:fileunix_ioctl}