X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=fileunix.tex;h=06efedab97d2b23bb7f2115ecd3a177e28fe245e;hp=7ae7ce633e5cd39b040b2c48cbabe2d71147afde;hb=4f6d6cb9e5228bfa2341e052acbf23c3d78b2780;hpb=fadbc082a6f07263bc3bdccfbba52d1c9b6d9521 diff --git a/fileunix.tex b/fileunix.tex index 7ae7ce6..06efeda 100644 --- a/fileunix.tex +++ b/fileunix.tex @@ -297,16 +297,16 @@ cio \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} + 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 - condition} + condition, vedi \secref{sec:file_atomic}.} \footnotetext[5]{l'opzione origina da SVr4, dove però causava il ritorno da una \func{read} con un valore nullo e non con un errore, questo introduce 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} + zero da parte di \func{read} ha il significato di una end-of-file.} Il nuovo file descriptor non è condiviso con nessun altro processo, (torneremo @@ -465,9 +465,8 @@ Si tenga presente inoltre che usare \macro{SEEK\_END} non assicura affatto che 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 settata in precedenza. -Questa è una potenziale sorgente di \textit{race condition}, e quando si vuole -essere sicuri di scrivere alla fine del file questo deve essere posto in -modalità \macro{O\_APPEND}. +(questa è una potenziale sorgente di \textit{race condition}, vedi +\secref{sec:file_atomic}). Non tutti i file supportano la capacità di eseguire una \func{lseek}, in questo caso la funzione ritorna l'errore \macro{EPIPE}. Questo, oltre che per @@ -726,19 +725,77 @@ vengono toccati anche in caso di condivisione della voce della \textit{file Come si è visto in un sistema unix è sempre possibile per più processi accedere in contemporanea allo stesso file, e che le operazioni di lettura e -scrittura saranno fatte in base alla posizione corrente nel file. Ovviamente -senza prevedere opportuni meccanismi di sincronizzazione le operazioni -potranno mescolarsi in maniera imprevedibile. L'unica garanzia è che se si è -in modalità \macro{O\_APPEND} il sistema assicura che si scriva (con il -procedimento appena esposto) sempre alla fine del file. - +scrittura possono essere fatte da ogni processo in maniera autonoma in base +ad una posizione corrente nel file che è locale a ciascuno di essi. + +Se dal punto di vista della lettura dei dati questo non comporta nessun +problema, quando si andrà a scrivere le operazioni potranno mescolarsi in +maniera imprevedibile. Il sistema però fornisce in alcuni casi la possibilità +di eseguire alcune operazioni di scrittura in maniera coordinata anche senza +utilizzare meccanismi di sincronizzazione più complessi (come il \textit{file + locking}, che esamineremo in \secref{cha:file_advanced}). + +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 \secref{sec:file_lseek} settare la posizione alla fine +del file e poi scrivere può condurre ad una \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 settata con la \func{lseek} che non corrisponde più alla fine del +file, e la successiva \func{write} sovrascriverà i dati del secondo processo. + +Il problema è che usare due system call in successione non è una operazione +atomica; il problema è stato risolto introducendo la modalità +\macro{O\_APPEND}, in questo caso infatti, come abbiamo visto, è il kernel che +aggiorna automaticamente la posizione alla fine del file prima di effettuare +la scrittura, e poi estende il file. Tutto questo avviene all'interno di una +singola system call (la \func{write}) che non essendo interrompibile da un +altro processo costituisce una operazione atomica. + +Un altro caso tipico in cui è necessaria l'atomicità è quello in cui si vuole +creare un 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 da parte di un altro processo che crea lo +stesso file fra il controllo e la creazione. + +Per questo motivo sono stati introdotti i due flag \macro{O\_CREAT} e +\macro{O\_EXCL}, in questo modo l'operazione di controllo dell'esistenza del +file (con relativa uscita dalla funzione con un errore) e creazione in caso di +assenza, diventa atomica essendo svolta tutta all'interno di una singola +\func{open}. \subsection{La funzioni \func{dup} e \func{dup2}} \label{sec:file_dup} +Abbiamo già visto in \secref{sec:file_sharing} come un processo figlio +condivida gli stessi file descriptor del padre; è possibile però ottenere un +comportamento analogo all'interno di uno stesso processo \textit{duplicando} +un file descriptor. Per far questo si usa la funzione \func{dup} il cui +prototipo è: +\begin{prototype}{unistd.h}{int dup(int oldfd)} + + La funzione crea una copia del file descriptor \param{oldfd}. + + La funzione ritorna il nuovo file descriptor in caso di successo e -1 in + caso di errore, nel qual caso \var{errno} viene settata ad uno dei valori: + \begin{errlist} + \item \macro{EBADF} \param{oldfd} non è un file aperto. + \item \macro{EMFILE} si è raggiunto il numero massimo consentito di file + descriptor aperti. + \end{errlist} +\end{prototype} +La funzione ritorna, come \func{open}, il primo file descriptor libero. Il +file descriptor è una copia esatta del precedente ed entrambi possono essere +interscambiati nell'uso. Per capire meglio il funzionamento della funzione si +può fare riferimento a \figref{fig:file_dup}: l'effetto della funzione è +semplicamente quello di copiare il valore nella struttura \var{file\_struct}, +cosicché anche il nuovo file descriptor fa riferirimento alla stessa voce +nella \textit{file table}. \begin{figure}[htb] \centering \includegraphics[width=14cm]{img/filedup.eps} @@ -746,10 +803,91 @@ procedimento appena esposto) sempre alla fine del file. \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 è: +\begin{prototype}{unistd.h}{int dup2(int oldfd, int newfd)} + + La funzione rende \param{newfd} una copia del file descriptor \param{oldfd}. + + La funzione ritorna il nuovo file descriptor in caso di successo e -1 in + caso di errore, nel qual caso \var{errno} viene settata ad uno dei valori: + \begin{errlist} + \item \macro{EBADF} \param{oldfd} non è un file aperto o \param{newfd} ha un + valore fuori dall'intervallo consentito per i file descriptor. + \item \macro{EMFILE} si è raggiunto il numero massimo consentito di file + descriptor aperti. + \end{errlist} +\end{prototype} +\noindent la funzione chiude il file descriptor \param{newfd} se è aperto. + +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}. + +L'operazione ha la sintassi \func{fnctl(oldfd, F\_DUPFD, newfd)} e se si usa 0 +come valore per \param{newfd} diventa equivalente a \func{dup}. La sola +differenza, a parte i codici di errore, è che \func{dup2} chiude il nuovo file +se è già aperto mentre \func{fcntl} apre il primo disponibile con un valore +superiore, per cui per poterla usare come \func{dup2} occorrerebbe prima +effettare 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 più avanti quando tratteremo le +pipe. + \subsection{La funzione \func{fcntl}} \label{sec:file_fcntl} +Oltre alle operazioni base esaminate in \secref{sec:file_base_func} esistono +tutta una serie di operazioni ausiliarie che è possibile eseguire su un file +descriptor. Per queste operazioni di manipolazione delle varie proprietà di un +file descriptor viene usata la funzione \func{fcntl} il cui prototipo è: +\begin{functions} + \headdecl{unistd.h} + \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)} + La funzione esegue una delle possibili operazioni specificate da \param{cmd} + sul file \param{fd}. + + La funzione ha valori di ritorno diversi a seconda dell'operazione. In caso + di errore il valore di ritorno è -1 e la variabile \var{errno} viene settata + ad un opportuno codice, quelli validi in generale sono: + \begin{errlist} + \item \macro{EBADF} \param{oldfd} non è un file aperto. + \end{errlist} +\end{functions} + +Il comportamento di questa funzione è determinato dal valore del comando +\param{cmd} che le viene fornito; in \secref{sec:file_dup} abbiamo incontrato +un esempio, una lista dei possibili valori è riportata di seguito: +\begin{basedescript}{\desclabelwidth{2.0cm}} +\item[\macro{F\_DUPFD}] trova il primo file descriptor disponibile di valore + maggiore o uguale ad \param{arg} e ne fa una copia di \var{fd}. In caso di + successo ritorna il nuovo file descriptor. Gli errori possibili sono + \macro{EINVAL} se \param{arg} è negativo o maggiore del massimo consentito o + \macro{EMFILE} se il processo ha già raggiunto il massimo numero di + descrittori consentito. +\item[\macro{F\_GETFD}] ritorna il valore dei \textit{file descriptor flag} di + \var{fd}, al momento è definito solo \macro{FD\_CLOEXEC}. Prova prov proep +\item[\macro{F\_SETFD}] setta il valore dei \textit{file descriptor flag} + specificato da \param{arg}. +\end{basedescript} + \subsection{La funzione \func{ioctl}} \label{sec:file_ioctl}