Risistemati un sacco di riferiementi, e la riorganizzazione della parte
[gapil.git] / signal.tex
index d819acd2083ca2a0cf1b1f1c8bc9360e4ef30af2..ec3bf5547afcab767eddadcd7d14a91cb477bb67 100644 (file)
@@ -1,5 +1,5 @@
 \chapter{I segnali}
-\label{sec:signals}
+\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
@@ -28,57 +28,137 @@ segnale sono vari; un breve elenco di possibile cause 
   essere eseguita.
 \item una richiesta dell'utente di terminare o fermare il programma. In genere
   si realizza attraverso un segnale mandato dalla shell in corrispondenza
-  della pressione di tasti come 'ctrl-c' o 'ctrl-z'.
+  della pressione di tasti del terminale come 'ctrl-c' o 'ctrl-z'.
 \item l'esecuzione di una \texttt{kill} o di una \texttt{raise} da parte del
   processo stesso o di un'altro (solo nel caso della \texttt{kill}).
 \end{itemize}
 
 Ciascuno di questi eventi (tranne gli ultimi due che sono controllati
-dall'utente) comporta da parte del kernel la generazione un particolare tipo
-di segnale.
+dall'utente) comporta l'intervento diretto da parte del kernel che causa la
+generazione un particolare tipo di segnale.
 
 
 \subsection{Le modalità di funzionamento}
 \label{sec:sig_semantics}
 
-Quando un processo riceve un segnale il kernel esegue una apposita routine di
-gestione (il cosiddetto \textit{signal handler}) che può essere specificata
-dall'utente.  Negli anni il comportamento del sistema in risposta ai segnali è
-stato modificato in vari modi nelle differenti implementazioni di unix.
-Attualmente si possono individuare due tipologie fondamentali di comportamento
-dei segnali (dette semantiche) che vengono chiamate rispettivamente
-\textit{reliable} e \textit{unreliable}.
-
-Nella semantica \textit{unreliable} la routine di gestione del segnale
-specificata dall'utente non resta installata una volta chiamata; è perciò a
-carico dell'utente stesso ripetere l'installazione all'interno della routine
-di gestione stessa in tutti i casi in cui si vuole che il signal handler
-esterno resti attivo.
-
-Per questo motivo è possibile una race-condition in cui un secondo segnale
-arriva prima che il manipolatore abbia eseguito la re-installazione di se
-stesso, nel qual caso il segnale può essere perso o causare il comportamento
-originale assegnato al segnale (in genere la terminazione del processo).
-Questa è la ragione per cui detti segnali sono chiamati \textit{inaffidabili},
-in quanto la ricezione del segnale e la reinstallazione del suo manipolatore
-non sono operazioni atomiche.
-
-Nel caso di implementazione inaffidabile le chiamate di sistema non
-sono fatte ripartire automaticamente quando sono interrotte da un segnale, per
-questo il programma deve controllare lo stato di uscita della chiamata al
-sistema e riperterla nel caso l'errore riportato da \texttt{errno} sia
-\texttt{EINTR}.
-
-Inoltre in questo caso non esiste una modalità semplice per ottenere una
-operazione di pausa atomica (cioè mandare in sleep un processo fino all'arrivo
-di un segnale), dato che ci sono casi in cui un segnale può arrivare quando il
-programma non è in grado di accorgersene.
-
-In caso di segnali \textit{reliable} invece il signal handler resta installato
-quando viene chiamato e i problemi precedenti sono evitati. Inoltre alcune
-chiamate di sistema possono essere fatte ripartire automaticamente e si può
-ottenere un'operazione di pausa atomica (usando la funzione POSIX
-\texttt{sigsuspend}).
+Quando un processo riceve un segnale il kernel esegue una azione di default o
+una apposita routine di gestione (il cosiddetto \textit{signal handler} o
+\textsl{manipolatore}) che può essere specificata dall'utente (nel qual caso
+si dice che si \textsl{intercetta} il segnale).  Negli anni il comportamento
+del sistema in risposta ai segnali è stato modificato in vari modi nelle
+differenti implementazioni di unix.  Si possono individuare due tipologie
+fondamentali di comportamento dei segnali (dette semantiche) che vengono
+chiamate rispettivamente \textit{reliable} e \textit{unreliable}.
+
+Nella semantica \textit{unreliable} (quella implementata dalle prime versioni
+di unix) la routine di gestione del segnale specificata dall'utente non resta
+installata una volta chiamata; è perciò a carico dell'utente stesso ripetere
+l'installazione all'interno della routine di gestione stessa in tutti i casi
+in cui si vuole che il signal handler esterno resti 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:
+\begin{lstlisting}{showlines=false}
+
+    int sig_handler();            /* handler function */
+    ...
+    signal(SIGINT, sig_handler);  /* establish handler */
+    ...
+
+int sig_handler() 
+{
+    signal(SIGINT, sig_handler);  /* restablish handler */
+    ...                           /* process signal */
+}
+\end{lstlisting}
+se un secondo segnale arriva prima che il manipolatore invocato dal primo
+abbia eseguito la re-installazione di se stesso il segnale può essere perso o
+causare il comportamento originale assegnato al segnale (in genere la
+terminazione del processo).
+
+Questa è la ragione per cui l'implementazione dei segnali secondo questa
+semantica viene chiamata \textit{inaffidabile}, in quanto la ricezione del
+segnale e la reinstallazione del suo manipolatore non sono operazioni
+atomiche.
+
+Un'altro problema è che in questa semantica è che non esiste un modo per
+bloccare i segnali quando non si vuole che arrivino; i processi possono si
+ignorare il segnale, ma non è possibile istruire il sistema a non fare nulla
+in occasione di un segnale, pur mantenendo memoria del fatto che è avvenuto.
+
+Un caso classico, riportato da Stevens, in cui si incontra questo problema, è
+quello in cui si usa il manipolatore per settare un flag che riporta al
+processo l'occorrenza del segnale. Si consideri il seguente segmento di
+codice il cui scopo sarebbe quello di fermare il processo fino all'occorrenza
+di un opportuno segnale:
+\begin{lstlisting}{}
+int signal_flag = 0;
+main ()
+{
+    int sig_handler();            /* handler function */
+    ...
+    signal(SIGINT, sig_handler);  /* establish handler */
+    ...
+    while(signal_flag == 0) {     /* while flag is zero */
+        pause();                  /* go to sleep */
+    }
+    ... 
+}
+int sig_handler() 
+{
+    signal(SIGINT, sig_handler);  /* restablish handler */
+    signal_flag = 1;              /* set flag */
+}
+\end{lstlisting}
+l'idea è che quando il processo trova il flag a zero viene messo in sleep e
+verrà risvegliato solo dalla ricezione di un segnale. Il manipolatore si
+limita in questo caso a settare il flag a uno; all'uscita dal manipolatore la
+chiamata a \func{pause} è interrotta ed il processo viene risvegliato e
+riprende l'esecuzione all'istruzione successiva, ma essendo cambiato il flag
+la condizione non è più soddisfatta e il programma prosegue.
+
+Il problema con l'implementazione inaffidabile è che niente ci garantisce che
+il segnale arrivi fra la valutazione della condizione del \func{while} e la
+chiamata a \func{pause}, nel qual caso, se il segnale non viene più generato,
+il processo resterà in sleep permanentemente.
+
+% Un'altra caratteristica della implementazione inaffidabile è che le chiamate
+% di sistema non sono fatte ripartire automaticamente quando sono interrotte da
+% un segnale, per questo un programma deve controllare lo stato di uscita della
+% chiamata al sistema e riperterla nel caso l'errore riportato da \texttt{errno}
+% sia \texttt{EINTR}.
+
+Questo ci mostra ad esempio come con la semantica inaffidabile non esista una
+modalità semplice per ottenere una operazione di pausa atomica (cioè mandare
+in sleep un processo fino all'arrivo di un segnale).
+
+Nella semantica \textit{reliable} (quella utilizzata da Linux e da ogni Unix
+moderno) invece il signal handler una volta installato resta attivo e non si
+hanno tutti i problemi precedenti. In questa semantica i segnali vengono
+\textsl{generati} dal kernel per un processo all'occorrenza dell'evento che
+causa il segnale. In genere questo viene fatto dal kernel settanto un flag
+nella process table del processo.
+
+Si dice che il segnale viene \textsl{consegnato} al processo (dall'inglese
+\textit{delivered}) quando viene eseguita l'azione per esso prevista, mentre
+per tutto il tempo che passa fra la generazione del segnale e la sua consegna
+esso è detto \textsl{pendente}. In genere questa procedura viene effettuata
+dal kernel quando, riprendendo l'esecuzione del processo in questione, verifica
+la presenza del flag del segnale nella process table.
+
+In questa semantica un processo ha la possibilità di bloccare la consegna dei
+segnali, in questo caso se l'azione per il suddetto segnale non è quella di
+ignorarlo, il segnale resta \textsl{pendente} fintanto che il processo non lo
+sblocca (nel qual caso viene consegnato) o setta l'azione di default per
+ignorarlo. 
+
+Si tenga presente kernel stabilisce cosa fare con un segnale che è stato
+bloccato al momento della consegna, non quando viene generato; questo consente
+di cambiare l'azione per il segnale prima che esso venga consegnato, e si può
+usare la funzione \func{sigpending} (vedi \secref{sec:sig_sigpending}) per
+determinare quali segnali sono bloccati e quali sono pendenti.
+
 
 
 \subsubsection{Tipi di segnali}
@@ -198,42 +278,42 @@ che i numeri dei segnali sono allocati progressivamente, essa corrisponde
 anche al successivo del valore numerico assegnato all'ultimo segnale definito.
 In \ntab\ si è riportato l'elenco completo dei segnali definiti in Linux
 (estratto dalle man page), comparati con quelli definiti in vari standard.
-
 \begin{table}[htb]
   \centering
-  \begin{tabular}[c]{|l|c|c|c||c|l|}
+  \begin{tabular}[c]{|l|c|c|c||c|p{8cm}|}
     \hline
-    Segnale  & POSIX.1 & SUSv2 & Linux  &Azione &  Descrizione                \\
+    Segnale  & POSIX.1 & SUSv2 & Linux  &Azione &  Descrizione \\
     \hline
     \hline
-    SIGHUP   &$\bullet$&&$\bullet$&  A  & Hangup                                \\
-    SIGINT   &$\bullet$&&$\bullet$&  A  & Interrupt from keyboard               \\
-    SIGQUIT  &$\bullet$&&$\bullet$&  C  & Quit from keyboard                    \\
-    SIGILL   &$\bullet$&&$\bullet$&  C  & Illegal Instruction                   \\
-    SIGABRT  &$\bullet$&&$\bullet$&  C  & Abort signal from abort(3)            \\
-    SIGFPE   &$\bullet$&&$\bullet$&  C  & Floating point exception              \\
-    SIGKILL  &$\bullet$&&$\bullet$& AEF & Kill signal                           \\
-    SIGSEGV  &$\bullet$&&$\bullet$&  C  & Invalid memory reference              \\
-    SIGPIPE  &$\bullet$&&$\bullet$&  A  & Broken pipe                           \\
-    SIGALRM  &$\bullet$&&$\bullet$&  A  & Timer signal from alarm(2)            \\
-    SIGTERM  &$\bullet$&&$\bullet$&  A  & Termination signal                    \\
-    SIGUSR1  &$\bullet$&&$\bullet$&  A  & User-defined signal 1                 \\
-    SIGUSR2  &$\bullet$&&$\bullet$&  A  & User-defined signal 2                 \\
-    SIGCHLD  &$\bullet$&&$\bullet$&  B  & Child stopped or terminated           \\
-    SIGCONT  &$\bullet$&&$\bullet$&     & Continue if stopped                   \\
-    SIGSTOP  &$\bullet$&&$\bullet$& DEF & Stop process                          \\
-    SIGTSTP  &$\bullet$&&$\bullet$&  D  & Stop typed at tty                     \\
-    SIGTTIN  &$\bullet$&&$\bullet$&  D  & tty input for background process      \\
-    SIGTTOU  &$\bullet$&&$\bullet$&  D  & tty output for background process     \\
-    SIGBUS    &&$\bullet$&$\bullet$& C & Bus error (bad memory access)         \\
+    SIGHUP   &$\bullet$&&$\bullet$&  A  & Hangup sul terminale  o
+    morte del processo di controllo  \\
+    SIGINT   &$\bullet$&&$\bullet$&  A  & Interrupt da tastiera (\cmd{C-c})\\
+    SIGQUIT  &$\bullet$&&$\bullet$&  C  & Quit da tastiera (\cmd{C-y}) \\
+    SIGILL   &$\bullet$&&$\bullet$&  C  & Istruzione illegale\\
+    SIGABRT  &$\bullet$&&$\bullet$&  C  & Segnale di Abort da \func{abort} \\
+    SIGFPE   &$\bullet$&&$\bullet$&  C  & Errore aritmetico\\
+    SIGKILL  &$\bullet$&&$\bullet$& AEF & Segnale di terminazione forzata \\
+    SIGSEGV  &$\bullet$&&$\bullet$&  C  & Errore di accesso in memoria\\
+    SIGPIPE  &$\bullet$&&$\bullet$&  A  & Pipe spezzata\\
+    SIGALRM  &$\bullet$&&$\bullet$&  A  & Segnale del timer da \func{alarm} \\
+    SIGTERM  &$\bullet$&&$\bullet$&  A  & Segnale di terminazione \verb|C-\|\\
+    SIGUSR1  &$\bullet$&&$\bullet$&  A  & User-defined signal 1\\
+    SIGUSR2  &$\bullet$&&$\bullet$&  A  & User-defined signal 2\\
+    SIGCHLD  &$\bullet$&&$\bullet$&  B  & Child stopped or terminated\\
+    SIGCONT  &$\bullet$&&$\bullet$&     & Continue if stopped\\
+    SIGSTOP  &$\bullet$&&$\bullet$& DEF & Stop process\\
+    SIGTSTP  &$\bullet$&&$\bullet$&  D  & Stop typed at tty \\
+    SIGTTIN  &$\bullet$&&$\bullet$&  D  & tty input for background process \\
+    SIGTTOU  &$\bullet$&&$\bullet$&  D  & tty output for background process \\
+    SIGBUS    &&$\bullet$&$\bullet$& C & Bus error (bad memory access) \\
     SIGPOLL   &&$\bullet$&$\bullet$& A & Pollable event (Sys V). Synonym of SIGIO\\
-    SIGPROF   &&$\bullet$&$\bullet$& A & Profiling timer expired               \\
-    SIGSYS    &&$\bullet$&$\bullet$& C & Bad argument to routine (SVID)        \\
-    SIGTRAP   &&$\bullet$&$\bullet$& C & Trace/breakpoint trap                 \\
-    SIGURG    &&$\bullet$&$\bullet$& B & Urgent condition on socket (4.2 BSD)  \\
-    SIGVTALRM &&$\bullet$&$\bullet$& A & Virtual alarm clock (4.2 BSD)         \\
-    SIGXCPU   &&$\bullet$&$\bullet$& C & CPU time limit exceeded (4.2 BSD)     \\
-    SIGXFSZ   &&$\bullet$&$\bullet$& C & File size limit exceeded (4.2 BSD)    \\
+    SIGPROF   &&$\bullet$&$\bullet$& A & Profiling timer expired \\
+    SIGSYS    &&$\bullet$&$\bullet$& C & Bad argument to routine (SVID)\\
+    SIGTRAP   &&$\bullet$&$\bullet$& C & Trace/breakpoint trap \\
+    SIGURG    &&$\bullet$&$\bullet$& B & Urgent condition on socket (4.2 BSD)\\
+    SIGVTALRM &&$\bullet$&$\bullet$& A & Virtual alarm clock (4.2 BSD) \\
+    SIGXCPU   &&$\bullet$&$\bullet$& C & CPU time limit exceeded (4.2 BSD)  \\
+    SIGXFSZ   &&$\bullet$&$\bullet$& C & File size limit exceeded (4.2 BSD)\\
     SIGIOT    &&&$\bullet$& C &     IOT trap. A synonym for SIGABRT        \\
     SIGEMT    &&&$\bullet$&   &                                            \\
     SIGSTKFLT &&&$\bullet$& A &     Stack fault on coprocessor             \\
@@ -249,8 +329,17 @@ In \ntab\ si 
   \caption{Lista dei segnali in Linux}
   \label{tab:sig_signal_list}
 \end{table}
-in \curtab\ si sono riportate anche le caratteristiche di ciascun segnale,
-indicate con una lettera nel campo azione, la cui legenda è:
+in \curtab\ si sono riportate le azioni di default di ciascun segnale
+(riassunte con delle lettere, la cui legenda completa è in \ntab), quando
+nessun manipolatore è installato un segnale può essere ignorato o causare la
+terminazione del processo.
+
+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]
   \centering
   \begin{tabular}[c]{c p{6cm}}
