Agginte altre figure sulla condivisione dei file e scritte read e write
authorSimone Piccardi <piccardi@gnulinux.it>
Sun, 11 Nov 2001 16:27:38 +0000 (16:27 +0000)
committerSimone Piccardi <piccardi@gnulinux.it>
Sun, 11 Nov 2001 16:27:38 +0000 (16:27 +0000)
fileunix.tex
img/filedup.dia [new file with mode: 0644]
img/filemultacc.dia [new file with mode: 0644]
img/fileshar.dia [new file with mode: 0644]
simpltcp.tex
socket.tex

index bc94a0e2f6d650fd91e539f008df2717964cf47b..c2412e8139397171bae5f769e764872b4626e44c 100644 (file)
@@ -80,7 +80,7 @@ strutture di dati sulla quale essa 
   \centering
   \includegraphics[width=14cm]{img/procfile.eps}
   \caption{Schema della architettura dell'accesso ai file attraverso
   \centering
   \includegraphics[width=14cm]{img/procfile.eps}
   \caption{Schema della architettura dell'accesso ai file attraverso
-  l'interfaccia dei \textit{file descroptor}}
+  l'interfaccia dei \textit{file descriptor}}
   \label{fig:file_proc_file}
 \end{figure}
 Ritorneremo su questo schema più volte, dato che esso è fondamentale per
   \label{fig:file_proc_file}
 \end{figure}
 Ritorneremo su questo schema più volte, dato che esso è fondamentale per
@@ -476,9 +476,7 @@ causano un errore ma restituiscono un valore indefinito.
 
 Per leggere da un file precedentemente aperto, si può la funzione \func{read},
 il cui prototipo è:
 
 Per leggere da un file precedentemente aperto, si può la funzione \func{read},
 il cui prototipo è:
-\begin{prototype}
-  \headdecl{unistd.h}
-  \funcdecl{ssize\_t read(int fd, void * buf, size\_t count)}
+\begin{prototype}{unistd.h}{ssize\_t read(int fd, void * buf, size\_t count)}
   
   La funzione cerca di leggere \var{count} bytes dal file \var{fd} al buffer
   \var{buf}.
   
   La funzione cerca di leggere \var{count} bytes dal file \var{fd} al buffer
   \var{buf}.
@@ -487,41 +485,255 @@ il cui prototipo 
   caso di errore, nel qual caso \var{errno} viene settata ad uno dei valori:
   \begin{errlist}
   \item \macro{EINTR} la funzione è stata interrotta da un segnale prima di
   caso di errore, nel qual caso \var{errno} viene settata ad uno dei valori:
   \begin{errlist}
   \item \macro{EINTR} la funzione è stata interrotta da un segnale prima di
-    aver letto quasiasi dato.
+    aver potuto leggere quasiasi dato.
     \item \macro{EAGAIN} la funzione non aveva nessun dato da restituire e si
       era aperto il file in modalità \macro{O\_NONBLOCK}.
   \end{errlist}
   ed inoltre \macro{EBADF}, \macro{EIO}, \macro{EISDIR}, \macro{EBADF},
     \item \macro{EAGAIN} la funzione non aveva nessun dato da restituire e si
       era aperto il file in modalità \macro{O\_NONBLOCK}.
   \end{errlist}
   ed inoltre \macro{EBADF}, \macro{EIO}, \macro{EISDIR}, \macro{EBADF},
-  \macro{EINVAL} e \macro{EFAULT}.
+  \macro{EINVAL} e \macro{EFAULT} ed eventuali altri errori dipendenti dalla
+  natura dell'oggetto connesso a \var{fd}.
+\end{prototype}
+
+La funzione tenta di leggere \var{count} byte a partire dalla posizione
+corrente nel file; dopo la lettura la posizione è spostata automaticamente in
+avanti del numero di bytes letti. Se \var{count} è zero la funzione
+restituisce zero senza nessun altro risultato.
+
+Si deve sempre tener presente che non è detto che la funzione \func{read}
+restituisca il numero di byte richiesto, ci sono infatti varie ragioni per cui
+la funzione può restituire un numero di byte inferiore. Questo è un
+comportamento normale e non un errore, che però bisogna sempre tenere
+presente.
+
+La prima e più ovvia di queste ragioni è che si è chiesto di leggere più bytes
+di quanto il file ne contenga. In questo caso il file viene letto fino alla
+sua fine, e la funzione ritorna regolarmente il numero di byte letti
+effettivamente. Se ripetessimo la lettura \func{read} restituirebbe uno zero.
+La condizione raggiungimento della fine del file non è un errore, e viene
+segnalata appunto da un valore di ritorno di \func{read} nullo, ripetere la
+lettura non avrebbe nessun effetto se non quello di continuare a ricevere zero
+come valore di ritorno.
+
+Con i \textsl{file regolari} questa è l'unica situazione in cui si può avere
+un numero di byte letti inferiore a quello richiesto, ma la situazione è
+invece normale quando si legge da un terminale, o su una pipe. In tal caso
+infatti, se non ci sono dati in ingresso, la \func{read} si blocca e ritorna
+solo quando ne arrivano; se il numero di byte richiesti eccede quelli
+disponibili la funzione ritorna comunque, ma con un numero di byte inferiore.
+
+Lo stesso comportamento avviene caso di lettura dalla rete (cioè su un socket,
+come vedremo in \secref{sec:sock_io_behav}), o per certi dispositivi come le
+unità a nastro che restituiscono un singolo blocco di dati alla volta.
+
+In realtà anche le due condizioni segnalate dagli errori \func{EINTR} e
+\func{EAGAIN} non sono errori. La prima si verifica quando la \func{read} è
+bloccata in attesa di dati in ingresso e viene interrotta da un segnale; in
+tal caso l'azione da prendere è quella di rieseguire la funzione. Torneremo
+sull'argomento in \secref{sec:signal_xxx}. 
+
+La seconda si verifica quando il file è in modalità non bloccante e non ci
+sono dati in ingresso: la funzione allora ritorna immediatamente con un errore
+\macro{EAGAIN}\footnote{sotto BSD questo per questo errore viene usata la
+  costante \macro{EWOULDBLOCK}, in GNU/Linux questa è sinonima di
+  \macro{EAGAIN}.} indicando che occorrerà provare a ripetere la lettura.
+
+
+Lo standard Unix98\footnote{questa funzione, e l'analoga \func{pwrite} sono
+  state aggiunte nel kernel 2.1.60, il supporto nelle \acr{glibc}, compresa
+  l'emulazione per i vecchi kernel che non hanno la system call, è stato
+  aggiutno con la versione 2.1} (vedi \secref{sec:intro_opengroup}) prevede la
+definizione di un'altra funzione di lettura, \func{pread}, che diventa
+accessibile con la definizione:
+\begin{verbatim}
+       #define _XOPEN_SOURCE 500
+\end{verbatim}
+il prototipo di questa funzione è:
+\begin{prototype}{unistd.h}
+{ssize\_t pread(int fd, void * buf, size\_t count, off\_t offset)}
+  
+La funzione cerca di leggere \var{count} bytes dal file \var{fd}, a partire
+dalla posizione \var{offset}, nel buffer \var{buf}.
+  
+La funzione ritorna il numero di byte letti in caso di successo e -1 in caso
+di errore, nel qual caso \var{errno} viene settata secondo i valori già visti
+per \func{read} e \func{lseek}.
 \end{prototype}
 
 \end{prototype}
 
+Questa funzione serve quando si vogliono leggere dati dal file senza
+modificarne la posizione corrente. È equivalente alla esecuzione di una
+\func{read} e una \func{lseek}, ma dato che la posizione sul file può essere
+condivisa fra vari processi (vedi \secref{sec:file_sharing}), essa permette di
+eseguire l'operazione atomicamente. Il valore di \var{offset} fa riferimento
+all'inizio del file.
+
 
 \subsection{La funzione \func{write}}
 \label{sec:file_write}
 
 
 \subsection{La funzione \func{write}}
 \label{sec:file_write}
 
+Per scrivere su un file si usa la funzione \func{write}, il cui prototipo è:
+\begin{prototype}{unistd.h}{ssize\_t write(int fd, void * buf, size\_t count)}
+  
+  La funzione scrive \var{count} bytes dal buffer \var{buf} sul file \var{fd}.
+  
+  La funzione ritorna il numero di byte scritti 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{EINVAL} \var{fd} è connesso ad un oggetto che non consente la
+    scrittura.
+  \item \macro{EFBIG} si è cercato di scrivere oltre la dimensione massima
+    consentita dal filesystem o il limite per le dimensioni dei file del
+    processo o su una posizione oltre il massimo consentito.
+  \item \macro{EPIPE} \var{fd} è connesso ad una pipe il cui altro capo è
+    chiuso in lettura; in questo caso viene anche generato il segnale
+    \macro{SIGPIPE}, se questo viene gestito (o bloccato o ignorato) la
+    funzione ritorna questo errore.
+  \item \macro{EINTR} la funzione è stata interrotta da un segnale prima di
+    aver potuto scerivere quasiasi dato.
+  \item \macro{EAGAIN} la funzione non aveva nessun dato da restituire e si
+    era aperto il file in modalità \macro{O\_NONBLOCK}.
+  \end{errlist}
+  ed inoltre \macro{EBADF}, \macro{EIO}, \macro{EISDIR}, \macro{EBADF},
+  \macro{ENOSPC}, \macro{EINVAL} e \macro{EFAULT} ed eventuali altri errori
+  dipendenti dalla natura dell'oggetto connesso a \var{fd}.
+\end{prototype}
+
+Come nel caso di \func{read} la funzione tenta di scrivere \var{count} byte a
+partire dalla posizione corrente nel file e sposta automaticamente la
+posizione in avanti del numero di bytes scritti. Se il file è aperto in
+modalità \macro{O\_APPEND} i dati vengono sempre scritti alla fine del file.
+Lo standard POSIX richiede che i dati scritti siano immediatamente disponibili
+ad una \func{read} chiamata dopo che la \func{write} che li ha scritti è
+ritornata; ma dati i meccanismi di caching non è detto che tutti i filesystem
+supportino questa capacità.
+
+Se \var{count} è zero la funzione restituisce zero senza fare nient'altro. Per
+i file ordinari il numero di bytes scritti è sempre uguale a quello indicato
+da \var{count}, a meno di un errore. Negli altri casi si ha lo stesso
+comportamento di \func{read}.
+
+Anche per \func{write} lo standard Unix98 definisce una analoga per scrivere
+alla posizione indicata senza modificare la posizione corrente nel file, il
+suo prototipo è:
+\begin{prototype}{unistd.h}
+{ssize\_t pwrite(int fd, void * buf, size\_t count, off\_t offset)}
+  
+La funzione cerca di scrivere sul file \var{fd}, a partire dalla posizione
+\var{offset}, \var{count} bytes dal buffer \var{buf}.
+  
+La funzione ritorna il numero di byte letti in caso di successo e -1 in caso
+di errore, nel qual caso \var{errno} viene settata secondo i valori già visti
+per \func{write} e \func{lseek}.
+\end{prototype}
 
 
-\section{Funzioni avanzate}
+
+
+\section{Caratteristiche avanzate}
 \label{sec:file_adv_func}
 
 \label{sec:file_adv_func}
 
+In questa sezione approfondireme alcune delle caratteristiche più sottili
+della gestione file in un sistema unix-like, esaminando in dettaglio il
+comportamento delle funzioni base, inoltre tratteremo alcune funzioni che
+permettono di eseguire operazioni avanzate con i file.
+
 
 \subsection{La condivisione dei files}
 \label{sec:file_sharing}
 
 
 \subsection{La condivisione dei files}
 \label{sec:file_sharing}
 
-Si noti che i flag di stato del file, quelli settati dal parametro \var{flag}
-di \func{open}, essendo tenuti nella vode sulla file table, vengono condivisi,
-ai file sono però associati anche altri flag, (tenuti invece nella struttura
-\var{file\_struct} interna alla process table) che sono unici per ciascun file
-descriptor, e sono pertanto detti \textit{file descriptor flags} (l'unico
-usato al momento è \macro{FD\_CLOEXEC}).
+In \secref{sec:file_fd} abbiamo descritto brevemente l'architettura
+dell'interfaccia coi file da parte di un processo, mostrando in
+\figref{fig:file_proc_file} le principali strutture usate dal kernel;
+esamineremo ora in dettaglio le conseguenze che questa architettura ha nei
+confronti dell'accesso allo stesso file da parte di processi diversi.
+
+\begin{figure}[htb]
+  \centering
+  \includegraphics[width=14cm]{img/filemultacc.eps}
+  \caption{Schema dell'accesso allo stesso file da parte di due processi 
+    diversi}
+  \label{fig:file_mult_acc}
+\end{figure}
+
+Il primo caso è quello in cui due processi diversi che aprono lo stesso file
+su disco; sulla base di quanto visto in \secref{sec:file_fd} avremo una
+situazione come quella illustrata in \figref{fig:file_mult_acc}: ciascun
+processo avrà una sua voce nella \textit{file table} referenziata da un
+diverso file descriptor nella sua \var{file\_struct}. Entrambe le voci nella
+\textit{file table} faranno però riferimento allo stesso inode su disco.
+
+Questo significa che ciascun processo avrà la sua posizione corrente sul file,
+la sua modalità di accesso e versioni proprie di tutte le proprietà che
+vengono mantenute nella sua voce della \textit{file table}. Questo ha
+conseguenze specifiche sugli effetti della possibile azione simultanea sullo
+stesso file, in particolare occorre tenere presente che:
+\begin{itemize}
+\item ciascun processo può scrivere indipendentemente; dopo ciascuna
+  \func{write} la posizione corrente sarà cambiata solo nel processo. Se la
+  scrittura eccede la dimensione corrente del file questo verrà esteso
+  automaticamente con l'aggiornamento del campo \var{i\_size} nell'inode.
+\item se un file è in modalità \macro{O\_APPEND} tutte le volte che viene
+  effettuata una scrittura la posizione corrente viene prima settata alla
+  dimensione corrente del file letta dall'inode. In questo modo il file viene
+  automaticamente esteso.
+\item l'effetto di \func{lseek} è solo quello di cambiare il campo \var{f\_pos}
+  nella struttura \var{file} della \textit{file table}, non c'è nessuna
+  operazione sul file su disco. Quando la si usa per porsi alla fine del file
+  la posizione viene settata leggendo la dimensione corrente dall'inode.
+\end{itemize}
+
+\begin{figure}[htb]
+  \centering
+  \includegraphics[width=14cm]{img/fileshar.eps}
+  \caption{Schema dell'accesso ai file da parte di un processo figlio}
+  \label{fig:file_acc_child}
+\end{figure}
+
+È comunque possibile che due file descriptor di due processi diversi puntino
+alla stessa voce nella \textit{file table}; questo è ad esempio il caso dei
+file aperti che venfono ereditati dal processo figlio all'esecuzione di una
+\func{fork} (si ricordi quanto detto in \secref{sec:proc_fork}). La situazione
+è illustrata in \figref{fig:file_acc_child}; dato che il processo figlio
+riceve una copia dello spazio di indirizzi del padre, riceverà anche una copia
+di \var{file\_struct} e relativa tabella dei file aperti. 
+
+In questo modo padre e figlio avranno gli stessi file descriptor che faranno
+riferimento alla stessa voce nella \textit{file table}, condividendo così la
+posizione corrente sul file. Questo ha le cosenguenze descritte a suo tempo in
+\secref{sec:proc_fork}: in caso di scrittura contemporanea la posizione
+corrente nel file varierà per entrambi i processi (in quanto verrà modificato
+\var{f\_pos} che è la stesso per entrambi).
+
+Si noti inoltre che anche i flag di stato del file (quelli settati dal
+parametro \var{flag} di \func{open}) essendo tenuti nella voce della
+\textit{file table} (il campo \var{f\_flag} di \var{file}), vengono in questo
+caso condivisi. Ai file però sono associati anche altri flag (l'unico usato al
+momento è \macro{FD\_CLOEXEC}), detti \textit{file descriptor flags}, tenuti
+invece in \var{file\_struct}; questi sono specifici di ciascun processo, e non
+vengono toccati anche in caso di condivisione della voce della \textit{file
+  table}.
+
+
 
 
 \subsection{Operazioni atomiche coi file}
 \label{sec:file_atomic}
 
 
 
 \subsection{Operazioni atomiche coi file}
 \label{sec:file_atomic}
 
+cvs add 
+
+
 
 \subsection{La funzioni \func{dup} e \func{dup2}}
 \label{sec:file_dup}
 
 
 \subsection{La funzioni \func{dup} e \func{dup2}}
 \label{sec:file_dup}
 
+
+
+\begin{figure}[htb]
+  \centering
+  \includegraphics[width=14cm]{img/filedup.eps}
+  \caption{Schema dell'accesso ai file duplicati}
+  \label{fig:file_dup}
+\end{figure}
+
+
 \subsection{La funzione \func{fcntl}}
 \label{sec:file_fcntl}
 
 \subsection{La funzione \func{fcntl}}
 \label{sec:file_fcntl}
 
diff --git a/img/filedup.dia b/img/filedup.dia
new file mode 100644 (file)
index 0000000..972233c
Binary files /dev/null and b/img/filedup.dia differ
diff --git a/img/filemultacc.dia b/img/filemultacc.dia
new file mode 100644 (file)
index 0000000..aa5a49e
Binary files /dev/null and b/img/filemultacc.dia differ
diff --git a/img/fileshar.dia b/img/fileshar.dia
new file mode 100644 (file)
index 0000000..b1dbfcc
Binary files /dev/null and b/img/fileshar.dia differ
index ff33a21a37343ed81239bd9a0cf44a9c0c8dbbe2..e592f2f634978b84ad570d8beb44d43cd59fcf71 100644 (file)
@@ -17,7 +17,7 @@ durante l'impiego di una applicazione di rete.
 L'applicazione scelta come esempio sarà una implementazione elementare, ma
 completa, del servizio \texttt{echo}. Il servizio \texttt{echo} è uno dei
 servizi standard solitamente provvisti direttamente dal superserver
 L'applicazione scelta come esempio sarà una implementazione elementare, ma
 completa, del servizio \texttt{echo}. Il servizio \texttt{echo} è uno dei
 servizi standard solitamente provvisti direttamente dal superserver
-\texttt{inetd}, ed è definito dall'RFC~862. Come dice il nome il servizio deve
+\cmd{inetd}, ed è definito dall'RFC~862. Come dice il nome il servizio deve
 rimandare indietro sulla connessione i dati che gli vengono inviati; l'RFC
 descrive le specifiche sia per TCP che UDP, e per il primo stabilisce che una
 volta stabilita la connessione ogni dato in ingresso deve essere rimandato in
 rimandare indietro sulla connessione i dati che gli vengono inviati; l'RFC
 descrive le specifiche sia per TCP che UDP, e per il primo stabilisce che una
 volta stabilita la connessione ogni dato in ingresso deve essere rimandato in
@@ -124,7 +124,7 @@ delegata alla funzione \func{ServEcho}.
 Il codice della funzione \func{ServEcho} è invece mostrata in \nfig, la
 comunicazione viene gestita all'interno del ciclo (linee \texttt{\small
   6--8}).  I dati inviati dal client vengono letti dal socket con una semplice
 Il codice della funzione \func{ServEcho} è invece mostrata in \nfig, la
 comunicazione viene gestita all'interno del ciclo (linee \texttt{\small
   6--8}).  I dati inviati dal client vengono letti dal socket con una semplice
