\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
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
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
-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 ()
{
...
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 */
+ }
...
}
int sig_handler()
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
% 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
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}
(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 \\
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}
+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).
+