X-Git-Url: https://gapil.gnulinux.it/gitweb/?a=blobdiff_plain;f=fileadv.tex;h=75f299f9376e70667cf660044fbf942eb962267f;hb=7281907d38ca9d916bff5a3579c61d805a33c71d;hp=fd08d0d185fc15149cdf52416d69d9dbc160ece9;hpb=71bd769469078fc921d9646d62b0f9293b6ae47c;p=gapil.git diff --git a/fileadv.tex b/fileadv.tex index fd08d0d..75f299f 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -38,12 +38,11 @@ I/O. Abbiamo visto in sez.~\ref{sec:sig_gen_beha}, affrontando la suddivisione fra \textit{fast} e \textit{slow} system call,\index{system~call~lente} che in certi casi le funzioni di I/O possono bloccarsi indefinitamente.\footnote{si - ricordi però che questo può accadere solo per le pipe, i - socket\index{socket} ed alcuni file di - dispositivo\index{file!di~dispositivo}; sui file normali le funzioni di - lettura e scrittura ritornano sempre subito.} Ad esempio le operazioni di -lettura possono bloccarsi quando non ci sono dati disponibili sul descrittore -su cui si sta operando. + ricordi però che questo può accadere solo per le pipe, i socket ed alcuni + file di dispositivo\index{file!di~dispositivo}; sui file normali le funzioni + di lettura e scrittura ritornano sempre subito.} Ad esempio le operazioni +di lettura possono bloccarsi quando non ci sono dati disponibili sul +descrittore su cui si sta operando. Questo comportamento causa uno dei problemi più comuni che ci si trova ad affrontare nelle operazioni di I/O, che si verifica quando si deve operare con @@ -91,8 +90,8 @@ ulteriori dettagli e qualche esempio in sez.~\ref{sec:TCP_sock_multiplexing}. Il primo kernel unix-like ad introdurre una interfaccia per l'\textit{I/O multiplexing} è stato BSD,\footnote{la funzione \func{select} è apparsa in BSD4.2 e standardizzata in BSD4.4, ma è stata portata su tutti i sistemi che - supportano i \textit{socket}\index{socket}, compreso le varianti di System - V.} con la funzione \funcd{select}, il cui prototipo è: + supportano i socket, compreso le varianti di System V.} con la funzione +\funcd{select}, il cui prototipo è: \begin{functions} \headdecl{sys/time.h} \headdecl{sys/types.h} @@ -128,10 +127,10 @@ degli insiemi specificati (\param{readfds}, \param{writefds} e Per specificare quali file descriptor si intende \textsl{selezionare}, la funzione usa un particolare oggetto, il \textit{file descriptor set}, identificato dal tipo \type{fd\_set}, che serve ad identificare un insieme di -file descriptor, in maniera analoga a come un -\itindex{signal~set}\textit{signal set} (vedi sez.~\ref{sec:sig_sigset}) -identifica un insieme di segnali. Per la manipolazione di questi \textit{file - descriptor set} si possono usare delle opportune macro di preprocessore: +file descriptor, in maniera analoga a come un \itindex{signal~set} +\textit{signal set} (vedi sez.~\ref{sec:sig_sigset}) identifica un insieme di +segnali. Per la manipolazione di questi \textit{file descriptor set} si +possono usare delle opportune macro di preprocessore: \begin{functions} \headdecl{sys/time.h} \headdecl{sys/types.h} @@ -167,8 +166,8 @@ effettuare una lettura,\footnote{per essere precisi la funzione ritorner tutti i casi in cui la successiva esecuzione di \func{read} risulti non bloccante, quindi anche in caso di \textit{end-of-file}.} il secondo, \param{writefds}, per verificare la possibilità effettuare una scrittura ed il -terzo, \param{exceptfds}, per verificare l'esistenza di eccezioni (come i -messaggi urgenti su un \textit{socket}\index{socket}, vedi +terzo, \param{exceptfds}, per verificare l'esistenza di eccezioni (come i dati +urgenti \itindex{out-of-band} su un socket, vedi sez.~\ref{sec:TCP_urgent_data}). Dato che in genere non si tengono mai sotto controllo fino a @@ -186,7 +185,6 @@ ritorni; se impostato a \val{NULL} la funzione attende indefinitamente. Si pu specificare anche un tempo nullo (cioè una struttura \struct{timeval} con i campi impostati a zero), qualora si voglia semplicemente controllare lo stato corrente dei file descriptor. -\itindend{file~descriptor~set} La funzione restituisce il numero di file descriptor pronti,\footnote{questo è il comportamento previsto dallo standard, ma la standardizzazione della @@ -199,6 +197,8 @@ modificati. In caso di errore la funzione restituisce -1, ed i valori dei tre insiemi sono indefiniti e non si può fare nessun affidamento sul loro contenuto. +\itindend{file~descriptor~set} + In Linux \func{select} modifica anche il valore di \param{timeout}, impostandolo al tempo restante in caso di interruzione prematura; questo è utile quando la funzione viene interrotta da un segnale, in tal caso infatti @@ -292,25 +292,26 @@ interrotta, e la ricezione del segnale non sar Per questo è stata introdotta \func{pselect} che attraverso l'argomento \param{sigmask} permette di riabilitare la ricezione il segnale -contestualmente all'esecuzione della funzione,\footnote{in Linux però non è - presente la relativa system call, e la funzione è implementata nelle - \acr{glibc} attraverso \func{select} (vedi \texttt{man select\_tut}) per cui - la possibilità di \itindex{race~condition}\textit{race condition} permane; - esiste però una soluzione, chiamata \itindex{self-pipe trick} - \textit{self-pipe trick}, che consiste nell'aprire una pipe (vedi - sez.~\ref{sec:ipc_pipes}) ed usare \func{select} sul capo in lettura della - stessa, e indicare l'arrivo di un segnale scrivendo sul capo in scrittura - all'interno del manipolatore; in questo modo anche se il segnale va perso - prima della chiamata di \func{select} questa lo riconoscerà comunque dalla - presenza di dati sulla pipe.} ribloccandolo non appena essa ritorna, così -che il precedente codice potrebbe essere riscritto nel seguente modo: +contestualmente all'esecuzione della funzione,\footnote{in Linux però, fino al + kernel 2.6.26, non è presente la relativa system call, e la funzione è + implementata nelle \acr{glibc} attraverso \func{select} (vedi \texttt{man + select\_tut}) per cui la possibilità di \itindex{race~condition} + \textit{race condition} permane; esiste però una soluzione, chiamata + \itindex{self-pipe trick} \textit{self-pipe trick}, che consiste nell'aprire + una pipe (vedi sez.~\ref{sec:ipc_pipes}) ed usare \func{select} sul capo in + lettura della stessa, e indicare l'arrivo di un segnale scrivendo sul capo + in scrittura all'interno del gestore dello stesso; in questo modo anche se + il segnale va perso prima della chiamata di \func{select} questa lo + riconoscerà comunque dalla presenza di dati sulla pipe.} ribloccandolo non +appena essa ritorna, così che il precedente codice potrebbe essere riscritto +nel seguente modo: \includecodesnip{listati/pselect_norace.c} in questo caso utilizzando \var{oldmask} durante l'esecuzione di \func{pselect} la ricezione del segnale sarà abilitata, ed in caso di interruzione si potranno eseguire le relative operazioni. % TODO pselect è stata introdotta nel kernel 2.6.16 (o 15 o 17?) insieme a -% ppoll mettere e verificare +% ppoll mettere e verificare, vedi articolo LWN http://lwn.net/Articles/176750/ \subsection{La funzione \func{poll}} @@ -393,7 +394,8 @@ nel campo \var{revents} per notificare delle condizioni di errore. \const{POLLIN} & È possibile la lettura.\\ \const{POLLRDNORM}& Sono disponibili in lettura dati normali.\\ \const{POLLRDBAND}& Sono disponibili in lettura dati prioritari. \\ - \const{POLLPRI} & È possibile la lettura di dati urgenti.\\ + \const{POLLPRI} & È possibile la lettura di \itindex{out-of-band} dati + urgenti.\\ \hline \const{POLLOUT} & È possibile la scrittura immediata.\\ \const{POLLWRNORM}& È possibile la scrittura di dati normali. \\ @@ -418,15 +420,15 @@ compatibilit delle librerie standard del C.} è da questi che derivano i nomi di alcune costanti, in quanto per essi sono definite tre classi di dati: \textsl{normali}, \textit{prioritari} ed \textit{urgenti}. In Linux la -distinzione ha senso solo per i dati \textit{out-of-band} dei socket (vedi -sez.~\ref{sec:TCP_urgent_data}), ma su questo e su come \func{poll} reagisce -alle varie condizioni dei socket torneremo in sez.~\ref{sec:TCP_serv_poll}, -dove vedremo anche un esempio del suo utilizzo. Si tenga conto comunque che le -costanti relative ai diversi tipi di dati (come \const{POLLRDNORM} e -\const{POLLRDBAND}) sono utilizzabili soltanto qualora si sia definita la -macro \macro{\_XOPEN\_SOURCE}.\footnote{e ci si ricordi di farlo sempre in - testa al file, definirla soltanto prima di includere \file{sys/poll.h} non è - sufficiente.} +distinzione ha senso solo per i dati urgenti \itindex{out-of-band} dei socket +(vedi sez.~\ref{sec:TCP_urgent_data}), ma su questo e su come \func{poll} +reagisce alle varie condizioni dei socket torneremo in +sez.~\ref{sec:TCP_serv_poll}, dove vedremo anche un esempio del suo utilizzo. +Si tenga conto comunque che le costanti relative ai diversi tipi di dati (come +\const{POLLRDNORM} e \const{POLLRDBAND}) sono utilizzabili soltanto qualora si +sia definita la macro \macro{\_XOPEN\_SOURCE}.\footnote{e ci si ricordi di + farlo sempre in testa al file, definirla soltanto prima di includere + \file{sys/poll.h} non è sufficiente.} In caso di successo funzione ritorna restituendo il numero di file (un valore positivo) per i quali si è verificata una delle condizioni di attesa richieste @@ -595,11 +597,10 @@ il segnale \const{SIGIO}, ma questo segnale pu comando \const{F\_SETSIG} di \func{fcntl}.\footnote{anche in questo caso si può rispecificare lo stesso \const{SIGIO}.} Se si è fatto questo\footnote{è in genere è opportuno farlo, come in precedenza, per utilizzare segnali - real-time.} e si è installato il manipolatore del segnale con -\const{SA\_SIGINFO} si riceverà nel campo \var{si\_fd} della struttura -\struct{siginfo\_t} il valore del file descriptor del file sul quale è stato -compiuto l'accesso; in questo modo un processo può mantenere anche più di un -\textit{file lease}. + real-time.} e si è installato il gestore del segnale con \const{SA\_SIGINFO} +si riceverà nel campo \var{si\_fd} della struttura \struct{siginfo\_t} il +valore del file descriptor del file sul quale è stato compiuto l'accesso; in +questo modo un processo può mantenere anche più di un \textit{file lease}. Esistono due tipi di \textit{file lease}: di lettura (\textit{read lease}) e di scrittura (\textit{write lease}). Nel primo caso la notifica avviene quando @@ -688,7 +689,7 @@ kernel \textit{lease breaker} rimaste bloccate proseguono automaticamente. -\index{file!notify|(} +\index{file!dnotify|(} Benché possa risultare utile per sincronizzare l'accesso ad uno stesso file da parte di più processi, l'uso dei \textit{file lease} non consente comunque di @@ -696,12 +697,13 @@ risolvere il problema di rilevare automaticamente quando un file viene modificato, che è quanto necessario ad esempio ai programma di gestione dei file dei vari desktop grafici. -Per risolvere questo problema è stata allora creata un'altra interfaccia che -consente di richiedere una notifica quando una directory, o di uno qualunque -dei file in essa contenuti, viene modificato. Come per i \textit{file lease} -la notifica avviene di default attraverso il segnale \const{SIGIO}, ma questo -può essere modificato e si può ottenere nel manipolatore il file descriptor -che è stato modificato dal contenuto della struttura \struct{siginfo\_t}. +Per risolvere questo problema è stata allora creata un'altra interfaccia, +chiamata \textit{dnotify}, che consente di richiedere una notifica quando una +directory, o di uno qualunque dei file in essa contenuti, viene modificato. +Come per i \textit{file lease} la notifica avviene di default attraverso il +segnale \const{SIGIO}, ma questo può essere modificato e si può ottenere nel +gestore il file descriptor che è stato modificato dal contenuto della +struttura \struct{siginfo\_t}. \index{file!lease|)} @@ -758,18 +760,13 @@ ultimi si \textsl{accumulano}; cio specificate in chiamate successive vengono aggiunte a quelle già impostate nelle precedenti. Se si vuole rimuovere la notifica si deve invece specificare un valore nullo. - -\index{file!notify|)} - - - - - - +\index{file!dnotify|)} -% TODO inserire anche inotify +\index{file!inotify|)} +% TODO inserire anche inotify, vedi http://www.linuxjournal.com/article/8478 +\index{file!inotify|(} @@ -785,14 +782,14 @@ possibile effettuare una richiesta preventiva di dati, in modo da poter effettuare in contemporanea le operazioni di calcolo e quelle di I/O. Benché la modalità di apertura asincrona di un file possa risultare utile in -varie occasioni (in particolar modo con i socket\index{socket} e gli altri -file per i quali le funzioni di I/O sono \index{system~call~lente}system call -lente), essa è comunque limitata alla notifica della disponibilità del file -descriptor per le operazioni di I/O, e non ad uno svolgimento asincrono delle -medesime. Lo standard POSIX.1b definisce una interfaccia apposita per l'I/O -asincrono vero e proprio, che prevede un insieme di funzioni dedicate per la -lettura e la scrittura dei file, completamente separate rispetto a quelle -usate normalmente. +varie occasioni (in particolar modo con i socket e gli altri file per i quali +le funzioni di I/O sono \index{system~call~lente}system call lente), essa è +comunque limitata alla notifica della disponibilità del file descriptor per le +operazioni di I/O, e non ad uno svolgimento asincrono delle medesime. Lo +standard POSIX.1b definisce una interfaccia apposita per l'I/O asincrono vero +e proprio, che prevede un insieme di funzioni dedicate per la lettura e la +scrittura dei file, completamente separate rispetto a quelle usate +normalmente. In generale questa interfaccia è completamente astratta e può essere implementata sia direttamente nel kernel, che in user space attraverso l'uso @@ -1140,7 +1137,8 @@ Oltre alle precedenti modalit accesso ai file più evolute rispetto alle normali funzioni di lettura e scrittura che abbiamo esaminato in sez.~\ref{sec:file_base_func}. In questa sezione allora prenderemo in esame le interfacce per l'\textsl{I/O - vettorizzato} e per l'\textsl{I/O mappato in memoria}. + vettorizzato} e per l'\textsl{I/O mappato in memoria} e la funzione +\func{sendfile}. \subsection{I/O vettorizzato} @@ -1343,7 +1341,7 @@ multiplo della dimensione di una pagina di memoria. Il valore dell'argomento \param{prot} indica la protezione\footnote{in Linux la memoria reale è divisa in pagine: ogni processo vede la sua memoria attraverso uno o più segmenti lineari di memoria virtuale. Per ciascuno di - questi segmenti il kernel mantiene nella \itindex{page~table}\textit{page + questi segmenti il kernel mantiene nella \itindex{page~table} \textit{page table} la mappatura sulle pagine di memoria reale, ed le modalità di accesso (lettura, esecuzione, scrittura); una loro violazione causa quella che si chiama una \textit{segment violation}, e la relativa emissione del @@ -1675,7 +1673,6 @@ una combinazione dei valori di tab.~\ref{tab:file_mmap_prot}. La nuova protezione verrà applicata a tutte le pagine contenute, anche parzialmente, dall'intervallo fra \param{addr} e \param{addr}+\param{size}-1. - Infine Linux supporta alcune operazioni specifiche non disponibili su altri kernel unix-like. La prima di queste è la possibilità di modificare un precedente \textit{memory mapping}, ad esempio per espanderlo o restringerlo. @@ -1838,6 +1835,19 @@ mappatura che gi \itindend{memory~mapping} +\subsection{L'I/O diretto fra file descriptor con \func{sendfile}} +\label{sec:file_sendfile} + +Uno dei problemi + +NdA è da finire, sul perché non è abilitata fra file vedi: + +\href{http://www.cs.helsinki.fi/linux/linux-kernel/2001-03/0200.html} +{\texttt{http://www.cs.helsinki.fi/linux/linux-kernel/2001-03/0200.html}} +% TODO documentare la funzione sendfile + + + % i raw device %\subsection{I \textit{raw} device} %\label{sec:file_raw_device} @@ -1852,6 +1862,8 @@ mappatura che gi % consultare le manpage di ioperm, iopl e outb + + \section{Il file locking} \label{sec:file_locking} @@ -2060,38 +2072,42 @@ piuttosto degli ulteriori riferimenti allo stesso. Questo viene realizzato dal kernel secondo lo schema di fig.~\ref{fig:file_flock_struct}, associando ad ogni nuovo \textit{file lock} un puntatore\footnote{il puntatore è mantenuto nel campo \var{fl\_file} di \struct{file\_lock}, e viene utilizzato solo per - i lock creati con la semantica BSD.} alla voce nella \textit{file table} da -cui si è richiesto il lock, che così ne identifica il titolare. + i lock creati con la semantica BSD.} alla voce nella \itindex{file~table} +\textit{file table} da cui si è richiesto il lock, che così ne identifica il +titolare. Questa struttura prevede che, quando si richiede la rimozione di un file lock, il kernel acconsenta solo se la richiesta proviene da un file descriptor che -fa riferimento ad una voce nella file table corrispondente a quella registrata -nel lock. Allora se ricordiamo quanto visto in sez.~\ref{sec:file_dup} e -sez.~\ref{sec:file_sharing}, e cioè che i file descriptor duplicati e quelli -ereditati in un processo figlio puntano sempre alla stessa voce nella file -table, si può capire immediatamente quali sono le conseguenze nei confronti -delle funzioni \func{dup} e \func{fork}. +fa riferimento ad una voce nella \itindex{file~table} \textit{file table} +corrispondente a quella registrata nel lock. Allora se ricordiamo quanto +visto in sez.~\ref{sec:file_dup} e sez.~\ref{sec:file_sharing}, e cioè che i +file descriptor duplicati e quelli ereditati in un processo figlio puntano +sempre alla stessa voce nella \itindex{file~table} \textit{file table}, si può +capire immediatamente quali sono le conseguenze nei confronti delle funzioni +\func{dup} e \func{fork}. Sarà così possibile rimuovere un file lock attraverso uno qualunque dei file -descriptor che fanno riferimento alla stessa voce nella file table, anche se -questo è diverso da quello con cui lo si è creato,\footnote{attenzione, questo - non vale se il file descriptor fa riferimento allo stesso file, ma - attraverso una voce diversa della file table, come accade tutte le volte che - si apre più volte lo stesso file.} o se si esegue la rimozione in un -processo figlio; inoltre una volta tolto un file lock, la rimozione avrà -effetto su tutti i file descriptor che condividono la stessa voce nella file -table, e quindi, nel caso di file descriptor ereditati attraverso una -\func{fork}, anche su processi diversi. +descriptor che fanno riferimento alla stessa voce nella \itindex{file~table} +\textit{file table}, anche se questo è diverso da quello con cui lo si è +creato,\footnote{attenzione, questo non vale se il file descriptor fa + riferimento allo stesso file, ma attraverso una voce diversa della + \itindex{file~table} \textit{file table}, come accade tutte le volte che si + apre più volte lo stesso file.} o se si esegue la rimozione in un processo +figlio; inoltre una volta tolto un file lock, la rimozione avrà effetto su +tutti i file descriptor che condividono la stessa voce nella +\itindex{file~table} \textit{file table}, e quindi, nel caso di file +descriptor ereditati attraverso una \func{fork}, anche su processi diversi. Infine, per evitare che la terminazione imprevista di un processo lasci attivi dei file lock, quando un file viene chiuso il kernel provveda anche a rimuovere tutti i lock ad esso associati. Anche in questo caso occorre tenere presente cosa succede quando si hanno file descriptor duplicati; in tal caso infatti il file non verrà effettivamente chiuso (ed il lock rimosso) fintanto -che non viene rilasciata la relativa voce nella file table; e questo avverrà -solo quando tutti i file descriptor che fanno riferimento alla stessa voce -sono stati chiusi. Quindi, nel caso ci siano duplicati o processi figli che -mantengono ancora aperto un file descriptor, il lock non viene rilasciato. +che non viene rilasciata la relativa voce nella \itindex{file~table} +\textit{file table}; e questo avverrà solo quando tutti i file descriptor che +fanno riferimento alla stessa voce sono stati chiusi. Quindi, nel caso ci +siano duplicati o processi figli che mantengono ancora aperto un file +descriptor, il lock non viene rilasciato. Si tenga presente infine che \func{flock} non è in grado di funzionare per i file mantenuti su NFS, in questo caso, se si ha la necessità di eseguire il @@ -2283,7 +2299,8 @@ di fig.~\ref{fig:file_flock_struct}:\footnote{in questo caso nella figura si impostato il bit \const{FL\_POSIX} ed il campo \var{fl\_file} non viene usato.} il lock è sempre associato all'inode\index{inode}, solo che in questo caso la titolarità non viene identificata con il riferimento ad una -voce nella file table, ma con il valore del \acr{pid} del processo. +voce nella \itindex{file~table} \textit{file table}, ma con il valore del +\acr{pid} del processo. Quando si richiede un lock il kernel effettua una scansione di tutti i lock presenti sul file\footnote{scandisce cioè la \itindex{linked~list} @@ -2613,7 +2630,7 @@ opportune verifiche nei processi, questo verrebbe comunque rispettato. Per poter utilizzare il \textit{mandatory locking} è stato introdotto un utilizzo particolare del bit \itindex{sgid~bit} \acr{sgid}. Se si ricorda -quanto esposto in sez.~\ref{sec:file_suid_sgid}), esso viene di norma +quanto esposto in sez.~\ref{sec:file_special_perm}), esso viene di norma utilizzato per cambiare il group-ID effettivo con cui viene eseguito un programma, ed è pertanto sempre associato alla presenza del permesso di esecuzione per il gruppo. Impostando questo bit su un file senza permesso di @@ -2622,9 +2639,10 @@ quest'ultimo venga attivato per il file in questione. In questo modo una combinazione dei permessi originariamente non contemplata, in quanto senza significato, diventa l'indicazione della presenza o meno del \textit{mandatory locking}.\footnote{un lettore attento potrebbe ricordare quanto detto in - sez.~\ref{sec:file_chmod} e cioè che il bit \acr{sgid} viene cancellato - (come misura di sicurezza) quando di scrive su un file, questo non vale - quando esso viene utilizzato per attivare il \textit{mandatory locking}.} + sez.~\ref{sec:file_perm_management} e cioè che il bit \acr{sgid} viene + cancellato (come misura di sicurezza) quando di scrive su un file, questo + non vale quando esso viene utilizzato per attivare il \textit{mandatory + locking}.} L'uso del \textit{mandatory locking} presenta vari aspetti delicati, dato che neanche l'amministratore può passare sopra ad un lock; pertanto un processo @@ -2734,4 +2752,4 @@ possibilit % LocalWords: flock shared exclusive operation dup inode linked NFS cmd ENOLCK % LocalWords: EDEADLK whence SEEK CUR type pid GETLK SETLK SETLKW all'inode HP % LocalWords: switch bsd lockf mandatory SVr sgid group root mount mand TRUNC -% LocalWords: SVID UX Documentation +% LocalWords: SVID UX Documentation sendfile