X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=blobdiff_plain;f=signal.tex;h=95974f3b9577ab899c08bee02ffc1b160e498fa8;hp=c2345f01e1c49dc0ba19659c9775ca2a2d6d81f6;hb=15fa5013a3b410ce14e4ec38ca3e312227478e76;hpb=0c63d62732e14ab73d5f1f0a0e5d70f3873bc334 diff --git a/signal.tex b/signal.tex index c2345f0..95974f3 100644 --- a/signal.tex +++ b/signal.tex @@ -1270,10 +1270,10 @@ 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 fra poco). 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. +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 @@ -1290,20 +1290,68 @@ quella di SUSv2 che prevede il seguente prototipo: \end{prototype} -Anche questa funzione 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 è: +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 \macro{EINVAL} o - \macro{EINTR}.} + 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. @@ -1315,27 +1363,86 @@ segnale \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; 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 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}.} è pertanto naturale completare qui la -trattazione della terminazione dei processi illustrando le modalità per -gestire questo segnale. + 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}.} è pertanto +naturale completare qui la trattazione della terminazione dei processi +illustrando le modalità per gestire questo segnale. + + + \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 è possibile implementare \func{sleep} a partire da dall'uso di -\func{pause} e \func{alarm}; +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 + @@ -1344,6 +1451,7 @@ Come accennato + \subsection{La funzione \func{sigaction}} \label{sec:sig_sigaction}