From: Simone Piccardi Date: Wed, 18 Jun 2003 21:19:24 +0000 (+0000) Subject: Aggiunta figura sul caso di aborto precoce della connessione. con revisione X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=commitdiff_plain;h=dc185040ca2e11aa51540bca530914f6bc11619d;hp=46e47e7eec85ac93132762fed65b84e4b7bcb047 Aggiunta figura sul caso di aborto precoce della connessione. con revisione del testo relativo al secondo esempio per il server echo. --- diff --git a/README b/README index 5fa758d..e9cca70 100644 --- a/README +++ b/README @@ -44,13 +44,3 @@ PDF pdflatex gapil (prima si devono generare le figure con make) - -Attenzione: a partire da fine Ottobre 2002, per un motivo che non sono -ancora riuscito a capire, viene generato un errore alla fine del file -session.tex. Il codice e` corretto, cio` non ostante latex si ferma ed -occorre farlo proseguire premendo invio. - -NB per un presunto problema di licenza il pacchetto listings e` stato -rimosso da Debian. L'ultima versione dello stesso era stata -profondamente modificata e non funzionava. Pertanto non e` detto che -sia possibile "compilare" Gapil su una Sid. diff --git a/biblio.bib b/biblio.bib index 0f08eb5..3b6cf3b 100644 --- a/biblio.bib +++ b/biblio.bib @@ -33,7 +33,7 @@ @Book{UNP1, author = {W. Richard Stevens}, editor = {}, - title = {UNIX Network Programming}, + title = {UNIX Network Programming, volume 1}, publisher = {Prentice Hall PTR}, year = {1998}, OPTkey = {}, @@ -48,8 +48,8 @@ } @Book{UNP2, author = {W. Richard Stevens}, - editor = {UNIX Network Programming}, - title = {}, + editor = {}, + title = {UNIX Network Programming, volume 2}, publisher = {Prentice Hall PTR}, year = {1998}, OPTkey = {}, diff --git a/elemtcp.tex b/elemtcp.tex index 1962edd..585f605 100644 --- a/elemtcp.tex +++ b/elemtcp.tex @@ -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 @@ -2075,6 +2082,23 @@ una riscrittura parziale del server, secondo quanto mostrato in \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 nella nuova versione del server, rispetto alla +versione precedente vista in \figref{fig:TCP_ServEcho}, è nella sezione +(\texttt{\small 43--48}) in cui si effettua la chiamata di \func{accept}. +Quest'ultima allora viene effettuata (\texttt{\small 43--44}) 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 45--48}), altrimenti il +programma prosegue esattamente allo stesso modo del precedente. @@ -2083,11 +2107,30 @@ 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. + +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}; questo significa che, oltre al caso di interruzione +da parte di un segnale, esistono altri errori non fatali che necessitano +semplicemente la ripetizione della chiamata senza che si debba uscire dal +programma. Un esempio possibile è quello mostrato in +\figref{fig:TCP_early_abort}, in cui la connessione viene abortita dal client +con l'invio di un segmento RST prima che 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} diff --git a/img/tcp_client_early_abort.dia b/img/tcp_client_early_abort.dia new file mode 100644 index 0000000..6c0472b Binary files /dev/null and b/img/tcp_client_early_abort.dia differ diff --git a/listati/TCP_echod.c b/listati/TCP_echod.c index 81085a7..3f17baa 100644 --- a/listati/TCP_echod.c +++ b/listati/TCP_echod.c @@ -40,7 +40,9 @@ int main(int argc, char *argv[]) exit(1); } while (1) { /* handle echo to client */ - if ( (conn_fd = accept(list_fd, NULL, NULL)) < 0) { + while (((conn_fd = accept(list_fd, NULL, NULL)) < 0) + && (errno == EINTR)); /* accept connection */ + if ( conn_fd < 0) { PrintErr("accept error"); exit(1); } diff --git a/sources/TCP_echod.c b/sources/TCP_echod.c index 3cc5ba3..08d926f 100644 --- a/sources/TCP_echod.c +++ b/sources/TCP_echod.c @@ -26,7 +26,7 @@ * * Usage: echod -h give all info * - * $Id: TCP_echod.c,v 1.3 2003/05/12 22:52:29 piccardi Exp $ + * $Id: TCP_echod.c,v 1.4 2003/06/18 21:19:24 piccardi Exp $ * ****************************************************************/ /* @@ -40,12 +40,13 @@ #include #include /* syslog system functions */ #include /* signal functions */ +#include /* error code */ #include "Gapil.h" #define BACKLOG 10 #define MAXLINE 256 -int demonize = 1; /* daemon use option */ -int debugging = 0; /* debug info printing option */ +int demonize = 1; /* daemon use option: default is daemon */ +int debugging = 0; /* debug info printing option: default is no debug */ /* Subroutines declaration */ void usage(void); void ServEcho(int sockfd); @@ -57,6 +58,7 @@ int main(int argc, char *argv[]) * Variables definition */ int list_fd, conn_fd; + int waiting; pid_t pid; struct sockaddr_in serv_add; /* @@ -65,7 +67,7 @@ int main(int argc, char *argv[]) */ int i; opterr = 0; /* don't want writing to stderr */ - while ( (i = getopt(argc, argv, "hdi")) != -1) { + while ( (i = getopt(argc, argv, "hdiw:")) != -1) { switch (i) { /* * Handling options @@ -81,6 +83,9 @@ int main(int argc, char *argv[]) case 'd': debugging = 1; break; + case 'w': + waiting = strtol(optarg, NULL, 10); + break; case '?': /* unrecognized options */ printf("Unrecognized options -%c\n",optopt); usage(); @@ -133,6 +138,7 @@ int main(int argc, char *argv[]) PrintErr("listen error"); exit(1); } + if (waiting) sleep(waiting); /* handle echo to client */ while (1) { /* accept connection */