@@ -267,9 +356,7 @@ indicate con una lettera nel campo azione, la cui legenda 
   \label{tab:sig_action_leg}
 \end{table}
 la descrizione dettagliata del significato dei vari segnali, raggruppati per
-tipologia, è a seguire; una descrizione dettagliata del significato delle
-varie azioni si trova invece in \secred{sec:sig_handlers}.
-
+tipologia, è a seguire.
 
 \subsubsection{Segnali di errore di programma}
 \label{sec:sig_prog_error}
@@ -334,7 +421,9 @@ Questi segnali sono:
 \item \texttt{SIGABRT} Il segnale indica che il programma stesso ha rilevato
   un errore che viene riportato chiamando la funzione \texttt{abort} che
   genera questo segnale. 
-\item \texttt{SIGTRAP} 
+\item \texttt{SIGTRAP} È il segnale generato da un'istruzione di breakpoint o
+  dall'attivazione del tracciamento per il processo. È usato dai programmi per
+  il debugging e se un programma normale non dovrebbe ricevere questo segnale.
 \item \texttt{SIGSYS} Sta ad indicare che si è eseguta una istruzione che
   richiede l'esecuzione di una system call, ma si è fornito un codice
   sbagliato per quest'ultima.
@@ -358,11 +447,16 @@ periferica).
 L'azione di default di questi segnali è di terminare il processo, questi
 segnali sono:
 \begin{description}
