From: Simone Piccardi Date: Sat, 30 Mar 2002 23:07:15 +0000 (+0000) Subject: Risistemati gli esempi per sleep e fissata una referenza X-Git-Url: https://gapil.gnulinux.it/gitweb/?a=commitdiff_plain;h=f6c4cb8cabe3c33f7057e937d66c8dad6d017086;p=gapil.git Risistemati gli esempi per sleep e fissata una referenza --- diff --git a/gapil.tex b/gapil.tex index 36fc3c7..2dd81de 100644 --- a/gapil.tex +++ b/gapil.tex @@ -105,6 +105,8 @@ % distance from margins for boxedminipage %\fboxsep=6pt + + \include{intro} \include{process} \include{prochand} diff --git a/prochand.tex b/prochand.tex index 73f2e7f..0826840 100644 --- a/prochand.tex +++ b/prochand.tex @@ -938,10 +938,10 @@ In genere in un programma non si vuole essere forzati ad attendere la conclusione di un processo per proseguire, specie se tutto questo serve solo per leggerne lo stato di chiusura (ed evitare la presenza di \textit{zombie}), per questo la modalità più usata per chiamare queste funzioni è quella di -utilizzarle all'interno di un \textit{signal handler} (torneremo sui segnali e -su come gestire \macro{SIGCHLD} in \secref{sec:sig_sigwait_xxx}). In questo -caso infatti, dato che il segnale è generato dalla terminazione di un figlio, -avremo la certezza che la chiamata a \func{wait} non si bloccherà. +utilizzarle all'interno di un \textit{signal handler} (vedremo un esempio di +come gestire \macro{SIGCHLD} con i segnali in \secref{sec:sig_example}). In +questo caso infatti, dato che il segnale è generato dalla terminazione di un +figlio, avremo la certezza che la chiamata a \func{wait} non si bloccherà. \begin{table}[!htb] \centering diff --git a/signal.tex b/signal.tex index 1fb4569..be01dc4 100644 --- a/signal.tex +++ b/signal.tex @@ -1443,42 +1443,41 @@ void Hand_CHLD(int sig) \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 -(\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 -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. +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}, 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. +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. -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. +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 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. +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. @@ -1503,15 +1502,20 @@ casistica ordinaria. 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}. -In questo caso si salva il precedente manipolatore in (\texttt{\small 15--21}) -per poi chiamare in sequenza \func{alarm} per specificare l'attesa e -\func{pause} per fermare il programma. Al ritorno di pause, causato dal -ritorno del manipolatore (\texttt{\small 15--21}) si ripristina il -manipolatore (\texttt{\small 15--21}) restituendo l'eventuale tempo rimanente -(\texttt{\small 15--21}). + +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 @@ -1543,23 +1547,25 @@ void alarm_hand(int sig) { \end{lstlisting} \end{minipage} \normalsize - \caption{Una implementazione pericolosamente sbagliata di \func{sleep}.} + \caption{Una implementazione pericolosa di \func{sleep}.} \label{fig:sig_sleep_wrong} \end{figure} -Ma questo codice, 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 (ad esempio se il sistema è 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 più 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, usando lo stato di uscita di -quest'ultima, si può evitare la chiamata a \func{pause}, con un codice come -quello riportato in \ref{fig:sig_sleep_incomplete}. +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}. \begin{figure}[!htb] \footnotesize \centering @@ -1598,17 +1604,20 @@ void alarm_hand(int sig) { \label{fig:sig_sleep_incomplete} \end{figure} -In questo caso il manipolatore non ritorna come in \ref{fig:sig_sleep_wrong}, -ma usa \func{longjmp} (\texttt{\small 15--21}) 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 15--21}) si evita -in ogni caso che \func{pause} sia chiamata a vuoto. +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 altri segnali; se infatti il -segnale di allarme interrompe un altro manipolatore, in questo caso +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. +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