X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=signal.tex;h=eeefd982d7f2115f4b9c100ac905a4151947e3d1;hp=3bc1af18268fc02f28356d0a52e9988d0fd3d5f5;hb=6a94b0f6f36bad6daf7b8d3f52d211661a135836;hpb=52c484e2b0db3aba63c356136bea66f9aec48ef5 diff --git a/signal.tex b/signal.tex index 3bc1af1..eeefd98 100644 --- a/signal.tex +++ b/signal.tex @@ -2,7 +2,7 @@ \label{cha:signals} I segnali sono il primo e più semplice meccanismo di comunicazione nei -confronti dei processi. Non portano con se nessuna informazione che non sia il +confronti dei processi. Non portano con sé nessuna informazione che non sia il loro tipo; si tratta in sostanza di un'interruzione software portata ad un processo. @@ -15,7 +15,7 @@ esempio vengono usati per il controllo di sessione), per notificare eventi In questo capitolo esamineremo i vari aspetti della gestione dei segnali, partendo da una introduzione relativa ai concetti base con cui essi vengono realizzati, per poi affrontarne la classificazione a secondo di uso e modalità -di generazionem fino ad esaminare in dettaglio funzioni e le metodologie di +di generazione fino ad esaminare in dettaglio funzioni e le metodologie di gestione. @@ -243,7 +243,7 @@ non \var{task\_struct} del processo; si dice così che il segnale diventa \textsl{pendente} (o \textit{pending}), e rimane tale fino al momento in cui verrà notificato al processo (o verrà specificata come azione di default -quella di ingorarlo). +quella di ignorarlo). Normalmente l'invio al processo che deve ricevere il segnale è immediato ed avviene non appena questo viene rimesso in esecuzione dallo scheduler che @@ -314,6 +314,74 @@ Ciascun segnale diretto di questo numero da parte dei programmi è da evitare, in quanto esso può variare a seconda dell'implementazione del sistema, e nel caso si Linux, anche a seconda dell'architettura hardware. +Per questo motivo ad ogni segnale viene associato un nome, definendo con una +macro di preprocessore una costante uguale al suddetto numero. Sono questi +nomi, che sono standardizzati e sostanzialmente uniformi rispetto alle varie +implementazioni, che si devono usare nei programmi. Tutti i nomi e le funzioni +che concernono i segnali sono definiti nell'header di sistema \file{signal.h}. + +Il numero totale di segnali presenti è dato dalla macro \macro{NSIG}, e dato +che i numeri dei segnali sono allocati progressivamente, essa corrisponde +anche al successivo del valore numerico assegnato all'ultimo segnale definito. +In \tabref{tab:sig_signal_list} si è riportato l'elenco completo dei segnali +definiti in Linux (estratto dalle man page), comparati con quelli definiti in +vari standard. + + +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|c|p{8cm}|} + \hline + \textbf{Sigla} & \textbf{Significato} \\ + \hline + \hline + A & L'azione di default è terminare il processo. \\ + B & L'azione di default è ignorare il segnale. \\ + C & L'azione di default è terminare il processo e scrivere un \textit{core + dump}. \\ + D & L'azione di default è fermare il processo. \\ + E & Il segnale non può essere intercettato. \\ + F & Il segnale non può essere ignorato.\\ + \hline + \end{tabular} + \caption{Legenda delle azioni di default dei segnali riportate in + \tabref{tab:sig_signal_list}.} + \label{tab:sig_action_leg} +\end{table} + +In \tabref{tab:sig_signal_list} si sono anche riportate le azioni di default +di ciascun segnale (riassunte con delle lettere, la cui legenda completa è in +\tabref{tab:sig_action_leg}), quando nessun manipolatore è installato un +segnale può essere ignorato o causare la terminazione del processo. Nella +colonna standard sono stati indicati anche gli standard in cui ciascun segnale +è definito, secondo lo schema di \tabref{tab:sig_standard_leg}. + + +\begin{table}[htb] + \footnotesize + \centering + \begin{tabular}[c]{|c|l|} + \hline + \textbf{Sigla} & \textbf{Standard} \\ + \hline + \hline + P & POSIX. \\ + B & BSD. \\ + L & Linux.\\ + S & SUSv2.\\ + \hline + \end{tabular} + \caption{Legenda dei valori della colonna \textbf{Standard} di + \tabref{tab:sig_signal_list}.} + \label{tab:sig_standard_leg} +\end{table} + +In alcuni casi alla terminazione del processo è associata la creazione di un +file (posto nella directory corrente del processo e chiamato \file{core}) su +cui viene salvata un'immagine della memoria del processo (il cosiddetto +\textit{core dump}), che può essere usata da un debugger per esaminare lo +stato dello stack e delle variabili al momento della ricezione del segnale. \begin{table}[htb] \footnotesize @@ -370,73 +438,6 @@ anche a seconda dell'architettura hardware. \label{tab:sig_signal_list} \end{table} -Per questo motivo ad ogni segnale viene associato un nome, definendo con una -macro di preprocessore una costante uguale al suddetto numero. Sono questi -nomi, che sono standardizzati e sostanzialemnte uniformi rispetto alle varie -implementazioni, che si devono usare nei programmi. Tutti i nomi e le funzioni -che concernono i segnali sono definiti nell'header di sistema \file{signal.h}. - -Il numero totale di segnali presenti è dato dalla macro \macro{NSIG}, e dato -che i numeri dei segnali sono allocati progressivamente, essa corrisponde -anche al successivo del valore numerico assegnato all'ultimo segnale definito. -In \tabref{tab:sig_signal_list} si è riportato l'elenco completo dei segnali -definiti in Linux (estratto dalle man page), comparati con quelli definiti in -vari standard. - -In \tabref{tab:sig_signal_list} si sono anche riportate le azioni di default -di ciascun segnale (riassunte con delle lettere, la cui legenda completa è in -\tabref{tab:sig_action_leg}), quando nessun manipolatore è installato un -segnale può essere ignorato o causare la terminazione del processo. Nella -colonna standard sono stati indicati anche gli standard in cui ciascun segnale -è definito, secondo lo schema di \tabref{tab:sig_standard_leg}. - -\begin{table}[htb] - \footnotesize - \centering - \begin{tabular}[c]{|c|p{8cm}|} - \hline - \textbf{Sigla} & \textbf{Significato} \\ - \hline - \hline - A & L'azione di default è terminare il processo. \\ - B & L'azione di default è ignorare il segnale. \\ - C & L'azione di default è terminare il processo e scrivere un \textit{core - dump}. \\ - D & L'azione di default è fermare il processo. \\ - E & Il segnale non può essere intercettato. \\ - F & Il segnale non può essere ignorato.\\ - \hline - \end{tabular} - \caption{Legenda delle azioni di default dei segnali riportate in - \tabref{tab:sig_signal_list}.} - \label{tab:sig_action_leg} -\end{table} - -In alcuni casi alla terminazione del processo è associata la creazione di un -file (posto nella directory corrente del processo e chiamato \file{core}) su -cui viene salvata un'immagine della memoria del processo (il cosiddetto -\textit{core dump}), che può essere usata da un debugger per esaminare lo -stato dello stack e delle variabili al momento della ricezione del segnale. - -\begin{table}[htb] - \footnotesize - \centering - \begin{tabular}[c]{|c|l|} - \hline - \textbf{Sigla} & \textbf{Standard} \\ - \hline - \hline - P & POSIX. \\ - B & BSD. \\ - L & Linux.\\ - S & SUSv2.\\ - \hline - \end{tabular} - \caption{Legenda dei valori della colonna \textbf{Standard} di - \tabref{tab:sig_signal_list}.} - \label{tab:sig_standard_leg} -\end{table} - La descrizione dettagliata del significato dei vari segnali, raggruppati per tipologia, verrà affrontate nel seguito. @@ -795,10 +796,10 @@ recapitati solo al padre, al figlio dovranno arrivare solo i segnali dovuti alle sue azioni. Quando si mette in esecuzione un nuovo programma con \func{exec} (si ricordi -quanto detto in \secref{sec:prog_exec}) tutti i segnali per i quali è stato +quanto detto in \secref{sec:proc_exec}) tutti i segnali per i quali è stato installato un manipolatore vengono resettati a \macro{SIG\_DFL}. Non ha più senso infatti fare riferimento a funzioni definite nel programma originario, -che non sono nemmeno presenti nello spazio di indirizzi del nuovo programma. +che non sono presenti nello spazio di indirizzi del nuovo programma. Si noti che questo vale solo per le azioni per le quali è stato installato un manipolatore; viene mantenuto invece ogni eventuale settaggio dell'azione a @@ -819,20 +820,20 @@ eseguire un manipolatore non comporta nessun inconveniente. Esistono però dei casi in cui questo non è possibile perché renderebbe impossibile una risposta pronta al segnale. In generale questo avviene tutte le volte che si ha a che fare con system call che possono bloccarsi -indenfinitamente, che per questo vengono chiamate \textsl{lente}. Un elenco +indefinitamente, che per questo vengono chiamate \textsl{lente}. Un elenco dei casi in cui si presenta questa situazione è il seguente: \begin{itemize*} \item lettura da file che possono bloccarsi in attesa di dati non ancora presenti (come per certi file di dispositivo, la rete o le pipe). \item scrittura sugli stessi file, nel caso in cui dati non possano essere accettati immediatamente. -\item apertura di un file di dipositivo che richiede operazioni non immediate +\item apertura di un file di dispositivo che richiede operazioni non immediate per una una risposta. \item operazioni eseguite con \func{ioctl} che non è detto possano essere eseguite immediatamente. \item le funzioni di intercomunicazione che si bloccano in attesa di risposte da altri processi. -\item la funzione \func{pause} (usata appunto per attendere l'arrivo di un +\item la funzione \func{pause} (usata appunto per attendere l'-arrivo di un segnale). \item la funzione \func{wait} (se nessun processo figlio è ancora terminato). \end{itemize*} @@ -945,7 +946,7 @@ un ciclo infinito. \label{sec:sig_kill_raise} Come accennato in \secref{sec:sig_types}, un segnale può essere generato -direttamente da un processo. L'invio di un sengale generico può essere +direttamente da un processo. L'invio di un segnale generico può essere effettuato attraverso delle funzioni \func{kill} e \func{raise}. La prima serve per inviare un segnale al processo corrente, ed il suo prototipo è: \begin{prototype}{signal.h}{int raise(int sig)} @@ -985,9 +986,9 @@ la funzione \func{kill}; il suo prototipo \end{functions} La funzione \code{raise(sig)} è sostanzialmente equivalente ad una -\code{kill(getpid(), sig)}. Siccome \func{raise} è definita nello standard ISO -C non esiste in alcune vecchie versioni di Unix, per cui in generale l'uso di -\func{kill} è più portabile. +\code{kill(getpid(), sig)}. Siccome \func{raise}, che è definita nello +standard ISO C, non esiste in alcune vecchie versioni di Unix, in generale +l'uso di \func{kill} finisce per essere più portabile. Lo standard POSIX poi prevede che il valore 0 sia usato per specificare il segnale nullo. Se le funzioni vengono chiamate con questo valore non viene @@ -998,18 +999,29 @@ conto per \secref{sec:proc_pid}) per cui l'esistenza di un processo non significa che esso sia realmente quello a cui si intendeva mandare il segnale. -Il valore dell'argomento \param{pid} specifica la destinazione a cui inviare -il segnale e può assumere i seguenti significati: -\begin{basedescript}{\desclabelwidth{2cm}\desclabelstyle{\nextlinelabel}} -\item[$\texttt{pid}>0$] il segnale è mandato al processo con il \acr{pid} - indicato. -\item[$\texttt{pid}=0$] il segnale è mandato ad ogni processo del - \textit{process group} del chiamante. -\item[$\texttt{pid}=-1$] il segnale è mandato ad ogni processo (eccetto - \cmd{init}). -\item[$\texttt{pid}<-1$] il segnale è mandato ad ogni processo del process - group $|\code{pid}|$. -\end{basedescript} +Il valore dell'argomento \param{pid} specifica il processo (o i processi) di +destinazione a cui il segnale deve essere inviato e può assumere i valori +riportati in \tabref{tab:sig_kill_values}. +\begin{table}[htb] + \centering + \begin{tabular}[c]{|r|l|} + \hline + \textbf{Valore} & \textbf{Significato} \\ + \hline + \hline + $>0$ & il segnale è mandato al processo con il \acr{pid} indicato.\\ + 0 & il segnale è mandato ad ogni processo del \textit{process group} + del chiamante.\\ + $-1$ & il segnale è mandato ad ogni processo (eccetto \cmd{init}).\\ + $<-1$ & il segnale è mandato ad ogni processo del process group + $|\code{pid}|$.\\ + \hline + \end{tabular} + \caption{Valori dell'argomento \param{pid} per la funzione + \func{kill}.} + \label{tab:sig_kill_values} +\end{table} + Solo l'amministratore può inviare un segnale ad un processo qualunque, in tutti gli altri casi il \textit{real user id} o l'\textit{effective user id} @@ -1057,8 +1069,8 @@ effettuare delle scelte in caso di necessit In \secref{sec:sys_unix_time} abbiamo visto che ad ogni processo sono associati tre tempi diversi: \textit{clock time}, \textit{user time} e -\textit{system time}. Per poterli calcolare il kernel mantiene tre diversi -timer per ciascun processo: +\textit{system time}. Per poterli calcolare il kernel mantiene per ciascun +processo tre diversi timer: \begin{itemize} \item un \textit{real-time timer} che calcola il tempo reale trascorso (che corrisponde al \textit{clock time}). La scadenza di questo timer provoca @@ -1151,7 +1163,7 @@ L'uso di \func{setitimer} consente dunque un controllo completo di tutte le caratteristiche dei timer, ed in effetti la stessa \func{alarm}, benché definita direttamente nello standard POSIX.1, può a sua volta essere espressa in termini di \func{setitimer}, come evidenziato dal manuale delle \acr{glibc} -\cite[glibc] che ne riporta la definizione in \figref{fig:sig_alarm_def}. +\cite{glibc} che ne riporta la definizione in \figref{fig:sig_alarm_def}. \begin{figure}[!htb] \footnotesize \centering @@ -1180,7 +1192,7 @@ Si deve comunque tenere presente che la precisione di queste funzioni limitata da quella del timer di sistema (in genere 10~ms). Il sistema assicura comunque che il segnale non sarà mai generato prima della scadenza programmata (l'arrotondamento cioè è sempre effettuato per eccesso). Una seconda causa di -potenziali ritardi è che il segnale viene generato alla scandenza del timer, +potenziali ritardi è che il segnale viene generato alla scadenza del timer, ma poi deve essere consegnato; se il processo è attivo (questo è sempre vero per \macro{ITIMER\_VIRT}) la consegna è immediata, altrimenti può esserci un ulteriore ritardo che può variare a seconda del carico del sistema. @@ -1236,22 +1248,260 @@ segnale Pone il processo in stato di sleep fino al ritorno di un manipolatore. \bodydesc{La funzione ritorna solo dopo che un segnale è stato ricevuto ed - il relativo manipilatore è ritornato, nel qual caso restituisce -1 e setta + il relativo manipolatore è ritornato, nel qual caso restituisce -1 e setta \var{errno} a \macro{EINTR}.} \end{prototype} +La funzione segnala sempre una condizione di errore (il successo sarebbe +quello di aspettare indefinitamente). In genere si usa questa funzione quando +si vuole mettere un processo in attesa di un qualche evento specifico che non +è sotto il suo diretto controllo (ad esempio la si può usare per far reagire +il processo ad un segnale inviato da un altro processo). + + +Se invece si vuole fare attendere un processo per un determinato intervallo di +tempo lo standard POSIX.1 definisce la funzione \func{sleep}, il cui prototipo +è: +\begin{prototype}{unistd.h}{unsigned int sleep(unsigned int seconds)} + + Pone il processo in stato di sleep per \param{seconds} secondi. + + \bodydesc{La funzione restituisce zero se l'attesa viene completata, o il + numero di secondi restanti se viene interrotta da un segnale.} +\end{prototype} + +La funzione attende per il tempo specificato, a meno di non essere interrotta +da un segnale. In questo caso non è una buona idea ripetere la chiamata per il +tempo rimanente, in quanto la riattivazione del processo può avvenire in un +qualunque momento, ma il valore restituito sarà sempre arrotondato al secondo, +con la conseguenza che, se la successione dei segnali è particolarmente +sfortunata, si potranno avere ritardi anche di parecchi secondi. In genere la +scelta più sicura è quella di stabilire un termine per l'attesa, e ricalcolare +tutte le volte il numero di secondi da aspettare. + +In alcune implementazioni inoltre l'uso di \func{sleep} può avere conflitti +con quello di \macro{SIGALRM}, dato che la funzione può essere realizzata +attraverso \func{pause} e \func{alarm} (in maniera analoga all'esempio che +vedremo in \ref{sec:sig_example}). In tal caso mescolare chiamata di +\func{alarm} e \func{sleep} o modificare l'azione di \macro{SIGALRM}, può +causare risultati indefiniti. Nel caso delle \acr{glibc} è stata usata una +implementazione completamente indipendente e questi problemi non ci sono. + +La granularità di \func{sleep} permette di specificare attese in secondi, per +questo sia sotto BSD4.3 che in SUSv2 è stata definita la funzione +\func{usleep} (dove la \texttt{u} è intesa come sostituzione di $\mu$); i due +standard hanno delle definizioni diverse, ma le \acr{glibc} +seguono\footnote{secondo la man page almeno dalla versione 2.2.2.} seguono +quella di SUSv2 che prevede il seguente prototipo: +\begin{prototype}{unistd.h}{int usleep(unsigned long usec)} + + Pone il processo in stato di sleep per \param{usec} microsecondi. + + \bodydesc{La funzione restituisce zero se l'attesa viene completata, o -1 in + caso di errore, nel qual caso \var{errno} è settata a \macro{EINTR}.} + +\end{prototype} + +Anche questa funzione a seconda delle implementazioni può presentare problemi +nell'interazione con \func{alarm} e \macro{SIGALRM}, ed è pertanto deprecata +in favore di \func{nanosleep}, definita dallo standard POSIX1.b, il cui +prototipo è: +\begin{prototype}{unistd.h}{int nanosleep(const struct timespec *req, struct + timespec *rem)} + + Pone il processo in stato di sleep per il tempo specificato da \param{req}. + In caso di interruzione restituisce il tempo restante in \param{rem}. + + \bodydesc{La funzione restituisce zero se l'attesa viene completata, o -1 in + caso di errore, nel qual caso \var{errno} è settata a + \begin{errlist} + \item[\macro{EINVAL}] si è specificato un numero di secondi negativo o un + numero di nanosecondi maggiore di 999.999.999. + \item[\macro{EINTR}] la funzione è stata interrotta da un segnale. + \end{errlist}} +\end{prototype} + +Lo standard richiede che la funzione sia implementata in maniera del tutto +indipendente da \func{alarm}\footnote{nel caso di Linux questo è fatto + utilizzando direttamente il timer del kernel.} e sia utilizzabile senza +interferenze con l'uso di \macro{SIGALRM}. La funzione prende come parametri +delle strutture di tipo \var{timespec}, la cui definizione è riportata in +\figref{fig:sig_timespec_def}, che permettono di specificare un tempo con una +precisione (teorica) fino al nanosecondo. + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} +struct timespec +{ + time_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ +}; + \end{lstlisting} + \end{minipage} + \normalsize + \caption{La struttura \var{timespec} di \func{nanosleep}.} + \label{fig:sig_timespec_def} +\end{figure} + +La funzione risolve anche il problema di proseguire l'attesa dopo +l'interruzione dovuta ad un segnale; infatti in tal caso in \param{rem} viene +restituito il tempo rimanente rispetto a quanto richiesto inizialmente, e +basta richiamare la funzione per completare l'attesa. + +Chiaramente, anche se il tempo può essere specificato con risoluzioni fino al +nanosecondo, la precisione di \func{nanosleep} è determinata dalla risoluzione +temporale del timer di sistema. Perciò la funzione attenderà comunque il tempo +specificato, ma prima che il processo possa tornare ad essere eseguito +occorrerà almeno attendere il successivo giro di scheduler e cioè un tempo che +a seconda dei casi può arrivare fino a 1/\macro{HZ}, (sempre che il sistema +sia scarico ed il processa venga immediatamente rimesso in esecuzione); per +questo motivo il valore restituito in \param{rem} è sempre arrotondato al +multiplo successivo di 1/\macro{HZ}. + +In realtà è possibile ottenere anche pause più precise del centesimo di +secondo usando politiche di scheduling real time come \macro{SCHED\_FIFO} o +\macro{SCHED\_RR}; in tal caso infatti il meccanismo di scheduling ordinario +viene evitato, e si raggiungono pause fino ai 2~ms con precisioni del $\mu$s. -\subsection{Le semantiche di \macro{SIGCHLD}} +\subsection{Un esempio elementare} \label{sec:sig_sigchld} +Un semplice esempio per illustrare il funzionamento di un manipolatore di +segnale è quello della gestione di \macro{SIGCHLD}. Abbiamo visto in +\secref{sec:proc_termination} che una delle azioni eseguite dal kernel alla +conclusione di un processo è quella di inviare questo segnale al +padre;\footnote{in realtà in SRV4 eredita la semantica di System V, in cui il + segnale si chiama \macro{SIGCLD} e viene trattato in maniera speciale; in + System V infatti se si setta esplicitamente l'azione a \macro{SIG\_IGN} il + segnale non viene generato ed il sistema non genera zombie (lo stato di + terminazione viene scartato senza dover chiamare una \func{wait}). L'azione + di default è sempre quella di ignorare il segnale, ma non attiva questo + comportamento. Linux, come BSD e POSIX, non supporta questa semantica ed usa + il nome di \macro{SIGCLD} come sinonimo di \macro{SIGCHLD}.} in questo caso +tutto quello che dovrà fare il manipolatore è ricevere lo stato di +terminazione in modo da evitare la formazione di zombie. + +% è pertanto +% naturale usare un esempio che ci permette di concludere la trattazione della +% terminazione dei processi. +% In questo caso si è tratterà di illustrare un esempio relativo ad un +% manipolatore per che è previsto ritornare, + +In + + + + +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} +#include /* error simbol definitions */ +#include /* signal handling declarations */ +#include +#include +#include "macro.h" + +void Hand_CHLD(int sig) +{ + int errno_save; + int status; + pid_t pid; + /* save errno current value */ + errno_save = errno; + /* loop until no */ + do { + errno = 0; + pid = waitpid(WAIT_ANY, &status, WNOHANG); + if (pid > 0) { + debug("child %d terminated with status %x\n", pid, status); + } + } while ((pid > 0) && (errno == EINTR)); + /* restore errno value*/ + errno = errno_save; + /* return */ + return; +} + \end{lstlisting} + \end{minipage} + \normalsize + \caption{Una implementazione sbagliata di \func{sleep}.} + \label{fig:sig_timespec_def} +\end{figure} + + \section{Gestione avanzata} \label{sec:sig_control} +Le funzioni esaminate finora fanno riferimento ad alle modalità più elementari +della gestione dei segnali; non si sono pertanto ancora prese in +considerazione le tematiche più complesse, collegate alle varie race condition +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 +risolvere i problemi più complessi connessi alla programmazione con i segnali. + + +\subsection{Un esempio di problema} +\label{sec:sig_example} + +Come accennato in \ref{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: +\begin{figure}[!htb] + \footnotesize \centering + \begin{minipage}[c]{15cm} + \begin{lstlisting}[labelstep=0,frame=,indent=1cm]{} +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); + } + alarm(second); + pause(); + /* 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 { /* do nothing, just interrupt pause */ + return; + } +} + \end{lstlisting} + \end{minipage} + \normalsize + \caption{Una implementazione sbagliata di \func{sleep}.} + \label{fig:sig_timespec_def} +\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). + +Come abbiamo accennato in \secref{sec:proc_atom_oper} quando si ha a che fare +con i segnali + @@ -1260,6 +1510,7 @@ segnale + \subsection{La funzione \func{sigaction}} \label{sec:sig_sigaction}