-\item  \texttt{SIGTERM}
-\item  \texttt{SIGINT}
-\item  \texttt{SIGQUIT}
-\item  \texttt{SIGKILL}
-\item  \texttt{SIGHUP}
+\item \macro{SIGTERM} Questo è un segnale generico usato per causare la
+  conclusione di un programma. Al contrario di \macro{SIGKILL} può essere
+  intercettato, ignorato, bloccato. In genere lo si usa per chiedere in
+  maniera ``educata'' ad un processo di concludersi.
+\item \macro{SIGINT} E il segnale di interruzione per il programma. È quello
+  che viene generato di default dal comando \cmd{kill} o dall'invio sul
+  terminale del carattere di interrupt (generato dalla sequenza \macro{C-\\}).
+\item  \macro{SIGQUIT} 
+\item  \macro{SIGKILL}
+\item  \macro{SIGHUP}
 \end{description}
 
 \subsection{I segnali di allarme}
@@ -370,9 +464,8 @@ segnali sono:
 
 Questi segnali sono generati dalla scadenza di un temporizzatore. Il loro
 comportamento di default è quello di causare la terminazione del programma, ma
-nessun default ha una utlità avrebbe una utilità particolare, in quanto l'uso
-di questi segnali presuppone quasi sempre la necessità di un
-manipolatore. Questi segnali sono:
+con questi segnali la scelta di default è irrilevante, in quanto il loro uso
+presuppone sempre la necessità di un manipolatore.  Questi segnali sono:
 \begin{description}
 \item  \texttt{SIGALRM}
 \item  \texttt{SIGVTALRM}
