Risistemati gli esempi per sleep e fissata una referenza
[gapil.git] / signal.tex
index fec20a72c0d0465508faae3bda50031b4a894bc8..be01dc4ec8304c0b992281a202f3c5aa056a6633 100644 (file)
@@ -19,6 +19,7 @@ di generazione fino ad esaminare in dettaglio funzioni e le metodologie di
 gestione.
 
 
 gestione.
 
 
+
 \section{Introduzione}
 \label{sec:sig_intro}
 
 \section{Introduzione}
 \label{sec:sig_intro}
 
@@ -1442,42 +1443,42 @@ void Hand_CHLD(int sig)
 \end{figure}
 
 Il codice del manipolatore è di lettura immediata; come buona norma di
 \end{figure}
 
 Il codice del manipolatore è di lettura immediata; come buona norma di
-programmazione (si ricordi quanto accennato \secref{sec:sys_errno}) si comincia
-(\texttt{\small 13}) con il salvare lo stato corrente di \var{errno}, in modo
-da poterla ripristinare prima del ritorno del manipolatore (\texttt{\small
-  23}). In questo modo si preserva il valore della variabile visto dal corso
-di esecuzione principale del processo, che sarebbe altrimenti sarebbe
-sovrascritto dal valore restituito nella successiva chiamata di \func{wait}.
+programmazione (si ricordi quanto accennato \secref{sec:sys_errno}) si
+comincia (\texttt{\small 12-13}) con il salvare lo stato corrente di
+\var{errno}, in modo da poterlo ripristinare prima del ritorno del
+manipolatore (\texttt{\small 22-23}). In questo modo si preserva il valore
+della variabile visto dal corso di esecuzione principale del processo, che
+sarebbe altrimenti sarebbe sovrascritto dal valore restituito nella successiva
+chiamata di \func{wait}.
 
 Il compito principale del manipolatore è quello di ricevere lo stato di
 terminazione del processo, cosa che viene eseguita nel ciclo in
 
 Il compito principale del manipolatore è quello di ricevere lo stato di
 terminazione del processo, cosa che viene eseguita nel ciclo in
-(\texttt{\small 15--21}).  Il ciclo è necessario a causa di una caratteristica
+(\texttt{\small 15-21}).  Il ciclo è necessario a causa di una caratteristica
 fondamentale della gestione dei segnali: abbiamo già accennato come fra la
 generazione di un segnale e l'esecuzione del manipolatore possa passare un
 fondamentale della gestione dei segnali: abbiamo già accennato come fra la
 generazione di un segnale e l'esecuzione del manipolatore possa passare un
-certo lasso di tempo; dato che questo lasso di tempo può dipendere da parecchi
-fattori esterni, niente ci assicura che il manipolatore venga eseguito prima
-della generazione di altri segnali dello stesso tipo. In questo caso
-normalmente i segnali vengono ``fusi'' insieme ed al processo ne viene
-recapitato soltanto uno.
-
-Questo può essere un caso comune proprio con \texttt{SIGCHLD}, quando molti
-processi figli terminano in rapida successione. Esso comunque si presenta
-tutte le volte che un segnale viene bloccato: per quanti siano i segnali
-emessi durante il periodo di blocco, una volta che esso viene rimosso ne sarà
-recapitato uno solo.
-
-Nel caso della terminazione dei processi figli, se si chiamasse \func{waitpid}
-una sola volta, essa leggerebbe un solo stato di teminazione, anche se i
-processi terminati sono più di uno, con relativa possibilità di avere zombie
-che non vengono eliminati. 
-
-Per questo si esegue un ciclo (\texttt{\small 15--21}) in cui ripete la
-lettura fintanto che essa non restituisce un valore nullo (si veda
-\secref{sec:proc_wait} per la sintassi della funzione) segno che non resta
-nessun processo di cui si debba ancora ricevere lo stato di terminazione. Si
-noti come la funzione viene invocata con il parametro \macro{WNOHANG} che
-permette di evitare che essa si blocchi quando tutti gli stati di terminazione
-sono stati ricevuti.
+certo lasso di tempo e niente ci assicura che il manipolatore venga eseguito
+prima della generazione di ulteriori segnali dello stesso tipo. In questo caso
+normalmente i segnali segnali successivi vengono ``fusi'' col primo ed al
+processo ne viene recapitato soltanto uno.
+
+Questo può essere un caso comune proprio con \macro{SIGCHLD}, qualora capiti
+che molti processi figli terminino in rapida successione. Esso inoltre si
+presenta tutte le volte che un segnale viene bloccato: per quanti siano i
+segnali emessi durante il periodo di blocco, una volta che quest'ultimo sarà
+rimosso sarà recapitato un solo segnale.
+
+Allora nel caso della terminazione dei processi figli, se si chiamasse
+\func{waitpid} una sola volta, essa leggerebbe lo stato di teminazione per un
+solo processo, anche se i processi terminati sono più di uno, e gli altri
+resterebbero in stato di zombie per un tempo indefinito.
+
+Per questo occorre ripetere la chiamata di \func{waitpid} fino a che essa non
+ritorni un valore nullo, segno che non resta nessun processo di cui si debba
+ancora ricevere lo stato di terminazione (si veda \secref{sec:proc_wait} per
+la sintassi della funzione). Si noti anche come la funzione venga invocata con
+il parametro \macro{WNOHANG} che permette di evitare il suo blocco quando
+tutti gli stati di terminazione sono stati ricevuti.
+
 
 
 \section{Gestione avanzata}
 
 
 \section{Gestione avanzata}
