From dc185040ca2e11aa51540bca530914f6bc11619d Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Wed, 18 Jun 2003 21:19:24 +0000 Subject: [PATCH] Aggiunta figura sul caso di aborto precoce della connessione. con revisione del testo relativo al secondo esempio per il server echo. --- README | 10 --- biblio.bib | 6 +- elemtcp.tex | 117 ++++++++++++++++++++++----------- img/tcp_client_early_abort.dia | Bin 0 -> 2117 bytes listati/TCP_echod.c | 4 +- sources/TCP_echod.c | 14 ++-- 6 files changed, 96 insertions(+), 55 deletions(-) create mode 100644 img/tcp_client_early_abort.dia 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 0000000000000000000000000000000000000000..6c0472ba912036bb5c270e758d64488d514f965c GIT binary patch literal 2117 zcmV-L2)g$liwFP!000001MOW~Z`(E$e&4SU+?NeVX;CCas%=uVS%(!1+M)Ia?8%UA z#?dNEh9t+y%YOR~CB@FgPDPoqoX7#%B$jndk>9y}=a6~#{%RUpmoiC%I6CfNWOpnX zjpA_-O^!Q%^?!RUI`2<@dN&UIH}G$g_)`n+h*I_2apxk-W^cOP#bSZN>(tNU1ckvI zrLy~;0Dy>st+Q8RxglckjDEKUQsmR-;8UAupu`oFxC!j))p^6CS9ReWUmLZ#2o zrhYOBqEb{M{jhip2MNbW!|2|DQ?*Z-B*AzC5N}<&f7c=yXBUI32M=eX%2Qp4 zxZXy@WsnBLP%g(2M49Fs-$=NwB%#*)&V#W`H_hFP%b~u;GEprl7c!V!WDmr+)dw?-ld()T497-xF;BJFH|XyDK==ffm+< z7MPPGYywv{!T>89c>pVdsG}fe3RPNzd<0UYy6t=nQZ{m11SzK$q|6wr0#t>qDnKR7 zg35@cG6buaYN`72DqDjqqU{Bd3kyb#I6>rK%whfCV&n(Ya% z&hw9Z*~usjWR%s&P|oA%=7ZFa*110o!fO~nFqNql$%U1~Q$OlhX?CrG6`1wn#=d&P z>u>qRiqE^z3|5{o6YH60lWQ_dJO8RG1(J?QG?mNAlhJ&-MM%4)ZKb+8K-8R>&;Wwi z(p|gAHtMdm2Kn?6V7uHks|6`D?z#X~;jRl%3A3O&Bo>XiYgZRn6bV!Anj*@DhrwMl zgdJ>qO}Xp->7VWN)tZa>ynXcs4dD8g)j4vi=LU~yfTJ`36IcUJWkO0WZ28p*cFJS~nQ z7+Cv|Pg;$8PoMjuW^k_r<(S719x}=S_qd6O=jUMvHb4F4xrRO4X@J&pKD7qe4G>8lxz%cBu+8 zgEa+)Fn74BDJbrXlcq?DR34AP}jhdq>a#=Y~0Y7;T_6}Ud&f-n8Zqn(%jx!C$c!3jVIZ>KD-f{D(m_ZYB8Lc7vbQ0zWYbeZI2%&5(6rkY+M! zA^1tVq3_m$zAzZ}aOBY(`~<)+5N!he{%9m;ZH#+FTjHJ#v+2rgCh! zsr=AT)D*3vUH!w3L0SdU87@3Ut2ipCz=z(CU2`hVfm8X4y*m9*hPt01n!pr-X)vw~ zIl>n15p5*0?)~#+(EIeI->&FFYbBpI`p<$$uG<4**FirqgMKauV~|&R>bvp+E@1=| z0H%|A)t4oT1~MAwLwsz!>x>_%&fvg0jb^p?TZS+`qA-2yAu!$$4igB68?e|GnCy00Yt2R6%T%zq zoVT0-q|^H^SQmi(a_Y0Vu+P@&^6d}3BV}uia%yD)#8e(Yyu1;R=BWU;bAGW2r8@iE z>wjvQTIsFii_8HWQYQ*BR92;k63=#v$|^(HwLRKUW%cyzYwy#q?aKl7sIb}${%I}q zZ>q1LbO1$b0XP8AfP(_;*8aC%16VB$V21FIA(eo{R|P->*&g7ZH4^|S9_ZWY-)&R@ z)@~xmt(O6q>E2b)XISk4;=*yfoD0I7gBCDPc*GPJbbH8a!T?s?=N#Ce{b7T69X5!X zX$ljtT)X0G1kIgcK!oWPLdO!)hNfME9pX4DW;(a?KJ$o7{q^lNmEp zrvnrf0*1()3W1BL=?XzP;&uMQt!@DiUSWT{f>*~YFf-vyA&RYP1Zv+%6CMG4LHk^R z#v;Cq8iCkJjeyv7&I&N(3T{pdV5;&4a@9}n;kS#;%#X80VxDQz`(qC?}oVg9_}tZtS*O18A*N8Hl0n zxD45XsrEayBiBV3Ql%&V2WE8l%;+YRy80+JM(GzgWymRU%5+S|k8rw)#@D1T4p`kA z7N6*EeN|S;P=tX9#T2UuBPU)QQ@+p$GxpbkH2YUbjsvr+@ literal 0 HcmV?d00001 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 */ -- 2.30.2