X-Git-Url: https://gapil.gnulinux.it/gitweb/?a=blobdiff_plain;ds=sidebyside;f=fileio.tex;h=9e7e960569f0ebc65beb52717d5c8601a10daade;hb=7b43a7843d483c826a6ed13224208c615a23c4d6;hp=77769ece37748d09c809388d4df7af03f8be0e41;hpb=88d22f4971adcbdb816c405a1375ae0a8d57bdde;p=gapil.git diff --git a/fileio.tex b/fileio.tex index 77769ec..9e7e960 100644 --- a/fileio.tex +++ b/fileio.tex @@ -64,32 +64,34 @@ file viene aperto la funzione \func{open} restituisce questo numero, tutte le ulteriori operazioni dovranno essere compiute specificando questo stesso numero come argomento alle varie funzioni dell'interfaccia. +\itindbeg{process~table} +\itindbeg{file~table} + Per capire come funziona il meccanismo occorre spiegare a grandi linee come il kernel gestisce l'interazione fra processi e file. Abbiamo già accennato in sez.~\ref{sec:proc_hierarchy} come il kernel mantenga un elenco di tutti -processi nella cosiddetta \itindex{process~table} \textit{process table}. Lo -stesso, come accennato in sez.~\ref{sec:file_vfs_work}, vale anche per tutti i -file aperti, il cui elenco viene mantenuto nella cosiddetta -\itindex{file~table} \textit{file table}. - -La \itindex{process~table} \textit{process table} è una tabella che contiene -una voce per ciascun processo attivo nel sistema. Ciascuna voce è costituita -dal puntatore a una struttura di tipo \kstruct{task\_struct} nella quale sono -raccolte tutte le informazioni relative al processo, fra queste informazioni -c'è anche il puntatore ad una ulteriore struttura di tipo +processi nella cosiddetta \textit{process table}. Lo stesso, come accennato in +sez.~\ref{sec:file_vfs_work}, vale anche per tutti i file aperti, il cui +elenco viene mantenuto nella cosiddetta \textit{file table}. + +La \textit{process table} è una tabella che contiene una voce per ciascun +processo attivo nel sistema. Ciascuna voce è costituita dal puntatore a una +struttura di tipo \kstruct{task\_struct} nella quale sono raccolte tutte le +informazioni relative al processo, fra queste informazioni c'è anche il +puntatore ad una ulteriore struttura di tipo \kstruct{files\_struct},\footnote{la definizione corrente di questa struttura si trova nel file \texttt{include/linux/fdtable.h} dei sorgenti del kernel, quella mostrata in fig.~\ref{fig:file_proc_file} è una versione pesantemente semplificata.} che contiene le informazioni relative ai file che il processo ha aperto. -La \itindex{file~table} \textit{file table} è una tabella che contiene una -voce per ciascun file che è stato aperto nel sistema. Come accennato in -sez.~\ref{sec:file_vfs_work} per ogni file aperto viene allocata una struttura -\kstruct{file} e la \textit{file table} è costituita da un elenco di puntatori -a ciascuna di queste strutture, che, come illustrato in -fig.~\ref{fig:kstruct_file}, contengono le informazioni necessarie per la -gestione dei file, ed in particolare: +La \textit{file table} è una tabella che contiene una voce per ciascun file +che è stato aperto nel sistema. Come accennato in sez.~\ref{sec:file_vfs_work} +per ogni file aperto viene allocata una struttura \kstruct{file} e la +\textit{file table} è costituita da un elenco di puntatori a ciascuna di +queste strutture, che, come illustrato in fig.~\ref{fig:kstruct_file}, +contengono le informazioni necessarie per la gestione dei file, ed in +particolare: \begin{itemize*} \item i flag di stato \itindex{file~status~flag} del file nel campo \var{f\_flags}. @@ -115,9 +117,9 @@ gestione dei file, ed in particolare: In fig.~\ref{fig:file_proc_file} si è riportato uno schema semplificato in cui è illustrata questa architettura, ed in cui si sono evidenziate le -interrelazioni fra la \itindex{file~table} \textit{file table}, la -\itindex{process~table} \textit{process table} e le varie strutture di dati -che il kernel mantiene per ciascun file e ciascun processo. +interrelazioni fra la \textit{file table}, la \textit{process table} e le +varie strutture di dati che il kernel mantiene per ciascun file e ciascun +processo. Come si può notare alla fine il collegamento che consente di porre in relazione i file ed i processi è effettuato attraverso i dati mantenuti nella @@ -134,11 +136,14 @@ essenziali come: In questa infrastruttura un \textit{file descriptor} non è altro che l'intero positivo che indicizza quest'ultima tabella, e che consente di recuperare il puntatore alla struttura \kstruct{file} corrispondente al file aperto dal -processo a cui era stato assegnato questo indice. Una volta ottenuta grazie -al \textit{file descriptor} la struttura \kstruct{file} corrispondente al file -voluto nella \itindex{file~table} \textit{file table}, il kernel potrà usare -le funzioni messe disposizione dal VFS per eseguire sul file tutte le -operazioni necessarie. +processo a cui era stato assegnato questo indice. Una volta ottenuta grazie al +\textit{file descriptor} la struttura \kstruct{file} corrispondente al file +voluto nella \textit{file table}, il kernel potrà usare le funzioni messe +disposizione dal VFS per eseguire sul file tutte le operazioni necessarie. + +\itindend{process~table} +\itindend{file~table} + Il meccanismo dell'apertura dei file prevede che venga sempre fornito il primo \textit{file descriptor} libero nella tabella, e per questo motivo essi @@ -467,11 +472,11 @@ sez.~\ref{sec:ipc_file_lock}). Si tenga presente che questa opzione è supportata su NFS solo a partire da NFSv3 e con il kernel 2.6, nelle versioni precedenti la funzionalità viene emulata controllando prima l'esistenza del file per cui usarla per creare \index{file!di lock} un file di lock potrebbe -dar luogo a una \itindex{race~condition} \textit{race condition}.\footnote{un - file potrebbe venir creato fra il controllo la successiva apertura con - \const{O\_CREAT}, la cosa si può risolvere comunque creando un file con un - nome univoco ed usando la funzione \func{link} per creare il \index{file!di - lock} file di lock, (vedi sez.~\ref{sec:ipc_file_lock}).} +dar luogo a una \textit{race condition}.\footnote{un file potrebbe venir + creato fra il controllo la successiva apertura con \const{O\_CREAT}, la cosa + si può risolvere comunque creando un file con un nome univoco ed usando la + funzione \func{link} per creare il \index{file!di lock} file di lock, (vedi + sez.~\ref{sec:ipc_file_lock}).} Se si usa \const{O\_EXCL} senza \const{O\_CREAT} il comportamento è indefinito. Nella creazione di un file con \const{O\_CREAT} occorre sempre @@ -500,9 +505,9 @@ si tronca il file con \const{O\_TRUNC} verranno impostati soltanto il viene sempre aggiunto al contenuto precedente. Con NFS questa funzionalità non è supportata e viene emulata, per questo possono verificarsi - \itindex{race~condition} \textit{race - condition} con una sovrapposizione dei dati se - più di un processo scrive allo stesso tempo.\\ + \textit{race condition} con una sovrapposizione dei + dati se più di un processo scrive allo stesso + tempo.\\ \const{O\_ASYNC} & Apre il file per l'I/O in modalità asincrona (vedi sez.~\ref{sec:signal_driven_io}). Quando è impostato viene generato il segnale \signal{SIGIO} @@ -518,11 +523,11 @@ si tronca il file con \const{O\_TRUNC} verranno impostati soltanto il sez.~\ref{sec:proc_exec}) sul file. Il flag è previsto dallo standard POSIX.1-2008, ed è stato introdotto con il kernel 2.6.23 per evitare una - \itindex{race~condition} \textit{race condition} - che si potrebbe verificare con i \textit{thread} - fra l'apertura del file e l'impostazione della - suddetta modalità con \func{fcntl} (vedi - sez.~\ref{sec:file_fcntl_ioctl}).\\ + \textit{race condition} che si potrebbe verificare + con i \textit{thread} fra l'apertura del file e + l'impostazione della suddetta modalità con + \func{fcntl} (vedi + sez.~\ref{sec:file_fcntl_ioctl}).\\ \const{O\_DIRECT} & Esegue l'I/O direttamente dalla memoria in \textit{user space} in maniera sincrona, in modo da scavalcare i meccanismi di bufferizzazione del @@ -1236,13 +1241,12 @@ meccanismi di sincronizzazione espliciti come il \itindex{file~locking} 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 \itindex{race~condition} -\textit{race condition}l 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 primo processo, che avrà la -posizione corrente che aveva impostato con la \func{lseek} che non corrisponde -più alla fine del file, e la sua successiva \func{write} sovrascriverà i dati -del secondo processo. +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 primo processo, avrà una posizione corrente che aveva +impostato con la \func{lseek} che non corrisponde più alla fine del file, e la +sua successiva \func{write} sovrascriverà i dati del secondo processo. Il problema deriva dal fatto che usare due \textit{system call} in successione non è mai un'operazione atomica dato che il kernel può interrompere @@ -1350,8 +1354,8 @@ file descriptor che si vuole ottenere come duplicato; il suo prototipo è: \begin{errlist} \item[\errcode{EBADF}] \param{oldfd} non è un file aperto o \param{newfd} ha un valore fuori dall'intervallo consentito per i file descriptor. - \item[\errcode{EBUSY}] si è rilevata la possibilità di una - \itindex{race~condition} \textit{race condition}. + \item[\errcode{EBUSY}] si è rilevata la possibilità di una \textit{race + condition}. \item[\errcode{EINTR}] la funzione è stata interrotta da un segnale. \item[\errcode{EMFILE}] si è raggiunto il numero massimo consentito di file descriptor aperti. @@ -1369,24 +1373,23 @@ e si limita a restituire \param{newfd}. L'uso di \func{dup2} ha vari vantaggi rispetto alla combinazione di \func{close} e \func{dup}; anzitutto se \param{oldfd} è uguale \param{newfd} questo verrebbe chiuso e \func{dup} fallirebbe, ma soprattutto l'operazione è -atomica e consente di evitare una \itindex{race~condition} \textit{race - condition} in cui dopo la chiusura del file si potrebbe avere la ricezione -di un segnale il cui gestore (vedi sez.~\ref{sec:sig_signal_handler}) potrebbe -a sua volta aprire un file, per cui alla fine \func{dup} restituirebbe un file -descriptor diverso da quello voluto. +atomica e consente di evitare una \textit{race condition} in cui dopo la +chiusura del file si potrebbe avere la ricezione di un segnale il cui gestore +(vedi sez.~\ref{sec:sig_signal_handler}) potrebbe a sua volta aprire un file, +per cui alla fine \func{dup} restituirebbe un file descriptor diverso da +quello voluto. Con Linux inoltre la funzione prevede la possibilità di restituire l'errore \errcode{EBUSY}, che non è previsto dallo standard, quando viene rilevata la -possibilità di una \itindex{race~condition} \textit{race condition} interna in -cui si cerca di duplicare un file descriptor che è stato allocato ma per il -quale non sono state completate le operazioni di apertura.\footnote{la - condizione è abbastanza peculiare e non attinente al tipo di utilizzo - indicato, quanto piuttosto ad un eventuale tentativo di duplicare file - descriptor non ancora aperti, la condizione di errore non è prevista dallo - standard, ma in condizioni simili FreeBSD risponde con un errore di - \errval{EBADF}, mentre OpenBSD elimina la possibilità di una \textit{race - condition} al costo di una perdita di prestazioni.} In tal caso occorre -ritentare l'operazione. +possibilità di una \textit{race condition} interna in cui si cerca di +duplicare un file descriptor che è stato allocato ma per il quale non sono +state completate le operazioni di apertura.\footnote{la condizione è + abbastanza peculiare e non attinente al tipo di utilizzo indicato, quanto + piuttosto ad un eventuale tentativo di duplicare file descriptor non ancora + aperti, la condizione di errore non è prevista dallo standard, ma in + condizioni simili FreeBSD risponde con un errore di \errval{EBADF}, mentre + OpenBSD elimina la possibilità di una \textit{race condition} al costo di + una perdita di prestazioni.} In tal caso occorre ritentare l'operazione. La duplicazione dei file descriptor può essere effettuata anche usando la funzione di controllo dei file \func{fcntl} (che esamineremo in @@ -1562,10 +1565,9 @@ come per le altre funzioni che prendono come argomenti dei quando un \textit{pathname} relativo non fa riferimento ad un file posto direttamente nella directory di lavoro corrente, che alcuni dei componenti del \textit{pathname} vengano modificati in parallelo alla chiamata a \func{open}, -cosa che lascia aperta la possibilità di una \itindex{race~condition} -\textit{race condition} in cui c'è spazio per un \itindex{symlink~attack} -\textit{symlink attack} (si ricordi quanto visto per \func{access} in -sez.~\ref{sec:file_perm_management}). +cosa che lascia aperta la possibilità di una \textit{race condition} in cui +c'è spazio per un \itindex{symlink~attack} \textit{symlink attack} (si ricordi +quanto visto per \func{access} in sez.~\ref{sec:file_perm_management}). Inoltre come già accennato, la directory di lavoro corrente è una proprietà del singolo processo; questo significa che quando si lavora con i @@ -1600,14 +1602,13 @@ directory come punto di partenza per la risoluzione. In questo modo, anche quando si lavora con i \itindex{thread} \textit{thread}, si può mantenere una directory di lavoro diversa per ciascuno di essi. -Questo metodo, oltre a risolvere i problemi di \itindex{race~condition} -\textit{race condition}, consente anche di ottenere aumenti di prestazioni -significativi quando si devono eseguire molte operazioni su sezioni -dell'albero dei file che prevedono delle gerarchie di sottodirectory molto -profonde. Infatti in questo caso basta eseguire la risoluzione del -\textit{pathname} della directory di partenza una sola volta (nell'apertura -iniziale) e non tutte le volte che si deve accedere a ciascun file che essa -contiene. +Questo metodo, oltre a risolvere i problemi di \textit{race condition}, +consente anche di ottenere aumenti di prestazioni significativi quando si +devono eseguire molte operazioni su sezioni dell'albero dei file che prevedono +delle gerarchie di sottodirectory molto profonde. Infatti in questo caso basta +eseguire la risoluzione del \textit{pathname} della directory di partenza una +sola volta (nell'apertura iniziale) e non tutte le volte che si deve accedere +a ciascun file che essa contiene. La sintassi generale di queste nuove funzioni è che esse prevedono come primo argomento il file descriptor della directory da usare come base per la @@ -3093,9 +3094,8 @@ rispettivi prototipi sono: La funzione \func{getc} legge un byte da \param{stream} e lo restituisce come intero, ed in genere è implementata come una macro per cui può avere -\itindex{side~effects} \textit{side effects}, mentre \func{fgetc} è assicurato -essere sempre una funzione. Infine \func{getchar} è equivalente a -\code{getc(stdin)}. +\textit{side effects}, mentre \func{fgetc} è assicurato essere sempre una +funzione. Infine \func{getchar} è equivalente a \code{getc(stdin)}. A parte \func{getchar}, che si usa in genere per leggere un carattere da tastiera, le altre due funzioni sono sostanzialmente equivalenti. La @@ -3291,11 +3291,11 @@ adiacente al buffer.\footnote{questa tecnica è spiegata in dettaglio e con Questa è una delle vulnerabilità più sfruttate per guadagnare accessi non autorizzati al sistema (i cosiddetti \textit{exploit}), basta infatti inviare una stringa sufficientemente lunga ed opportunamente forgiata per -sovrascrivere gli indirizzi di ritorno nello \itindex{stack} \textit{stack} -(supposto che la \func{gets} sia stata chiamata da una subroutine), in modo da -far ripartire l'esecuzione nel codice inviato nella stringa stessa, che in -genere contiene uno \textit{shell code}, cioè una sezione di programma che -lancia una shell da cui si potranno poi eseguire altri programmi. +sovrascrivere gli indirizzi di ritorno nello \textit{stack} (supposto che la +\func{gets} sia stata chiamata da una subroutine), in modo da far ripartire +l'esecuzione nel codice inviato nella stringa stessa, che in genere contiene +uno \textit{shell code}, cioè una sezione di programma che lancia una shell da +cui si potranno poi eseguire altri programmi. La funzione \func{fgets} non ha i precedenti problemi di \func{gets} in quanto prende in ingresso la dimensione del buffer \param{size}, che non verrà mai @@ -3415,10 +3415,9 @@ dimensioni del buffer suddetto. Se il buffer di destinazione è sufficientemente ampio la stringa viene scritta subito, altrimenti il buffer viene allargato usando \func{realloc} e la nuova dimensione ed il nuovo puntatore vengono restituiti indietro, si noti infatti -come entrambi gli argomenti siano dei \itindex{value~result~argument} -\textit{value result argument}, per i quali vengono passati dei puntatori -anziché i valori delle variabili, secondo quanto abbiamo descritto in -sez.~\ref{sec:proc_var_passing}). +come entrambi gli argomenti siano dei \textit{value result argument}, per i +quali vengono passati dei puntatori anziché i valori delle variabili, secondo +quanto abbiamo descritto in sez.~\ref{sec:proc_var_passing}). Se si passa alla funzione l'indirizzo di un puntatore impostato a \val{NULL} e \var{*n} è zero, la funzione provvede da sola all'allocazione della memoria @@ -3426,9 +3425,8 @@ necessaria a contenere la linea. In tutti i casi si ottiene dalla funzione un puntatore all'inizio del testo della linea letta. Un esempio di codice può essere il seguente: \includecodesnip{listati/getline.c} -e per evitare \itindex{memory~leak} \textit{memory leak} occorre ricordarsi di -liberare la memoria allocata dalla funzione eseguendo una \func{free} su -\var{ptr}. +e per evitare \textit{memory leak} occorre ricordarsi di liberare la memoria +allocata dalla funzione eseguendo una \func{free} su \var{ptr}. Il valore di ritorno di \func{getline} indica il numero di caratteri letti dallo \textit{stream}, quindi compreso il \textit{newline}, ma non lo zero di @@ -3490,8 +3488,8 @@ L'output formattato viene eseguito con una delle 13 funzioni della famiglia Le funzioni usano la stringa \param{format} come indicatore del formato con -cui dovrà essere scritto il contenuto degli argomenti, il cui numero -\index{funzioni!variadic} è variabile e dipende dal formato stesso. +cui dovrà essere scritto il contenuto degli argomenti, il cui numero è +variabile e dipende dal formato stesso. Le prime due servono per scrivere su file (lo \itindex{standard~output} \textit{standard output} o quello specificato) la terza permette di scrivere @@ -3563,12 +3561,12 @@ non possa essere sovrascritto. La parte più complessa delle funzioni di scrittura formattata è il formato della stringa \param{format} che indica le conversioni da fare, e da cui deriva anche il numero degli argomenti che dovranno essere passati a seguire: -si noti come tutte queste funzioni siano \index{funzioni!variadic} -\textit{variadic}, prendendo un numero di argomenti variabile che dipende -appunto da quello che si è specificato in \param{format}. +si noti come tutte queste funzioni siano ``\textit{variadic}'', prendendo un +numero di argomenti variabile che dipende appunto da quello che si è +specificato in \param{format}. La stringa di formato è costituita da caratteri normali (tutti eccetto -``\texttt{\%}''), che vengono passati invariati all'output, e da direttive di +``\texttt{\%}''), che vengono passati invariati in uscita, e da direttive di conversione, in cui devono essere sempre presenti il carattere ``\texttt{\%}'', che introduce la direttiva, ed uno degli specificatori di conversione (riportati in tab.~\ref{tab:file_format_spec}) che la conclude. @@ -3644,11 +3642,11 @@ dettagliate dei vari formati possono essere trovati nella pagina di manuale di \cmd{L} & Una conversione in virgola mobile corrisponde a un \ctyp{double}.\\ \cmd{q} & Sinonimo di \cmd{ll}.\\ - \cmd{j} & Una conversione intera corrisponde a un \type{intmax\_t} o - \type{uintmax\_t}.\\ - \cmd{z} & Una conversione intera corrisponde a un \type{size\_t} o - \type{ssize\_t}.\\ - \cmd{t} & Una conversione intera corrisponde a un \type{ptrdiff\_t}.\\ + \cmd{j} & Una conversione intera corrisponde a un \ctyp{intmax\_t} o + \ctyp{uintmax\_t}.\\ + \cmd{z} & Una conversione intera corrisponde a un \ctyp{size\_t} o + \ctyp{ssize\_t}.\\ + \cmd{t} & Una conversione intera corrisponde a un \ctyp{ptrdiff\_t}.\\ \hline \end{tabular} \caption{Il modificatore di tipo di dato per il formato di \func{printf}} @@ -3678,11 +3676,10 @@ sez.~\ref{sec:proc_variadic}), sono \funcd{vprintf}, \funcd{vfprintf} e Con queste funzioni diventa possibile selezionare gli argomenti che si vogliono passare ad una funzione di stampa, passando direttamente la lista tramite l'argomento \param{ap}. Per poter far questo ovviamente la lista -variabile\index{funzioni!variadic} degli argomenti dovrà essere opportunamente -trattata (l'argomento è esaminato in sez.~\ref{sec:proc_variadic}), e dopo -l'esecuzione della funzione l'argomento -\param{ap} non sarà più utilizzabile (in generale dovrebbe essere eseguito un -\code{va\_end(ap)} ma in Linux questo non è necessario). +variabile degli argomenti dovrà essere opportunamente trattata (l'argomento è +esaminato in sez.~\ref{sec:proc_variadic}), e dopo l'esecuzione della funzione +l'argomento \param{ap} non sarà più utilizzabile (in generale dovrebbe essere +eseguito un \code{va\_end(ap)} ma in Linux questo non è necessario). Come per \func{sprintf} anche per \func{vsprintf} esiste una analoga \funcd{vsnprintf} che pone un limite sul numero di caratteri che vengono @@ -3722,11 +3719,10 @@ sono: Entrambe le funzioni prendono come argomento \param{strptr} che deve essere l'indirizzo di un puntatore ad una stringa di caratteri, in cui verrà restituito (si ricordi quanto detto in sez.~\ref{sec:proc_var_passing} a -proposito dei \itindex{value~result~argument} \textit{value result argument}) -l'indirizzo della stringa allocata automaticamente dalle funzioni. Occorre -inoltre ricordarsi di invocare \func{free} per liberare detto puntatore quando -la stringa non serve più, onde evitare \itindex{memory~leak} \textit{memory - leak}. +proposito dei \textit{value result argument}) l'indirizzo della stringa +allocata automaticamente dalle funzioni. Occorre inoltre ricordarsi di +invocare \func{free} per liberare detto puntatore quando la stringa non serve +più, onde evitare \textit{memory leak}. % TODO verificare se mettere prototipi di \func{dprintf} e \func{vdprintf} @@ -3955,12 +3951,12 @@ Ovviamente se si usa un buffer specificato dall'utente questo deve essere stato allocato e rimanere disponibile per tutto il tempo in cui si opera sullo \textit{stream}. In genere conviene allocarlo con \func{malloc} e disallocarlo dopo la chiusura del file; ma fintanto che il file è usato all'interno di una -funzione, può anche essere usata una \index{variabili!automatiche} variabile -automatica. In \headfile{stdio.h} è definita la macro \const{BUFSIZ}, che -indica le dimensioni generiche del buffer di uno \textit{stream}, queste -vengono usate dalla funzione \func{setbuf}. Non è detto però che tale -dimensione corrisponda sempre al valore ottimale (che può variare a seconda -del dispositivo). +funzione, può anche essere usata una variabile automatica. In +\headfile{stdio.h} è definita la macro \const{BUFSIZ}, che indica le +dimensioni generiche del buffer di uno \textit{stream}, queste vengono usate +dalla funzione \func{setbuf}. Non è detto però che tale dimensione +corrisponda sempre al valore ottimale (che può variare a seconda del +dispositivo). Dato che la procedura di allocazione manuale è macchinosa, comporta dei rischi, come delle scritture accidentali sul buffer, e non assicura la scelta