@@ -461,14 +554,56 @@ essere controllata all'interno del flusso di esecuzione di quest'ultimo, ma
 tutto quello che si potrà fare è di specificare (al kernel, che li genera)
 quale azione andrà intrapresa quando essi si verificano.
 
-In questa sezione vedremo allora come si gestiscono i segnali, partendo dalla
-descrizione di cosa fanno le azioni di default citate in precedenza, per poi
-esaminare le funzioni usate per personalizzare la gestione dei segnali,
-analizzando tutte le problematiche relative alla gestione di eventi asincroni
-di questo tipo.
-
-
-
-\subsection{Le azioni di default}
-\label{sec:sig_default_acttion}
+In questa sezione vedremo allora come si gestiscono i segnali, esaminando le
+funzioni che si usano per effettuare la gestione dei segnali ed analizzando le
+problematiche relative alla gestione di eventi asincroni di questo tipo.
+
+
+\subsection{La funzione \func{signal}}
+\label{sec:sig_signal}
+
+L'interfaccia più semplice alla manipolazione dei segnali è costituita dalla
+funzione \func{signal}; questa funzione è definita fin dallo standard ANSI C
+che però non considera sistemi multitasking, per cui la sua definizione in
+tale standard è tanto vaga da essere del tutto inutile in un sistema unix, per
+questo ogni implementazione successiva ne ha modificato e ridefinito il
+comportamento, pur mantenendone immutato il prototipo\footnote{in realtà
+  alcune vecchie implementazioni (SVR4 e 4.3+BSD) usano parametri aggiuntivi
+  per definire il comportamento della funzione} che è:
+\begin{prototype}{signal.h}
+  {sighandler\_t signal(int signum, sighandler\_t handler)} 
+  
+  Installa una nuova funzione di gestione (manipolatore) per il segnale
+  \param{signum}, usando il manipolatore \param{handler}.
+  
+  La funzione ritorna il precedente manipolatore in caso di successo o
+  \macro{SIG\_ERR} in caso di errore.
+\end{prototype}
+
+In questa definizione si è usato il tipo \type{sighandler\_t} che è una
+estensione GNU definita in Linux che permette di riscrivere il prototipo in
+forma più leggibile dell'originario \func{void (*signal(int signum, void
+  (*handler)(int)))int)}, e che è sostanzialmente equivalente alla
+definizione:
+\begin{verbatim}
+typedef void (* sighandler_t)(int) 
+\end{verbatim}
+cioè un puntatore ad una funzione di tipo \type{void} con un parametro di tipo
+\type{int}\footnote{si devono usare le parentesi intorno al nome della
+  funzione per via delle precedenze degli operatori del C, senza di esse si
+  sarebbe definita una funzione che ritorna un puntatarore a \type{void} e non
+  un puntatore ad una funzione \type{void}}.
+
+Il numero di segnale passato in \param{signum} segnale può essere indicato
+direttamente con una delle costanti definite in \secref{sec:sig_standard}, il
+manipolatore \param{handler} invece, oltre all'indirizzo della funzione da
+chiamare all'occorrenza del segnale, può assumere anche i valori costanti
+\macro{SIG\_IGN} con cui si dice ignorare il segnale e \macro{SIG\_DFL} per
+installare l'azione di di default (si ricordi però che i due segnali
+\macro{SIGKILL} e \macro{SIGSTOP} non possono essere ignorati né
+intercettati).
+
+
+\subsection{Funzioni rientranti e default dei segnali}
+\label{sec:sig_reentrant}