X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=filestd.tex;h=e5ea8fdd48b7baf8618a18fc9f4c91fa75dfdd45;hp=b761d99661947ed6bfe991e486a54505537e37e3;hb=4200529d613212e34c8cd8afd1628aedc20d74f2;hpb=c6c424a264a13da8680542f137b665ddf5e136a6 diff --git a/filestd.tex b/filestd.tex index b761d99..e5ea8fd 100644 --- a/filestd.tex +++ b/filestd.tex @@ -86,7 +86,7 @@ file \file{stdio.h}. Ai tre file descriptor standard (vedi \secref{sec:file_std_descr}) aperti per ogni processo, corrispondono altrettanti stream, che rappresentano i canali standard di input/output prestabiliti; anche questi tre stream sono -idetificabili attraverso dei nomi simbolici definiti nell'header +identificabili attraverso dei nomi simbolici definiti nell'header \file{stdio.h} che sono: \begin{itemize} @@ -126,21 +126,21 @@ riguarda l'aspetto della scrittura dei dati sul file. I caratteri che vengono scritti su uno stream normalmente vengono accumulati in un buffer e poi trasmessi in blocco in maniera asincrona rispetto alla -scrittura (quello che viene chiamato il \textit{flush} dei dati) tutte le -volte che il buffer viene riempito. Un comportamento analogo avviene anche in -lettura (cioè dal file viene letto un blocco di dati, anche se se ne sono -richiesti una quantità inferiore), ma la cosa ovviamente ha rilevanza -inferiore, dato che i dati letti sono sempre gli stessi; in caso di scrittura -invece, quando si ha un accesso contemporaneo allo stesso file (ad esempio da -parte di un altro processo) si potranno vedere solo le parti effettivamente -scritte, e non quelle ancora presenti nel buffer. +scrittura (quello che viene chiamato lo \textsl{scarico}, dall'ingelese +\textit{flush}, dei dati) tutte le volte che il buffer viene riempito. Un +comportamento analogo avviene anche in lettura (cioè dal file viene letto un +blocco di dati, anche se se ne sono richiesti una quantità inferiore), ma la +cosa ovviamente ha rilevanza inferiore, dato che i dati letti sono sempre gli +stessi; in caso di scrittura invece, quando si ha un accesso contemporaneo +allo stesso file (ad esempio da parte di un altro processo) si potranno vedere +solo le parti effettivamente scritte, e non quelle ancora presenti nel buffer. Allo stesso modo, se si sta facendo dell'input/output interattivo bisognerà -tenere presente le caratteristiche delle operazioni di \textit{flush} dei -dati, poiché non è detto che ad una scrittura sullo stream corrisponda una -immediata scrittura sul dispositivo. +tenere presente le caratteristiche delle operazioni di scarico dei dati, +poiché non è detto che ad una scrittura sullo stream corrisponda una immediata +scrittura sul dispositivo. -Per risondere ad esigenze diverse, lo standard definisce tre distinte modalità +Per rispondere ad esigenze diverse, lo standard definisce tre distinte modalità in cui può essere eseguita la bufferizzazione, delle quali occorre essere ben consapevoli, specie in caso di lettura e scrittura da dispositivi interattivi: \begin{itemize} @@ -177,18 +177,18 @@ chiarimenti e attenzioni per quel che concerne il suo funzionamento. Come gi accennato nella descrizione, \emph{di norma} i dati vengono inviati al kernel alla ricezione di un carattere di a capo; questo non è vero in tutti i casi, infatti, dato che le dimensioni del buffer usato dalle librerie sono fisse, se -le si eccedono si può avere un \textit{flush} dei dati anche prima che sia -stato inviato un carattere di \textit{newline}. +le si eccedono si può avere uno scarico dei dati anche prima che sia stato +inviato un carattere di \textit{newline}. Un secondo punto da tenere presente, particolarmente quando si ha a che fare con I/O interattivo, è che quando si effettua una lettura su uno stream che comporta l'accesso al kernel\footnote{questo vuol dire sempre se lo stream da - cui si legge è in modalità \textit{unbuffered}} viene anche eseguito il -\textit{flush} di tutti i buffer degli stream in scrittura. + cui si legge è in modalità \textit{unbuffered}} viene anche eseguito lo +scarico di tutti i buffer degli stream in scrittura. In \secref{sec:file_buffering_ctrl} vedremo come la libreria definisca delle -opportune funzioni per controllare le modalità di bufferizzazione ed il -\textit{flush} dei dati. +opportune funzioni per controllare le modalità di bufferizzazione e lo scarico +dei dati. @@ -200,9 +200,134 @@ analoghe a quelle di \secref{sec:file_base_func} per i file descriptor. In particolare vedremo come aprire, leggere, scrivere e cambiare la posizione corrente in uno stream. + \subsection{Apertura e chiusura di uno stream} \label{sec:file_fopen} +Le funzioni che permettono di aprire uno stream sono tre\footnote{\func{fopen} + e \func{freopen} fanno parte dello standard ANSI C, \func{fdopen} è parte +dello standard POSIX.1.}, i loro prototipi sono: + +\begin{functions} + \headdecl{stdio.h} + \funcdecl{FILE * fopen(const char * path, const char *mode)} + Apre il file specificato da \param{path}. + \funcdecl{FILE * fdopen(int fildes, const char * mode)} + Associa uno stream al file descriptor \param{fildes}. + \funcdecl{FILE * freopen(const char * path, const char * mode, FILE * + stream)} + Apre il file specificato da \param{path} associandolo allo stream + specificato da \param{stream}, se questo è già aperto prima lo chiude. + + Le funzioni ritornano un puntatore valido in caso di successo e \macro{NULL} + in caso di errore, in tal caso \var{errno} viene settata al valore ricevuto + dalla funzione sottostante di cui è fallita l'esecuzione. + + Gli errori pertanto possono essere quelli di \func{malloc} per tutte e tre + le funzioni, quelli \func{open} per \func{fopen}, quelli di \func{fcntl} per + \func{fdopen} e quelli di \func{fopen}, \func{fclose} e \func{fflush} per + \func{freopen}. +\end{functions} + +Normalmente la funzione che si usa per aprire uno stream è \func{fopen}, essa +apre il file specificato nella modalità specificata da \param{mode} che è una +delle stringhe elencate in \tabref{tab:file_fopen_mode}. + +L'uso di \func{freopen} è in genere per redirigere uno dei tre file standard +(vedi \secref{sec:file_std_stream}): il file \param{path} viene associato a +\param{stream} e se questo è uno stream aperto prima viene chiuso. + +Infine \func{fdopen} viene usato per associare uno stream ad un file +descriptor esistente ottenuto tramite una altra funzione (come \func{open}, +\func{dup}, \func{pipe}) e serve quando si vogliono usare gli stream con file +speciali come le fifo od i socket, che non possono essere aperti con le +funzioni delle librerie standard del C. + +\begin{table}[htb] + \centering + \begin{tabular}[c]{|l|p{8cm}|} + \hline + \textbf{Valore} & \textbf{Significato}\\ + \hline + \hline + \texttt{r} & Il file viene aperto in sola lettura; lo stream è posizionato + all'inizio del file.\\ + \texttt{r+} & Il file viene aperto in lettura e scrittura; lo stream è + posizionato all'inizio del file. \\ +% \hline + \texttt{w} & Il file viene troncato a lunghezza nulla (o creato se non + esiste), ed aperto in sola lettura; lo stream è posizionato all'inizio del + file.\\ + \texttt{w+} & Il file viene troncato a lunghezza nulla (o creato se non + esiste), ed aperto in scrittura e lettura; lo stream è posizionato + all'inizio del file.\\ +% \hline + \texttt{a} & Il file è aperto in \textit{append mode} in sola scrittura + (o creato se non esiste). \\ + \texttt{a+} & Il file è aperto in \textit{append mode} in lettura e + scrittura (o creato se non esiste). \\ + \hline + \end{tabular} + \caption{Modalità di apertura di uno stream} + \label{tab:file_fopen_mode} +\end{table} + +In realtà lo standard ANSI C prevede un totale di 15 possibili valori diversi +per \param{mode}, ma in \tabref{tab:file_fopen_mode} si sono riportati solo i +sei valori effettivi, ad essi può essere aggiunto pure il carattere \func{b} +(come ultimo carattere o nel mezzo agli altri per le stringhe di due +caratteri) che in altri sistemi operativi serve a distinguere i file binari +dai file di testo; in un sistema POSIX questa distinzione non esiste e il +valore viene accettato solo per compatibilità, ma non ha alcun effetto. + +Inoltre nel caso si usi \func{fdopen} i valori specificati da \param{mode} +devono essere compatibili con quelli con cui il file descriptor è stato +aperto. Inoltre i modi \cmd{w} e \cmd{w+} non troncano il file. La posizione +nello stream viene settata a quella corrente nel file descriptor, e le +variabili di errore e di fine del file sono cancellate. Il file non viene +duplicato e verrà chiuso alla chiusura dello stream. + +I nuovi file saranno creati secondo quanto visto in +\secref{sec:file_ownership} ed avranno i permessi di accesso settati al valore +\macro{S\_IRUSR|S\_IWUSR|S\_IRGRP|S\_IWGRP|S\_IROTH|S\_IWOTH} (pari a +\macro{0666}) modificato secondo il valore di \acr{umask} per il processo (si +veda \secref{sec:file_umask}). + +In caso di file aperti in lettura e scrittura occorre ricordarsi che c'è di +messo una bufferizzazione; per questo motivo lo standard ANSI C richiede che +ci sia una operazione di posizionamento fra una operazione di output ed una di +input o viceversa (eccetto il caso in cui l'input ha incontrato la fine del +file), altrimenti una lettura può ritornare anche il risultato di scritture +precedenti l'ultima effettuata. + +Per questo motivo è una buona pratica (e talvolta necessario) far seguire ad +una scrittura una delle funzioni \func{fflush}, \func{fseek}, \func{fsetpos} o +\func{rewind} prima di eseguire una rilettura; viceversa nel caso in cui si +voglia fare una scrittura subito dopo aver eseguito una lettura occorre prima +usare una delle funzioni \func{fseek}, \func{fsetpos} o \func{rewind}. Anche +una operazione nominalmente nulla come \func{fseek(file, 0, SEEK\_CUR)} è +sufficiente a garantire la sincronizzazione. + +Una volta aperto lo stream, si può cambiare la modalità di bufferizzazione +fintanto che non si è effettuat + + +Uno stream viene chiuso con la funzione \func{fclose} il cui prototipo è: +\begin{prototype}{stdio.h}{int fclose(FILE * stream)} + Chiude lo stream \param{stream}. + + Restituisce 0 in caso di successo e \macro{EOF} in caso di errore, nel qual + caso setta \var{errno} a \macro{EBADF} se il file descriptor indicato da + \param{stream} non è valido, o uno dei valori specificati dalla sottostante + funzione che è fallita (\func{close}, \func{write} o \func{fflush}). +\end{prototype} + +La funzione effettua uno scarico di tutti i dati presenti nei buffer di uscita +e scarta tutti i dati in ingresso, se era stato allocato un buffer per lo +stream questo verrà rilasciato. La funzione effettua lo scarico solo per i +dati presenti nei buffer in user space usati dalle \acr{glibc}; se si essere +sicuri che il kernel forzi la scrittura su disco occorrà effettuare . + \subsection{Lettura e scrittura su uno stream} \label{sec:file_io} @@ -224,10 +349,6 @@ corrente in uno stream. \label{sec:file_formatted_io} -\subsection{Chiusura di uno stream} -\label{sec:file_fclose} - - \section{Funzioni avanzate} \label{sec:file_stream_adv_func} @@ -251,11 +372,3 @@ stream; se non si \subsection{Efficienza} \label{sec:file_stream_efficiency} - - - - - - - -