Modifiche varie per i test delle situazioni limite
[gapil.git] / elemtcp.tex
index 4ad16ac30f6bf9a0be5fad975802d02111e8d871..1ae67f4a24b6bc29b1806b2b3e737b08ac5c01dc 100644 (file)
@@ -1,6 +1,6 @@
 %% elemtcp.tex
 %%
-%% Copyright (C) 2000-2002 Simone Piccardi.  Permission is granted to
+%% Copyright (C) 2000-2003 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 "Prefazione",
@@ -1053,12 +1053,12 @@ errori rispetto ad altre implementazioni dei socket BSD, infatti la funzione
 \func{accept} passa gli errori di rete pendenti sul nuovo socket come codici
 di errore per \func{accept}, per cui l'applicazione deve tenerne conto ed
 eventualmente ripetere la chiamata alla funzione come per l'errore di
-\errcode{EAGAIN}. Un'altra differenza con BSD è che la funzione non fa
-ereditare al nuovo socket i flag del socket originale, come
-\const{O\_NONBLOCK},\footnote{ed in generale tutti quelli che si possono
-  impostare con \func{fcntl}, vedi \secref{sec:file_fcntl}.} che devono essere
-rispecificati ogni volta. Tutto questo deve essere tenuto in conto se si
-devono scrivere programmi portabili.
+\errcode{EAGAIN} (torneremo su questo in \secref{sec:TCP_echo_critical}).
+Un'altra differenza con BSD è che la funzione non fa ereditare al nuovo socket
+i flag del socket originale, come \const{O\_NONBLOCK},\footnote{ed in generale
+  tutti quelli che si possono impostare con \func{fcntl}, vedi
+  \secref{sec:file_fcntl}.} che devono essere rispecificati ogni volta. Tutto
+questo deve essere tenuto in conto se si devono scrivere programmi portabili.
 
 Il meccanismo di funzionamento di \func{accept} è essenziale per capire il
 funzionamento di un server: in generale infatti c'è sempre un solo socket in
@@ -1328,7 +1328,7 @@ Il primo passo (\texttt{\small 14--18}) 
 \func{socket} ritorna il descrittore che viene usato per identificare il
 socket in tutte le chiamate successive. Nel caso la chiamata fallisca si
 stampa un errore (\texttt{\small 16}) con la funzione \func{perror} e si esce
-(\texttt{\small 16}) con un codice di errore.
+(\texttt{\small 17}) con un codice di errore.
 
 Il passo seguente (\texttt{\small 19--27}) è quello di costruire un'apposita
 struttura \struct{sockaddr\_in} in cui sarà inserito l'indirizzo del server ed
@@ -1635,7 +1635,7 @@ output.
 \label{sec:TCP_echo_client}
 
 Il codice della prima versione del client per il servizio \textit{echo},
-disponibile nel file \file{TCP\_echo1.c}, è riportato in
+disponibile nel file \file{TCP\_echo\_first.c}, è riportato in
 \figref{fig:TCP_echo_client_1}. Esso ricalca la struttura del precedente
 client per il servizio \textit{daytime} (vedi
 \secref{sec:TCP_daytime_client}), e la prima parte (\texttt{\small 10--27}) è
@@ -1644,7 +1644,7 @@ sostanzialmente identica, a parte l'uso di una porta diversa.
 \begin{figure}[!htb]
   \footnotesize \centering
   \begin{minipage}[c]{15.6 cm}
-    \includecodesample{listati/TCP_echo1.c}
+    \includecodesample{listati/TCP_echo_first.c}
   \end{minipage} 
   \normalsize
   \caption{Codice della prima versione del client \textit{echo}.}
@@ -1711,7 +1711,7 @@ illustriamo immediatamente.
 \subsection{Il server: prima versione}
 \label{sec:TCPsimp_server_main}
 
-La prima versione del server, contenuta nel file \file{TCP\_echod_first.c}, è
+La prima versione del server, contenuta nel file \file{TCP\_echod\_first.c}, è
 riportata in \figref{fig:TCP_echo_server_first_code}. Come abbiamo fatto per
 il client anche il server è stato diviso in un corpo principale, costituito
 dalla funzione \code{main}, che è molto simile a quello visto nel precedente
