X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=fileadv.tex;h=8b979cb3780e0ebae471a271497d36bc3d67db62;hp=54628571617c3b4b3fdc4f46658f43d079017a3f;hb=beece18eba2dcc2a9b915dab61277df8685a3da6;hpb=414401b178e7542189e5cc13ebafd8806cee3724 diff --git a/fileadv.tex b/fileadv.tex index 5462857..8b979cb 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -1,6 +1,6 @@ %% fileadv.tex %% -%% Copyright (C) 2000-2012 Simone Piccardi. Permission is granted to +%% Copyright (C) 2000-2014 Simone Piccardi. Permission is granted to %% copy, distribute and/or modify this document under the terms of the GNU Free %% Documentation License, Version 1.1 or any later version published by the %% Free Software Foundation; with the Invariant Sections being "Un preambolo", @@ -914,13 +914,13 @@ I/O. \label{sec:file_noblocking} 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 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. +\textit{fast} e \textit{slow} \textit{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 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 @@ -945,8 +945,8 @@ modalità di I/O permette di risolvere il problema controllando a turno i vari file descriptor, in un ciclo in cui si ripete l'accesso fintanto che esso non viene garantito. Ovviamente questa tecnica, detta \itindex{polling} \textit{polling}, è estremamente inefficiente: si tiene costantemente -impiegata la CPU solo per eseguire in continuazione delle system call che -nella gran parte dei casi falliranno. +impiegata la CPU solo per eseguire in continuazione delle \textit{system call} +che nella gran parte dei casi falliranno. Per superare questo problema è stato introdotto il concetto di \textit{I/O multiplexing}, una nuova modalità di operazioni che consente di tenere sotto @@ -1161,15 +1161,16 @@ precedenti, ed inoltre aggiunge a \func{select} una nuova funzione La funzione è sostanzialmente identica a \func{select}, solo che usa una struttura \struct{timespec} (vedi fig.~\ref{fig:sys_timespec_struct}) per indicare con maggiore precisione il timeout e non ne aggiorna il valore in -caso di interruzione.\footnote{in realtà la system call di Linux aggiorna il - valore al tempo rimanente, ma la funzione fornita dalle \acr{glibc} modifica - questo comportamento passando alla system call una variabile locale, in modo - da mantenere l'aderenza allo standard POSIX che richiede che il valore di - \param{timeout} non sia modificato.} Inoltre prende un argomento aggiuntivo -\param{sigmask} che è il puntatore ad una \index{maschera~dei~segnali} -maschera di segnali (si veda sez.~\ref{sec:sig_sigmask}). La maschera -corrente viene sostituita da questa immediatamente prima di eseguire l'attesa, -e ripristinata al ritorno della funzione. +caso di interruzione.\footnote{in realtà la \textit{system call} di Linux + aggiorna il valore al tempo rimanente, ma la funzione fornita dalle + \acr{glibc} modifica questo comportamento passando alla \textit{system call} + una variabile locale, in modo da mantenere l'aderenza allo standard POSIX + che richiede che il valore di \param{timeout} non sia modificato.} Inoltre +prende un argomento aggiuntivo \param{sigmask} che è il puntatore ad una +\index{maschera~dei~segnali} maschera di segnali (si veda +sez.~\ref{sec:sig_sigmask}). La maschera corrente viene sostituita da questa +immediatamente prima di eseguire l'attesa, e ripristinata al ritorno della +funzione. L'uso di \param{sigmask} è stato introdotto allo scopo di prevenire possibili \textit{race condition} \itindex{race~condition} quando ci si deve porre in @@ -1196,18 +1197,19 @@ interrotta, e la ricezione del segnale non sarà rilevata. 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ò, fino al - kernel 2.6.16, non era presente la relativa system call, e la funzione era - implementata nelle \acr{glibc} attraverso \func{select} (vedi \texttt{man - select\_tut}) per cui la possibilità di \itindex{race~condition} - \textit{race condition} permaneva; in tale situazione si può ricorrere ad una - soluzione alternativa, 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; si può 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: + kernel 2.6.16, non era presente la relativa \textit{system call}, e la + funzione era implementata nelle \acr{glibc} attraverso \func{select} (vedi + \texttt{man select\_tut}) per cui la possibilità di \itindex{race~condition} + \textit{race condition} permaneva; in tale situazione si può ricorrere ad + una soluzione alternativa, 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; si può 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 @@ -1903,8 +1905,8 @@ interruzioni delle funzioni di attesa sincrone, ed evitare possibili \itindex{race~condition} \textit{race conditions}.\footnote{in sostanza se non fossero per i segnali non ci sarebbe da doversi preoccupare, fintanto che si effettuano operazioni all'interno di un processo, della non atomicità delle - \index{system~call~lente} system call lente che vengono interrotte e devono - essere riavviate.} + \index{system~call~lente} \textit{system call} lente che vengono interrotte + e devono essere riavviate.} Abbiamo visto però in sez.~\ref{sec:sig_real_time} che insieme ai segnali \textit{real-time} sono state introdotte anche delle interfacce di gestione @@ -3237,8 +3239,8 @@ approssimativamente 512 eventi.\footnote{si ricordi che la quantità di dati del nome del file restituito insieme a \struct{inotify\_event}.} In caso di errore di lettura (\texttt{\small 35--40}) il programma esce con un messaggio di errore (\texttt{\small 37--39}), a meno che non si tratti di una -interruzione della system call, nel qual caso (\texttt{\small 36}) si ripete la -lettura. +interruzione della \textit{system call}, nel qual caso (\texttt{\small 36}) si +ripete la lettura. Se la lettura è andata a buon fine invece si esegue un ciclo (\texttt{\small 43--52}) per leggere tutti gli eventi restituiti, al solito si inizializza @@ -3336,12 +3338,12 @@ 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 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 +le funzioni di I/O sono \index{system~call~lente} \textit{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 @@ -3473,8 +3475,8 @@ verificatosi, ed esegue la corrispondente impostazione di \var{errno}. Il codice può essere sia \errcode{EINVAL} ed \errcode{EBADF}, dovuti ad un valore errato per \param{aiocbp}, che uno degli errori possibili durante l'esecuzione dell'operazione di I/O richiesta, nel qual caso saranno restituiti, a seconda -del caso, i codici di errore delle system call \func{read}, \func{write} e -\func{fsync}. +del caso, i codici di errore delle \textit{system call} \func{read}, +\func{write} e \func{fsync}. Una volta che si sia certi che le operazioni siano state concluse (cioè dopo che una chiamata ad \func{aio\_error} non ha restituito @@ -3498,10 +3500,10 @@ l'operazione cui \param{aiocbp} fa riferimento si è completata. Una chiamata precedente il completamento delle operazioni darebbe risultati indeterminati. La funzione restituisce il valore di ritorno relativo all'operazione eseguita, -così come ricavato dalla sottostante system call (il numero di byte letti, -scritti o il valore di ritorno di \func{fsync}). É importante chiamare sempre -questa funzione, altrimenti le risorse disponibili per le operazioni di I/O -asincrono non verrebbero liberate, rischiando di arrivare ad un loro +così come ricavato dalla sottostante \textit{system call} (il numero di byte +letti, scritti o il valore di ritorno di \func{fsync}). É importante chiamare +sempre questa funzione, altrimenti le risorse disponibili per le operazioni di +I/O asincrono non verrebbero liberate, rischiando di arrivare ad un loro esaurimento. Oltre alle operazioni di lettura e scrittura l'interfaccia POSIX.1b mette a @@ -4227,7 +4229,7 @@ unix-like. Diventa così possibile utilizzare una sola mappatura iniziale\footnote{e quindi una sola \textit{virtual memory area} nella \itindex{page~table} \textit{page table} del processo.} e poi rimappare a piacere all'interno di questa i dati del file. Ciò è possibile grazie ad una -nuova system call, \funcd{remap\_file\_pages}, il cui prototipo è: +nuova \textit{system call}, \funcd{remap\_file\_pages}, il cui prototipo è: \begin{functions} \headdecl{sys/mman.h} @@ -4442,13 +4444,13 @@ l'operazione sia facilmente eseguibile attraverso una serie multipla di chiamate a \func{read} e \func{write}, ci sono casi in cui si vuole poter contare sulla atomicità delle operazioni. -Per questo motivo fino da BSD 4.2 vennero introdotte delle nuove system call -che permettessero di effettuare con una sola chiamata una serie di letture o -scritture su una serie di buffer, con quello che viene normalmente chiamato -\textsl{I/O vettorizzato}. Queste funzioni sono \funcd{readv} e -\funcd{writev},\footnote{in Linux le due funzioni sono riprese da BSD4.4, esse - sono previste anche dallo standard POSIX.1-2001.} ed i relativi prototipi -sono: +Per questo motivo fino da BSD 4.2 vennero introdotte delle nuove +\textit{system call} che permettessero di effettuare con una sola chiamata una +serie di letture o scritture su una serie di buffer, con quello che viene +normalmente chiamato \textsl{I/O vettorizzato}. Queste funzioni sono +\funcd{readv} e \funcd{writev},\footnote{in Linux le due funzioni sono riprese + da BSD4.4, esse sono previste anche dallo standard POSIX.1-2001.} ed i +relativi prototipi sono: \begin{functions} \headdecl{sys/uio.h} @@ -4512,10 +4514,10 @@ stesso valore deve essere ottenibile in esecuzione tramite la funzione sez.~\ref{sec:sys_limits}). Nel caso di Linux il limite di sistema è di 1024, però se si usano le -\acr{glibc} queste forniscono un \textit{wrapper} per le system call che si -accorge se una operazione supererà il precedente limite, in tal caso i dati -verranno letti o scritti con le usuali \func{read} e \func{write} usando un -buffer di dimensioni sufficienti appositamente allocato e sufficiente a +\acr{glibc} queste forniscono un \textit{wrapper} per le \textit{system call} +che si accorge se una operazione supererà il precedente limite, in tal caso i +dati verranno letti o scritti con le usuali \func{read} e \func{write} usando +un buffer di dimensioni sufficienti appositamente allocato e sufficiente a contenere tutti i dati indicati da \param{vector}. L'operazione avrà successo ma si perderà l'atomicità del trasferimento da e verso la destinazione finale. @@ -4695,16 +4697,16 @@ semplicemente un ``\textsl{dimezzamento}'' di \func{sendfile}.\footnote{nel senso che un trasferimento di dati fra due file con \func{sendfile} non sarebbe altro che la lettura degli stessi su un buffer seguita dalla relativa scrittura, cosa che in questo caso si dovrebbe eseguire con due - chiamate a \func{splice}.} In realtà le due system call sono profondamente -diverse nel loro meccanismo di funzionamento;\footnote{questo fino al kernel - 2.6.23, dove \func{sendfile} è stata reimplementata in termini di - \func{splice}, pur mantenendo disponibile la stessa interfaccia verso l'user - space.} \func{sendfile} infatti, come accennato, non necessita di avere a -disposizione un buffer interno, perché esegue un trasferimento diretto di -dati; questo la rende in generale più efficiente, ma anche limitata nelle sue -applicazioni, dato che questo tipo di trasferimento è possibile solo in casi -specifici.\footnote{e nel caso di Linux questi sono anche solo quelli in cui - essa può essere effettivamente utilizzata.} + chiamate a \func{splice}.} In realtà le due \textit{system call} sono +profondamente diverse nel loro meccanismo di funzionamento;\footnote{questo + fino al kernel 2.6.23, dove \func{sendfile} è stata reimplementata in + termini di \func{splice}, pur mantenendo disponibile la stessa interfaccia + verso l'user space.} \func{sendfile} infatti, come accennato, non necessita +di avere a disposizione un buffer interno, perché esegue un trasferimento +diretto di dati; questo la rende in generale più efficiente, ma anche limitata +nelle sue applicazioni, dato che questo tipo di trasferimento è possibile solo +in casi specifici.\footnote{e nel caso di Linux questi sono anche solo quelli + in cui essa può essere effettivamente utilizzata.} Il concetto che sta dietro a \func{splice} invece è diverso,\footnote{in realtà la proposta originale di Larry Mc Voy non differisce poi tanto negli @@ -5103,16 +5105,16 @@ fig.~\ref{fig:splice_example}). Infine una nota finale riguardo \func{splice}, \func{vmsplice} e \func{tee}: occorre sottolineare che benché finora si sia parlato di trasferimenti o copie -di dati in realtà nella implementazione di queste system call non è affatto -detto che i dati vengono effettivamente spostati o copiati, il kernel infatti -realizza le \textit{pipe} come un insieme di puntatori\footnote{per essere - precisi si tratta di un semplice buffer circolare, un buon articolo sul tema - si trova su \url{http://lwn.net/Articles/118750/}.} alle pagine di memoria -interna che contengono i dati, per questo una volta che i dati sono presenti -nella memoria del kernel tutto quello che viene fatto è creare i suddetti -puntatori ed aumentare il numero di referenze; questo significa che anche con -\func{tee} non viene mai copiato nessun byte, vengono semplicemente copiati i -puntatori. +di dati in realtà nella implementazione di queste \textit{system call} non è +affatto detto che i dati vengono effettivamente spostati o copiati, il kernel +infatti realizza le \textit{pipe} come un insieme di puntatori\footnote{per + essere precisi si tratta di un semplice buffer circolare, un buon articolo + sul tema si trova su \url{http://lwn.net/Articles/118750/}.} alle pagine di +memoria interna che contengono i dati, per questo una volta che i dati sono +presenti nella memoria del kernel tutto quello che viene fatto è creare i +suddetti puntatori ed aumentare il numero di referenze; questo significa che +anche con \func{tee} non viene mai copiato nessun byte, vengono semplicemente +copiati i puntatori. % TODO?? dal 2.6.25 splice ha ottenuto il supporto per la ricezione su rete @@ -5427,7 +5429,7 @@ livello di kernel. % http://lwn.net/Articles/432757/ -% LocalWords: dell'I locking multiplexing cap dell' sez system call socket BSD +% LocalWords: dell'I locking multiplexing cap sez system call socket BSD % LocalWords: descriptor client deadlock NONBLOCK EAGAIN polling select kernel % LocalWords: pselect like sys unistd int fd readfds writefds exceptfds struct % LocalWords: timeval errno EBADF EINTR EINVAL ENOMEM sleep tab signal void of