@@ -1490,7 +1491,9 @@ che i segnali possono generare e alla natura asincrona degli stessi.
 
 Affronteremo queste problematiche in questa sezione, partendo da un esempio
 che le evidenzi, per poi prendere in esame le varie funzioni che permettono di
 
 Affronteremo queste problematiche in questa sezione, partendo da un esempio
 che le evidenzi, per poi prendere in esame le varie funzioni che permettono di
-risolvere i problemi più complessi connessi alla programmazione con i segnali.
+risolvere i problemi più complessi connessi alla programmazione con i segnali,
+fino a trattare le caratteristiche generali della gestione dei medesimi nella
+casistica ordinaria.
 
 
 \subsection{Un esempio di problema}
 
 
 \subsection{Un esempio di problema}
@@ -1499,8 +1502,20 @@ risolvere i problemi pi
 Come accennato in \secref{sec:sig_pause_sleep} è possibile implementare
 \func{sleep} a partire da dall'uso di \func{pause} e \func{alarm}. A prima
 vista questo può sembrare di implementazione immediata; ad esempio una
 Come accennato in \secref{sec:sig_pause_sleep} è possibile implementare
 \func{sleep} a partire da dall'uso di \func{pause} e \func{alarm}. A prima
 vista questo può sembrare di implementazione immediata; ad esempio una
-semplice versione di \func{sleep} potrebbe essere la seguente quella
-illustrata in \ref{fig:sig_sleep_wrong}.
+semplice versione di \func{sleep} potrebbe essere quella illustrata in
+\figref{fig:sig_sleep_wrong}.
+
+
+Dato che è nostra intenzione utilizzare \macro{SIGALARM} il primo passo della
+nostra implementazione di sarà quello di installare il relativo manipolatore
+salvando il precedente (\texttt{\small 4-7}).  Si effettuerà poi una chiamata
+ad \func{alarm} per specificare il tempo d'attesa per l'invio del segnale a
+cui segue la chiamata a \func{pause} per fermare il programma (\texttt{\small
+  8-9}) fino alla sua ricezione.  Al ritorno di \func{pause}, causato dal
+ritorno del manipolatore (\texttt{\small 15-23}), si ripristina il
+manipolatore originario (\texttt{\small 10-11}) restituendo l'eventuale tempo
+rimanente (\texttt{\small 12-13}) che potrà essere diverso da zero qualora
+l'interruzione di \func{pause} venisse causata da un altro segnale.
 
 \begin{figure}[!htb]
   \footnotesize \centering
 
 \begin{figure}[!htb]
   \footnotesize \centering
