Scritta un'altra po' di roba sui segnali.
[gapil.git] / signal.tex
index 4fb155da3aa856a28402d76c2596b69423bdb285..c1923626387899e29323bcad5b14cdbee5f67bbe 100644 (file)
@@ -41,13 +41,14 @@ generazione un particolare tipo di segnale.
 \subsection{Le modalità di funzionamento}
 \label{sec:sig_semantics}
 
 \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.  Si
-possono individuare due tipologie fondamentali di comportamento dei segnali
-(dette semantiche) che vengono chiamate rispettivamente \textit{reliable} e
-\textit{unreliable}.
+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
 
 Nella semantica \textit{unreliable} (quella implementata dalle prime versioni
 di unix) la routine di gestione del segnale specificata dall'utente non resta
@@ -55,10 +56,26 @@ installata una volta chiamata; 
 l'installazione all'interno della routine di gestione stessa in tutti i casi
 in cui si vuole che il signal handler esterno resti attivo.
 
 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.  In questo caso il segnale può essere perso o causare il comportamento
-originale assegnato al segnale (in genere la terminazione del processo).
+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
 
 Questa è la ragione per cui l'implementazione dei segnali secondo questa
 semantica viene chiamata \textit{inaffidabile}, in quanto la ricezione del
@@ -66,15 +83,16 @@ segnale e la reinstallazione del suo manipolatore non sono operazioni
 atomiche.
 
 Un'altro problema è che in questa semantica è che non esiste un modo per
 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
+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
 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, se si considera il seguente segmento di
-codice: 
-\begin{lstlisting}
+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 signal_flag = 0;
 main ()
 {
@@ -82,8 +100,9 @@ main ()
     ...
     signal(SIGINT, sig_handler);  /* establish handler */
     ...
     ...
     signal(SIGINT, sig_handler);  /* establish handler */
     ...
-    while(signal_flag == 0)       /* while flag is zero */
+    while(signal_flag == 0) {     /* while flag is zero */
         pause();                  /* go to sleep */
         pause();                  /* go to sleep */
+    }
     ... 
 }
 int sig_handler() 
     ... 
 }
 int sig_handler() 
@@ -92,13 +111,17 @@ int sig_handler()
     signal_flag = 1;              /* set flag */
 }
 \end{lstlisting}
     signal_flag = 1;              /* set flag */
 }
 \end{lstlisting}
-
-
-% non supporta l'ultima delle due ma vale la pena parlarne
-% dato che è stata la prima ad essere stata implementata (e se ne trovano
-% conseguenze in alcuni programmi e funzioni di libreria) ed illustra bene
-% alcune delle caratteristiche dei segnali.
-
+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'altra caratteristica della implementazione inaffidabile è che le chiamate
 % di sistema non sono fatte ripartire automaticamente quando sono interrotte da
@@ -106,10 +129,9 @@ int sig_handler()
 % chiamata al sistema e riperterla nel caso l'errore riportato da \texttt{errno}
 % sia \texttt{EINTR}.
 
 % 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.
+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
 
 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
@@ -125,16 +147,20 @@ esso 
 dal kernel quando, riprendendo l'esecuzione del processo in questione, verifica
 la presenza del flag del segnale nella process table.
 
 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 può bloccare i segnali 
+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.
 
 
 
 
-% Torneremo su
-% questo più avanti in \secref{sec:sig_linux_sematic}.
 
 
-% 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}).
 
 
 \subsubsection{Tipi di segnali}
 
 
 \subsubsection{Tipi di segnali}
@@ -256,39 +282,40 @@ In \ntab\ si 
 (estratto dalle man page), comparati con quelli definiti in vari standard.
 \begin{table}[htb]
   \centering
 (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
     \hline
-    Segnale  & POSIX.1 & SUSv2 & Linux  &Azione &  Descrizione                \\
+    Segnale  & POSIX.1 & SUSv2 & Linux  &Azione &  Descrizione \\
     \hline
     \hline
     \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\\
     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             \\
     SIGIOT    &&&$\bullet$& C &     IOT trap. A synonym for SIGABRT        \\
     SIGEMT    &&&$\bullet$&   &                                            \\
     SIGSTKFLT &&&$\bullet$& A &     Stack fault on coprocessor             \\
@@ -527,13 +554,48 @@ funzioni che si usano per effettuare la gestione dei segnali ed analizzando le
 problematiche relative alla gestione di eventi asincroni di questo tipo.
 
 
 problematiche relative alla gestione di eventi asincroni di questo tipo.
 
 
-\subsection{La semantica dei segnali in Linux}
-\label{sec:sig_linux_semantic}
-
-
-
 \subsection{La funzione \func{signal}}
 \label{sec:sig_signal}
 
 \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 i valori costanti
+\macro{SIG\_IGN} per ignorare il segnale e \macro{SIG\_DFL} per installare
+l'azione di di default (si ricordi però che \macro{SIGKILL} e \macro{SIGSTOP}
+non possono essere ignorati né intercettati).
+