X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=signal.tex;h=3187101587105ffb6066fb68ecd5345f0a9bd0e1;hp=1f0649db98fba4f9895f8a0af72ca997c2b57234;hb=689b99ce22ba645115d553468ec3bfed263a69fa;hpb=042d89a299fa60bc6e08ddba02f36986cae1fd6c diff --git a/signal.tex b/signal.tex index 1f0649d..3187101 100644 --- a/signal.tex +++ b/signal.tex @@ -84,8 +84,21 @@ attivo. In questo caso è possibile una situazione in cui i segnali possono essere perduti. Si consideri il seguente segmento di codice, in cui la prima operazione del manipolatore è quella di reinstallare se stesso: -s -e un secondo segnale arriva prima che il manipolatore invocato dal primo +\footnotesize +\begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} + int sig_handler(); /* handler function */ + ... + signal(SIGINT, sig_handler); /* establish handler */ + ... + +int sig_handler() +{ + signal(SIGINT, sig_handler); /* restablish handler */ + ... /* process signal */ +} +\end{lstlisting} +\normalsize +se un secondo segnale arriva prima che il manipolatore invocato dal primo abbia eseguito la reinstallazione di se stesso il segnale può essere perso o causare il comportamento originale assegnato al segnale (in genere la terminazione del processo). @@ -419,7 +432,7 @@ stato dello stack e delle variabili al momento della ricezione del segnale. \end{table} La descrizione dettagliata del significato dei vari segnali, raggruppati per -tipologia, verrà affrontate nel seguito. +tipologia, verrà affrontate nei paragrafi successivi. \subsection{Segnali di errore di programma} @@ -1501,7 +1514,7 @@ fino a trattare le caratteristiche generali della gestione dei medesimi nella casistica ordinaria. -\subsection{Un esempio di problema} +\subsection{Alcune problematiche aperte} \label{sec:sig_example} Come accennato in \secref{sec:sig_pause_sleep} è possibile implementare @@ -1622,9 +1635,63 @@ 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 i segnali in maniera più completa. +principale, interrompendone inopportunamente l'esecuzione. Lo stesso tipo di +problemi si presenterebbero se si volesse usare \func{alarm} per stabilire un +timeout su una qualunque system call bloccante. + +Un secondo esempio è quello in cui si usa il segnale per notificare una +quelche forma di evento; in genere quello che si fa in questo caso è settare +nel manipolatore un opportuno flag da controllare nel corpo principale del +programma (con un codice del tipo di quello riportato in +\secref{fig:sig_event_wrong}. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}{} +sig_atomic_t flag; +int main() +{ + flag = 0; + ... + if (flag) { /* test if signal occurred */ + flag = 0; /* reset flag */ + do_response(); /* do things */ + } else { + do_other(); /* do other things */ + } + ... +} +void alarm_hand(int sig) +{ + /* set the flag + flag = 1; + return; +} + \end{lstlisting} + \end{minipage} + \normalsize + \caption{Un esempio non funzionante del codice per il controllo di un + evento generato da un segnale.} + \label{fig:sig_event_wrong} +\end{figure} + +La logica è quella di far settare al manipolatore (\texttt{\small 14-19}) una +variabile globale preventivamente inizializzata nel programma principale, il +quale potrà determinare, osservandone il contenuto, l'occorrenza o meno del +segnale, e prendere le relative azioni conseguenti (\texttt{\small 6-11}). + +Questo è il tipico esempio di caso, già citato in \secref{sec:proc_race_cond}, +in cui si genera una race condition; se infatti il segnale arriva +immediatamente dopo l'esecuzione del controllo (\texttt{\small 6}) ma prima +della cancellazione del flag (\texttt{\small 7}), la sua occorrenza sarà +perduta. + +Questi esempi ci mostrano che per una gestione effettiva dei segnali occorrono +funzioni più sofisticate della semplice interfaccia dei primi sistemi Unix, +che permettano di gestire tutti i possibili aspetti con cui un processo deve +reagire alla ricezione di un segnale. + \subsection{I \textit{signal set}} @@ -1701,7 +1768,6 @@ La funzione principale dell'interfaccia standard POSIX.1 per i segnali \func{sigaction}, essa ha sostanzialemente le stesse funzioni di \func{signal}, permette cioè di specificare come un segnale può essere gestito da un processo. Il suo prototipo è: - \begin{prototype}{signal.h}{int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)} @@ -1783,7 +1849,7 @@ segnali; i valori possibili ed il relativo significato sono riportati in \centering \begin{tabular}[c]{|l|p{8cm}|} \hline - \textbf{Valore} & \textbf{Timer} \\ + \textbf{Valore} & \textbf{Significato} \\ \hline \hline \macro{SA\_NOCLDSTOP}& Se il segnale è \macro{SIGCHLD} allora non deve @@ -1812,20 +1878,20 @@ segnali; i valori possibili ed il relativo significato sono riportati in \end{table} Benché sia possibile usare nello stesso programma sia \func{sigaction} che -\func{signal} occorre comunque stare attenti, in quanto le due funzioni -possono interagire in maniera anomala. In generale infatti l'azione -specificata da \var{sigaction} contiene un maggior numero di informazioni -rispetto al semplice indirizzo del manipolatore restituito da \func{signal}. -Per questo motivo se si usa quest'ultima per installare un manipolatore -sostituendone uno precedentemente installato con \func{sigaction}, non sarà -possibile effettuare il ripristino dello stesso con il valore di ritorno. - -Per questo motivo è sempre il caso di usare \func{sigaction}, che è in grado -di ripristinare correttamente un manipolatore precedente, anche se questo è -stato installato con \func{signal}. In generale poi non è il caso di usare il -valore di ritorno di \func{signal} come campo \var{sa\_handler}, o viceversa, -dato che in certi sistemi questi possono essere diversi. In generale dunque, a -meno che non si sia vincolati allo standard ISO C, è sempre il caso di evitare +\func{signal} occorre molta attenzione, in quanto le due funzioni possono +interagire in maniera anomala. Infatti l'azione specificata con +\var{sigaction} contiene un maggior numero di informazioni rispetto al +semplice indirizzo del manipolatore restituito da \func{signal}. Per questo +motivo se si usa quest'ultima per installare un manipolatore sostituendone uno +precedentemente installato con \func{sigaction}, non sarà possibile effettuare +un ripristino corretto dello stesso. + +Per questo è sempre opportuno usare \func{sigaction}, che è in grado di +ripristinare correttamente un manipolatore precedente, anche se questo è stato +installato con \func{signal}. In generale poi non è il caso di usare il valore +di ritorno di \func{signal} come campo \var{sa\_handler}, o viceversa, dato +che in certi sistemi questi possono essere diversi. In generale dunque, a meno +che non si sia vincolati allo standard ISO C, è sempre il caso di evitare l'uso di \func{signal} a favore di \func{sigaction}. @@ -1833,14 +1899,76 @@ l'uso di \func{signal} a favore di \func{sigaction}. \subsection{La gestione del blocco dei segnali} \label{sec:sig_sigmask} -Una delle informazioni che ciascun processo porta con se è l'insieme dei -segnali bloccati (la cosiddetta \textit{signal mask}, anch'essa un signal set, -mantenuta nel campo \var{blocked} di \var{task\_struct}); abbiamo accennato in -\secref{sec:proc_fork} che essa viene ereditata alla creazione di un processo -figlio, e abbiamo visto come essa viene controllata dal campo \var{sa\_mask} -di \var{sigaction}. +Come spiegato in \secref{sec:sig_semantics} tutti i moderni sistemi unix-like +permettono si bloccare temporaneamente (o di eliminare completamente, settando +\macro{SIG\_IGN} come azione) la consegna dei segnali ad un processo. Questo è +fatto specificando la cosiddetta \textit{signal mask} del +processo\footnote{nel caso di Linux essa è mantenuta dal campo \var{blocked} + della \var{task\_struct} del processo.} che viene espressa come il signal +set dei segnali la cui consegna è bloccata. Abbiamo accennato in +\secref{sec:proc_fork} che la \textit{signal mask} viene ereditata dal padre +alla creazione di un processo figlio, e abbiamo visto al paragrafo precedente +che essa può essere specificata, durante l'esecuzione di un manipolatore, +attraverso l'uso dal campo \var{sa\_mask} di \var{sigaction}. + +Uno dei problemi evidenziatisi con l'esempio di \secref{fig:sig_event_wrong} è +che in molti casi è necessario proteggere delle sezioni di codice (nel caso la +sezione fra il test e la eventuale cancellazione del flag che testimoniava +l'avvenuta occorrenza del segnale) in modo da essere sicuri che essi siano +eseguiti senza interruzioni. Le operazioni più semplici, come l'assegnazione o +il controllo di una variabile (per essere sicuri si può usare il tipo +\type{sig\_atomic\_t}) di norma sono atomiche, ma quando le operazioni sono +più complesse si può usare la funzione \func{sigprocmask} per bloccare uno o +più segnali; il suo prototipo è: +\begin{prototype}{signal.h} +{int sigprocmask(int how, const sigset\_t *set, sigset\_t *oldset)} + + Cambia la \textit{signal mask} del processo corrente. + + \bodydesc{La funzione restituisce zero in caso di successo e -1 per un + errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\macro{EINVAL}] Si è specificato un numero di segnale invalido. + \item[\macro{EFAULT}] Si sono specificati indirizzi non validi. + \end{errlist}} +\end{prototype} + +La funzione +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|l|p{8cm}|} + \hline + \textbf{Valore} & \textbf{Significato} \\ + \hline + \hline + \macro{SIG\_BLOCK} & .\\ + \macro{SIG\_UNBLOCK} & .\\ + \macro{SIG\_SETMASK} & .\\ + \hline + \end{tabular} + \caption{Valori e significato dell'argomento \param{how} della funzione + \func{sigprocmask}.} + \label{tab:sig_procmask_how} +\end{table} + + +Un altro +\begin{prototype}{signal.h} +{int sigsuspend(const sigset\_t *mask)} + + Cambia la \textit{signal mask} del processo corrente. + + \bodydesc{La funzione restituisce zero in caso di successo e -1 per un + errore, nel qual caso \var{errno} assumerà i valori: + \begin{errlist} + \item[\macro{EINVAL}] Si è specificato un numero di segnale invalido. + \item[\macro{EFAULT}] Si sono specificati indirizzi non validi. + \end{errlist}} +\end{prototype} + \subsection{Le funzioni \func{sigpending} e \func{sigsuspend}}