@@ -1532,21 +1547,77 @@ void alarm_hand(int sig) {
     \end{lstlisting}
   \end{minipage} 
   \normalsize 
     \end{lstlisting}
   \end{minipage} 
   \normalsize 
-  \caption{Una implementazione sbagliata di \func{sleep}.} 
+  \caption{Una implementazione pericolosa di \func{sleep}.} 
   \label{fig:sig_sleep_wrong}
 \end{figure}
 
   \label{fig:sig_sleep_wrong}
 \end{figure}
 
-Ma questa funzione, a parte il non gestire il caso in cui si è avuta una
-precedente chiamata a \func{alarm}, presenta una pericolosa race condition.
-Infatti se il processo viene interrotto fra la chiamata di \func{alarm} e
-\func{pause} può capitare (nel caso il sistema sia molto carico) che
-quest'ultima possa essere eseguita dopo l'arrivo di \macro{SIGALRM}. In questo
-caso ci si troverebbe di fronte ad un deadlock, in cui \func{pause} non
-verrebbe mai interrotta (se non in caso di un altro segnale).
+Questo codice però, a parte il non gestire il caso in cui si è avuta una
+precedente chiamata a \func{alarm} (che si è tralasciato per brevità),
+presenta una pericolosa race condition.  Infatti se il processo viene
+interrotto fra la chiamata di \func{alarm} e \func{pause} può capitare (ad
+esempio se il sistema è molto carico) che il tempo di attesa scada prima
+dell'esecuzione quest'ultima, cosicchè essa sarebbe eseguita dopo l'arrivo di
+\macro{SIGALRM}. In questo caso ci si troverebbe di fronte ad un deadlock, in
+quanto \func{pause} non verrebbe mai più interrotta (se non in caso di un
+altro segnale).
+
+Questo problema può essere risolto (ed è la modalità con cui veniva fatto in
+SVr2) usando la funzione \func{longjump} (vedi \secref{sec:proc_longjmp}) per
+uscire dal manipolatore; in questo modo, con una condizione sullo stato di
+uscita di quest'ultima, si può evitare la chiamata a \func{pause}, usando un
+codice del tipo di quello riportato in \figref{fig:sig_sleep_incomplete}.
 
 
-Come abbiamo accennato in \secref{sec:proc_atom_oper} quando si ha a che fare
-con i segnali 
+\begin{figure}[!htb]
+  \footnotesize \centering
+  \begin{minipage}[c]{15cm}
+    \begin{lstlisting}{}
+static jmp_buff alarm_return;
+unsigned int sleep(unsigned int seconds)
+{
+    signandler_t prev_handler;
+    if ((prev_handler = signal(SIGALRM, alarm_hand)) == SIG_ERR) {
+        printf("Cannot set handler for alarm\n");
+        exit(1);
+    }
+    if (setjmp(alarm_return) == 0) { /* if not returning from handler */
+        alarm(second);      /* call alarm */
+        pause();            /* then wait */
+    }
+    /* restore previous signal handler */
+    signal(SIGALRM, prev_handler);
+    /* remove alarm, return remaining time */
+    return alarm(0);
+}
+void alarm_hand(int sig) {
+    /* check if the signal is the right one */
+    if (sig != SIGALRM) { /* if not exit with error */
+        printf("Something wrong, handler for SIGALRM\n");
+        exit(1);
+    } else {    /* return in main after the call to pause */
+        longjump(alarm_return, 1);
+    }
+}      
+    \end{lstlisting}
+  \end{minipage} 
+  \normalsize 
+  \caption{Una implementazione ancora malfunzionante di \func{sleep}.} 
+  \label{fig:sig_sleep_incomplete}
+\end{figure}
 
 
+In questo caso il manipolatore (\texttt{\small 18-26}) non ritorna come in
+\figref{fig:sig_sleep_wrong}, ma usa \func{longjmp} (\texttt{\small 24}) per
+rientrare nel corpo principale del programma; dato che in questo caso il
+valore di uscita di \func{setjmp} è 1 grazie alla condizione in
+(\texttt{\small 9-12}) si evita comunque che \func{pause} sia chiamata a
+vuoto.
+
+Ma anche questa implementazione comporta dei problemi; in questo caso infatti
+non viene gestita correttamente l'interazione con gli altri segnali; se
+infatti il segnale di allarme interrompe un altro manipolatore, in questo caso
+l'esecuzione non riprenderà nel manipolatore in questione, ma nel ciclo
+principale, interrompendone inopportunamente l'esecuzione.  È per questo
+motivo che occorrono funzioni più sofisticate della semplice \func{signal} che
+permettano di gestire in maniera più completa