-\texttt{read} (che ritorna solo in presenza di dati in arrivo), la riscrittura
+\func{read} (che ritorna solo in presenza di dati in arrivo), la riscrittura
 viene invece gestita dalla funzione \func{SockWrite} (descritta a suo tempo
 in \figref{fig:sock_SockWrite_code}) che si incarica di tenere conto
 automaticamente della possibilità che non tutti i dati di cui è richiesta la
 viene invece gestita dalla funzione \func{SockWrite} (descritta a suo tempo
 in \figref{fig:sock_SockWrite_code}) che si incarica di tenere conto
 automaticamente della possibilità che non tutti i dati di cui è richiesta la
@@ -145,7 +145,7 @@ void ServEcho(int sockfd) {
     return;
 }
   \end{lstlisting}
     return;
 }
   \end{lstlisting}
-  \caption{Codice della prima versione della funzione \texttt{ServEcho} per la
+  \caption{Codice della prima versione della funzione \func{ServEcho} per la
     gestione del servizio \texttt{echo}.}
   \label{fig:TCPsimpl_server_elem_sub}
 \end{figure}
     gestione del servizio \texttt{echo}.}
   \label{fig:TCPsimpl_server_elem_sub}
 \end{figure}
index 360690420e5ca2bf93e15a716fa7acc43a83068c..b8d1a19d44e1f011de06c6fee6cb2d269a2d9ad2 100644 (file)
@@ -728,11 +728,11 @@ socket 
 comportamento che avrebbero con i normali files (in particolare questo accade
 per i socket di tipo stream). 
 
 comportamento che avrebbero con i normali files (in particolare questo accade
 per i socket di tipo stream). 
 
-Infatti con i socket può accadere che funzioni come \func{read} o
-\func{write} possano restituire in input o scrivere in output un numero di
-bytes minore di quello richiesto. Questo è un comportamento normale e non un
-errore, e succede perché si eccede in lettura o scrittura il limite di buffer
-del kernel. 
+Infatti con i socket è comune che funzioni come \func{read} o \func{write}
+possano restituire in input o scrivere in output un numero di bytes minore di
+quello richiesto. Come già accennato in \secref{sec:file_read} questo è un
+comportamento normale anche per l'I/O su file, e succede
+perché si eccede in lettura o scrittura il limite di buffer del kernel.
 
 In questo caso tutto quello che il programma chiamante deve fare è di ripetere
 la lettura (o scrittura) per la quantità di bytes rimanenti (lo stesso può
 
 In questo caso tutto quello che il programma chiamante deve fare è di ripetere
 la lettura (o scrittura) per la quantità di bytes rimanenti (lo stesso può