@@ -1997,39 +1997,45 @@ ricevere opportunamente lo stato di terminazione del processo (si veda
 quanto illustrato in \secref{sec:sig_sigchld}. Una prima modifica al nostro
 server è pertanto quella di inserire la gestione della terminazione dei
 processi figli attraverso l'uso di un gestore.  Per questo useremo la funzione
-\code{Signal} della nostra libreria personale, che abbiamo illustrato in
-\figref{fig:sig_Signal_code}, per installare il gestore che riceve i segnali
-dei processi figli terminati già visto in \figref{fig:sig_sigchld_handl}.
-Basterà allora aggiungere il seguente codice:
-\includecodesnip{listati/sigchildhand.c}
+\code{Signal} (che abbiamo illustrato in \figref{fig:sig_Signal_code}), per
+installare il gestore che riceve i segnali dei processi figli terminati già
+visto in \figref{fig:sig_sigchld_handl}.  Basterà allora aggiungere il
+seguente codice: \includecodesnip{listati/sigchildhand.c}
 \noindent
 all'esempio illustrato in \figref{fig:TCP_echo_server_first_code}.
 
-In questo modo però si introduce un altro problema, il fatto che, come
-spiegato in \secref{sec:sig_gen_beha}, quando un programma si trova in stato
-di \texttt{sleep} durante l'esecuzione di una system call, questa viene
-interrotta alla ricezione di un segnale, per cui alla fine dell'esecuzione del
-gestore il programma, se questo ritorna, l'esecuzione del programma riprenderà
-con l'uscita dalla system call con un errore di \errcode{EINTR}. 
-
-Questo comporta che quando si chiude il client, con la terminazione del
-processo figlio, il padre, che riceve il segnale ed esegue il gestore,
-ritornerà dalla \func{accept} (a meno di un caso fortuito in cui il segnale
-arriva durante l'esecuzione del programma in risposta ad una connessione) con
-un errore di \errcode{EINTR}, terminando a sua volta con un errore del tipo:
+In questo modo però si introduce un altro problema, si ricordi infatti che,
+come spiegato in \secref{sec:sig_gen_beha}, quando un programma si trova in
+stato di \texttt{sleep} durante l'esecuzione di una system call, questa viene
+interrotta alla ricezione di un segnale. Per questo motivo, alla fine
+dell'esecuzione del gestore del segnale, se questo ritorna, il programma
+riprenderà l'esecuzione ritornando dalla system call con un errore di
+\errcode{EINTR}.
+
+Vediamo allora cosa comporta tutto questo nel nostro caso: quando si chiude il
+client, il processo figlio che gestisce la connessione terminerà, ed il padre,
+per evitare la creazione di zombie, riceverà il segnale \const{SIGCHLD}
+eseguendo il relativo gestore. Al ritorno del gestore però l'esecuzione nel
+padre ripartirà subito con il ritorno della funzione \func{accept} (a meno di
+un caso fortuito in cui il segnale arriva durante l'esecuzione del programma
+in risposta ad una connessione) con un errore di \errcode{EINTR}. Non avendo
+previsto questa eventualità il programma considera questo un errore fatale
+terminando a sua volta con un messaggio del tipo:
 \begin{verbatim}
 [root@gont sources]# ./echod -i
 accept error: Interrupted system call
 \end{verbatim}%#
 
 
-Come accennato in \secref{sec:sig_gen_beha} questo ci mette di fronte a due
-possibili soluzioni, la più semplice è quella di modificare il codice di
-\func{Signal} per richiedere il riavvio automatico delle system call
-interrotte secondo la semantica di BSD, usando l'opzione \const{SA\_RESTART}
-di \func{sigaction}; rispetto a quanto visto in \figref{fig:sig_Signal_code}
-definiremo allora la nuova funzione \func{SignalRestart} come mostrato in
-\figref{fig:sig_SignalRestart_code}.
+Come accennato in \secref{sec:sig_gen_beha} le conseguenze di questo
+comportamento delle system call possono essere superate in due modi diversi,
+il più semplice è quello di modificare il codice di \func{Signal} per
+richiedere il riavvio automatico delle system call interrotte secondo la
+semantica di BSD, usando l'opzione \const{SA\_RESTART} di \func{sigaction};
+rispetto a quanto visto in \figref{fig:sig_Signal_code}. Definiremo allora la
+nuova funzione \func{SignalRestart} come mostrato in
+\figref{fig:sig_SignalRestart_code}, ed installeremo il gestore usando
+quest'ultima.
 
 \begin{figure}[!htb]
   \footnotesize  \centering
@@ -2038,17 +2044,18 @@ definiremo allora la nuova funzione \func{SignalRestart} come mostrato in
   \end{minipage}  
   \normalsize 
   \caption{La funzione \funcd{SignalRestart}, che installa un gestore di
-    segnali in semantica BSD per il riavvio delle system call interrotte.}
+    segnali in semantica BSD per il riavvio automatico delle system call
+    interrotte.}
   \label{fig:sig_SignalRestart_code}
 \end{figure}
 
-Come si può notare questa funzione è identica a \func{Signal}, solo che in
-questo caso invece di inizializzare a zero il campo \var{sa\_flags} di
-\struct{sigaction}, lo si inizializza (\texttt{\small 5}) al valore
-\const{SA\_RESTART}. Usando questa funzione al posto di \func{Signal} nel
-server non è necessaria nessuna altra modifica: le system call interrotte
-saranno automaticamente riavviate, e l'errore \errcode{EINTR} non si
-manifesterà più.
+Come si può notare questa funzione è identica alla precedente \func{Signal},
+solo che in questo caso invece di inizializzare a zero il campo
+\var{sa\_flags} di \struct{sigaction}, lo si inizializza (\texttt{\small 5})
+al valore \const{SA\_RESTART}. Usando questa funzione al posto di
+\func{Signal} nel server non è necessaria nessuna altra modifica: le system
+call interrotte saranno automaticamente riavviate, e l'errore \errcode{EINTR}
+non si manifesterà più.
 
 La seconda soluzione è più invasiva e richiede di controllare tutte le volte
 l'errore restituito dalle varie system call, ripetendo la chiamata qualora
@@ -2062,20 +2069,46 @@ Inoltre in certi casi,\footnote{Stevens in \cite{UNP1} accenna che la maggior
   di Linux questa è disponibile.} anche quando questa è presente, non è detto
 possa essere usata con \func{accept}. La portabilità però viene al costo di
 una riscrittura parziale del server, secondo quanto mostrato in
-\figref{fig:TCP_echo_server_code}.
+\figref{fig:TCP_echo_server_code}, dove si è riportata la sezione di codice
+modificata per la seconda versione del programma.
 
-\begin{figure}[!htbp]
+\begin{figure}[!htb]
   \footnotesize \centering
   \begin{minipage}[c]{15.6cm}
     \includecodesample{listati/TCP_echod.c}
   \end{minipage} 
   \normalsize
-  \caption{Codice del corpo principale della seconda versione del server
-    per il servizio \textit{echo}.}
+  \caption{La sezione nel codice della seconda versione del server
+    per il servizio \textit{echo} modificata per tener conto dell'interruzione
+    delle system call.}
   \label{fig:TCP_echo_server_code}
 \end{figure}
 
-
+In realtà l'unica chiamata critica che può essere interrotta nel server è
+quella ad \func{accept}, dato che questa è l'unica che può mettere il processo
+padre in stato di sleep.\footnote{si noti infatti che le altre \textit{slow
+    system call} o sono chiamate prima di entrare nel ciclo principale, quando
+  ancora non esistono processi figli, o sono chiamate dai figli stessi.}  Per
+questo l'unica modifica sostanziale nella nuova versione del server, rispetto
+alla versione precedente vista in \figref{fig:TCP_ServEcho}, è nella sezione
+(\texttt{\small 9--14}) in cui si effettua la chiamata di \func{accept}.
+Quest'ultima viene effettuata (\texttt{\small 9--10}) all'interno di un ciclo
+di \code{while}\footnote{la sintassi del C relativa a questo ciclo può non
+  essere del tutto chiara. In questo caso infatti si è usato un ciclo vuoto
+  che non esegue nessuna istruzione, in questo modo quello che viene ripetuto
+  con il ciclo è soltanto il codice che esprime la condizione all'interno del
+  \code{while}.}  che la ripete indefinitamente qualora in caso di errore il
+valore di \var{errno} sia \errcode{EINTR}. Negli altri casi si esce in caso di
+errore effettivo (\texttt{\small 11--14}), altrimenti il programma prosegue.
+
+Si noti che in questa nuova versione si è aggiunta una ulteriore sezione
+(\texttt{\small 15--28}) per il debugging, che stampa l'indirizzo da cui si è
+avuta la connessione, e la gestione (\texttt{\small 5}) di una nuova opzione,
+specificabile con \code{-w Nsec}, che inizializza la variabile \var{waiting}
+al numero di secondi da aspettare. Questa opzione permette di specificare un
+tempo di attesa prima di passare alla chiamata \func{accept}, il cui scopo
+sarà più chiaro facendo riferimento al primo degli scenari critici di
+\secref{sec:TCP_echo_critical}.
 
 
 
@@ -2083,11 +2116,87 @@ una riscrittura parziale del server, secondo quanto mostrato in
 \label{sec:TCP_echo_critical}
 
 Con le modifiche viste in \secref{sec:TCP_child_hand} il nostro esempio
-diventa in grado di affrontare correttamente la gestione ordinaria delle
-connessioni, ma un server di rete deve tenere conto che, al contrario di
-quanto avviene per i server che operano nei confronti di processi presenti
-sulla stessa macchina, la rete è di sua natura inaffidabile, per cui è
-necessario essere in grado di gestire tutta una serie di situazioni critiche.
+diventa in grado di affrontare la gestione ordinaria delle connessioni, ma un
+server di rete deve tenere conto che, al contrario di quanto avviene per i
+server che operano nei confronti di processi presenti sulla stessa macchina,
+la rete è di sua natura inaffidabile, per cui è necessario essere in grado di
+gestire tutta una serie di situazioni critiche che non esistono per i processi
+locali.
+
+
+\subsection{La terminazione precoce della connessione}
+\label{sec:TCP_conn_early_abort}
+
+La prima situazione critica è quella della terminazione precoce, causata da un
+qualche errore sulla rete, della connessione effettuata da un client. Come
+accennato in \secref{sec:TCP_func_accept} la funzione \func{accept} riporta
+tutti gli eventuali errori di rete pendenti su una connessione sul
+\textit{connected socket}. Di norma questo non è un problema, in quanto non
+appena completata la connessione, \func{accept} ritorna e l'errore sarà
+rilevato in seguito, dal processo che gestisce la connessione, alla prima
+chiamata di una funzione che opera sul socket.
+
+È però possibile, dal punto di vista teorico, incorrere anche in uno scenario
+del tipo di quello mostrato in \figref{fig:TCP_early_abort}, in cui la
+connessione viene abortita sul lato client per un qualche errore di rete con
+l'invio di un segmento RST, prima che nel server sia stata chiamata la
+funzione \func{accept}.
+
+\begin{figure}[htb]
+  \centering
+  \includegraphics[width=10cm]{img/tcp_client_early_abort}  
+  \caption{Un possibile caso di terminazione precoce della connessione.}
+  \label{fig:TCP_early_abort}
+\end{figure}
+
+Benché questo non sia un fatto comune, un evento simile può essere osservato
+con dei server molto occupati. In tal caso, con una struttura del server
+simile a quella del nostro esempio, in cui la gestione delle singole
+connessioni è demandata a processi figli, può accadere che il three way
+handshake venga completato e la relativa connessione abortita subito dopo,
+prima che il padre, per via del carico della macchina, abbia fatto in tempo ad
+eseguire la chiamata ad \func{accept}. Di nuovo si ha una situazione analoga
+a quella illustrata in \figref{fig:TCP_early_abort}, in cui la connessione
+viene stabilita, ma subito dopo si ha una condizione di errore che la chiude
+prima che essa sia stata accettata dal programma.
+
+Questo significa che, oltre alla interruzione da parte di un segnale, che
+abbiamo trattato in \secref{sec:TCP_child_hand} nel caso particolare di
+\const{SIGCHLD}, si possono ricevere altri errori non fatali all'uscita di
+\func{accept}, che come nel caso precedente, necessitano semplicemente la
+ripetizione della chiamata senza che si debba uscire dal programma. In questo
+caso anche la versione modificata del nostro server non sarebbe adatta, in
+quanto uno di questi errori causerebbe la terminazione dello stesso. In Linux
+i possibili errori di rete non fatali, riportati sul socket connesso al
+ritorno di \func{accept}, sono \errcode{ENETDOWN}, \errcode{EPROTO},
+\errcode{ENOPROTOOPT}, \errcode{EHOSTDOWN}, \errcode{ENONET},
+\errcode{EHOSTUNREACH}, \errcode{EOPNOTSUPP} e \errcode{ENETUNREACH}.
+
+Si tenga presente che questo tipo di terminazione non è riproducibile
+terminando il client prima della chiamata ad \func{accept}; in tal caso
+infatti il socket associato alla connessione viene semplicemente chiuso,
+attraverso la sequenza vista in \secref{sec:TCP_conn_term}, per cui la
+\func{accept} ritornerà senza errori, e si avrà semplicemente un end-of-file
+al primo accesso al socket. Nel caso di Linux inoltre anche l'invio da parte
+del client di un segmento di RST non provoca nessun errore al ritorno di
+\funcd{accept} quanto un errore di \errcode{ECONNRESET} al primo tentativo di
+accesso al socket.
+
+
+
+\subsection{Il crollo del server}
+\label{sec:TCP_server_crash}
+
+Un secondo caso critico è quello in cui si ha il crollo del server. In tal
+caso si suppone che il processo del server termini per un errore fatale, cosa
+che potremo simulare inviandogli un segnale di terminazione. La conclusione
+del processo comporta la chiusura di tutti i file aperti, compresi tutti i
+socket relativi a connessioni stabilite; questo significa che al momento del
+crollo del servizio il client riceverà un FIN dal server in corrispondenza
+della chiusura del socket.
+
+
+