X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=elemtcp.tex;h=585f6053803cc296a121b2e9fee9408a76ff069a;hp=534dd398f18819943c1811ed5bbb63fcf5d556ab;hb=dc185040ca2e11aa51540bca530914f6bc11619d;hpb=7444bbe1f4d1e9858693bfcb41921fa601450a89 diff --git a/elemtcp.tex b/elemtcp.tex index 534dd39..585f605 100644 --- a/elemtcp.tex +++ b/elemtcp.tex @@ -1711,23 +1711,23 @@ illustriamo immediatamente. \subsection{Il server: prima versione} \label{sec:TCPsimp_server_main} -La prima versione del server, contenuta nel file \file{TCP\_echod.c}, è -riportata in \figref{fig:TCP_echo_server_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 esempio -per il server del servizio \textit{daytime} di +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 +esempio per il server del servizio \textit{daytime} di \secref{sec:TCP_daytime_cunc_server}, e da una funzione ausiliaria \code{ServEcho} che si cura della gestione del servizio. \begin{figure}[!htbp] \footnotesize \centering \begin{minipage}[c]{15.6cm} - \includecodesample{listati/TCP_echod.c} + \includecodesample{listati/TCP_echod_first.c} \end{minipage} \normalsize \caption{Codice del corpo principale della prima versione del server per il servizio \textit{echo}.} - \label{fig:TCP_echo_server_code} + \label{fig:TCP_echo_server_first_code} \end{figure} In questo caso però, rispetto a quanto visto nell'esempio di @@ -1997,22 +1997,140 @@ 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_code}. +all'esempio illustrato in \figref{fig:TCP_echo_server_first_code}. + +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} 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 + \begin{minipage}[c]{15.6cm} + \includecodesample{listati/SignalRestart.c} + \end{minipage} + \normalsize + \caption{La funzione \funcd{SignalRestart}, che installa un gestore di + 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 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 +questo corrisponda ad \errcode{EINTR}. Questa soluzione ha però il pregio +della portabilità, infatti lo standard POSIX dice che la funzionalità di +riavvio automatico delle system call, fornita da \const{SA\_RESTART}, è +opzionale, per cui non è detto che essa sia disponibile su qualunque sistema. +Inoltre in certi casi,\footnote{Stevens in \cite{UNP1} accenna che la maggior + parte degli Unix derivati da BSD non fanno ripartire \func{select}, ed + alcuni non fanno ripartire neanche \func{accept} e \func{recvfrom}; nel caso + 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}. + +\begin{figure}[!htbp] + \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}.} + \label{fig:TCP_echo_server_code} +\end{figure} -In questo modo però si introduce un altro problema, +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. -\subsection{I vari scenari critici} + +\section{I vari scenari critici} \label{sec:TCP_echo_critical} +Con le modifiche viste in \secref{sec:TCP_child_hand} il nostro esempio +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}