X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=elemtcp.tex;h=70caf59463babdf922a02ac50eeca7eb94622cbe;hp=1962eddd978795e35433e4d043acc23a0126823e;hb=9b316c16855b494012c49ee1f6ec6d435ae148cd;hpb=13e1dfef7b970db44781986970292e62ad7c7c58 diff --git a/elemtcp.tex b/elemtcp.tex index 1962edd..70caf59 100644 --- a/elemtcp.tex +++ b/elemtcp.tex @@ -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", @@ -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}.} @@ -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,45 @@ 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{Sezione del codice seconda versione del server + per il servizio \textit{echo} modificate rispetto alla prima.} \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 +2115,65 @@ 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. + +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 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 a +rieseguire 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. + +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. Per riprodurre l'errore occorrerebbe usare +l'opzione \const{SO\_LINGER} (vedi \secref{sec:xxx}) per chiedere al client di +chiudere il socket con un segmento RST, invece che con la normale sequenza di +chiusura. + + +