Cours 05 Signals
Cours 05 Signals
Cours 5 — Signals
Stefano Zacchiroli
zack@pps.univ-paris-diderot.fr
2013–2014
URL http://upsilon.cc/zack/teaching/1314/progsyst/
Copyright © 2011–2013 Stefano Zacchiroli
License Creative Commons Attribution-ShareAlike 3.0 Unported License
http://creativecommons.org/licenses/by-sa/3.0/
1 Signal concepts
4 Real-time signals
1 Signal concepts
4 Real-time signals
Definition (Signal)
A signals is a software interrupt. A signal is delivered to processes
as an asynchronous event wrt the usual execution flow.
Most of the events a UNIX process deals with are handled according
to the pull model:
when the process is ready/willing to handle an event, it uses
syscalls to check whether an event has occurred in the past and
to retrieve the associated information
In the early days of UNIX, the “reputation” of signals was pretty bad,
due to a number of unreliability causes in signal delivery. We refer
to signal implementation and specification of those days as
unreliable signals.2
The bad reputation of those days still affects the usage of signals.
2
we’ll discuss unreliability causes in a bit
Stefano Zacchiroli (Paris Diderot) Signals 2013–2014 6 / 92
Signal names
Each signal is identified by a signal name starting with SIG
<signal.h> associates signal names to platform-specific signal
numbers
available signals are standardized by SUS, although some are
XSI extensions; each platform might support additional
implementation-specific signals
signal number 0 corresponds to the null signal, which is no
signal and has special meaning for kill
Available signals on a given platform can be listed with kill(1).
Example (Linux x86 signals)
$ k i l l −l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS <snip>
$
Stefano Zacchiroli (Paris Diderot) Signals 2013–2014 7 / 92
Signal disposition
Each process can express his wishes to the kernel about what should
happen when a specific signal occurs. The signal disposition (or
action) is one of the following:
start of program
flowof execution
Kernel calls signal
handler on behalf Signal handler
Delivery of process
of signal 2
3
1 instruction m Code of
instruction m+1 signal handler
4 is executed
Program
resumes at return
point of interruption
exit()
For each signal we mention its number on the Linux x86 platform.
$ u l i m i t −c
0 core file generation is disabled
i n t main ( void ) {
sleep ( 6 0 ) ;
e x i t ( EXIT_SUCCESS ) ;
}
( gdb ) bt
#0 0x00007f0b7c7731b0 in nanosleep ( ) from / l i b /x86_64−linux−gnu/ l i b c . so .6
#1 0x00007f0b7c773040 in sleep ( ) from / l i b /x86_64−linux−gnu/ l i b c . so .6
#2 0x0000000000400542 in main ( ) at sleep . c :5
( gdb )
$
1 Signal concepts
4 Real-time signals
#include <signal.h>
void (*signal(int signo, void (*handler)(int)))(int)
Returns: previous signal disposition if OK, SIG_ERR on error
#include <signal.h>
void (*signal(int signo, void (*handler)(int)))(int)
Returns: previous signal disposition if OK, SIG_ERR on error
Demo
Upon fork
child inherits parent’s signal disposition
ñ ignored and default signals remain the same in child
ñ caught signals will continue to be caught by the same handlers
Upon exec
Upon fork
child inherits parent’s signal disposition
ñ ignored and default signals remain the same in child
ñ caught signals will continue to be caught by the same handlers
Upon exec
while ignore and default dispositions could remain the same, catch
dispositions could not: function pointers would be meaningless in
the address space of a new (different) program
#include <signal.h>
int kill (pid_t pid, int signo);
int raise(int signo);
Returns: 0 if OK, -1 on error
3
historic misnomer
Stefano Zacchiroli (Paris Diderot) Signals 2013–2014 29 / 92
Sending signals — permissions
Notes:
permission to kill a specific process can be checked by
kill-ing with the null signal
group kill-ing sends signals only to processes allowed by
permissions (without failing)
#include <unistd.h>
int pause(void);
Returns: -1 with errno set to EINTR
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
Returns: 0 or n. of seconds until previously set alarm
Using alarm, a process can set a timer that will expire seconds
seconds in the future. After timer expiration, the signal SIGALRM will
be sent to the calling process.
Note: default action for SIGALRM is process termination.
void c l o c k _ t i c k ( i n t signo ) {
p r i n t f ( " \ r%ld " , time ( NULL ) ) ; / * overwrite prev . time with new * /
alarm ( 1 ) ; / * re−set alarm * /
}
i f ( s i g n a l ( SIGALRM , c l o c k _ t i c k ) == SIG_ERR )
err_sys ( " can ’ t catch SIGALRM " ) ;
c l o c k _ t i c k ( −1); / * p r i n t current time * /
alarm ( 1 ) ;
for ( ; ; ) / * wait / catch loop * /
pause ( ) ;
e x i t ( EXIT_SUCCESS ) ;
} / * clock . c * /
Stefano Zacchiroli (Paris Diderot) Signals 2013–2014 33 / 92
alarm — example (cont.)
Demo
#include <unistd.h>
unsigned int sleep(unsigned int seconds);
Returns: 0 or number of unslept seconds
4
depending on whether sleep is implemented using alarm or not, there might
be nasty interactions among the two. . .
Stefano Zacchiroli (Paris Diderot) Signals 2013–2014 35 / 92
Unreliable-signal semantics
i n t main ( void ) {
...
s i g n a l ( SIGINT , my_handler ) ; / * e s t a b l i s h handler * /
...
}
i n t main ( void ) {
...
s i g n a l ( SIGINT , my_handler ) ; / * e s t a b l i s h handler * /
...
}
problem: there is race condition between the start of handler
execution and the re-establishment of the signal handler
a signal delivered in between will trigger default action
ñ potentially terminating the process
Demo
Let’s assume different signals are generated for the same target
process p in the order s1 , . . . sn .
What would be the delivery order of signals to p?
Demo
we don’t know what the process was doing when the signal was
delivered
What if the process:
1 was in the middle of malloc or free?
we don’t know what the process was doing when the signal was
delivered
What if the process:
1 was in the middle of malloc or free?
FAIL.
Stefano Zacchiroli (Paris Diderot) Signals 2013–2014 47 / 92
Reentrant functions
The functions that can safely be invoked from signal handlers are
called reentrant functions (or async-safe functions). The full list is
prescribed by POSIX.
Note: functions of the standard I/O library are not reentrant, due to
the usage of global data structures (e.g. buffers).
Yes, we’ve been lazy and used printf within signal handlers.
You can’t.
1 Signal concepts
4 Real-time signals
The first (trivial) part of the reliable signals API is used to manipulate
sets of signals and offers basic set operations.
The data type sigset_t is defined by POSIX.1 to represent a signal
set.
POSIX.1 functions
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset (sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
Returns: 0 if OK, -1 on error
#include <signal.h>
int sigismember(const sigset_t *set, int signo);
Returns: 1 if true, 0 if false, -1 on error
#define _GNU_SOURCE
#include <signal.h>
int sigisemptyset(sigset_t *set);
Returns: 1 if set contains no signals, 0 otherwise
#define _GNU_SOURCE
#include <signal.h>
int sigorset(sigset_t *dest, sigset_t *left, sigset_t *right);
int sigandset(sigset_t *dest, sigset_t *left, sigset_t *right);
Returns: 0 if OK, -1 on error
#include <signal.h>
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
Returns: 0 if OK, -1 on error
During the interim, signals are pending and can be retrieved by the
target process using:
#include <signal.h>
int sigpending(sigset_t *set);
Returns: 0 if OK, -1 on error
void s i g _ q u i t ( i n t signo ) {
p r i n t f ( " caught SIGQUIT\n " ) ;
i f ( s i g n a l ( SIGQUIT , SIG_DFL ) == SIG_ERR )
err_sys ( " can ’ t reset SIGQUIT " ) ;
}
i n t main ( void ) {
s i g s e t _ t newmask, oldmask , pendmask ;
i f ( s i g n a l ( SIGQUIT , s i g _ q u i t ) == SIG_ERR )
err_sys ( " can ’ t catch SIGQUIT " ) ;
sigemptyset (&newmask ) ;
sigaddset (&newmask, SIGQUIT ) ; / * block SIGQUIT * /
i f ( sigprocmask ( SIG_BLOCK , &newmask, &oldmask ) < 0)
err_sys ( " SIG_BLOCK e r r o r " ) ;
sleep ( 5 ) ; / * SIGQUIT here w i l l remain pending * /
i f ( sigpending (&pendmask ) < 0)
err_sys ( " sigpending e r r o r " ) ;
i f ( sigismember(&pendmask, SIGQUIT ) )
p r i n t f ( " \nSIGQUIT pending\n " ) ;
Demo
Demo
sigaction, not signal, should be used in all new code that deals
with signals.
#include <signal.h>
int sigaction(int signo, const struct sigaction *restrict act,
struct sigaction *restrict oact);
Returns: 0 if OK, -1 on error
struct s i g a c t i o n {
void ( * sa_handler ) ( i n t ) ; / * old−s t y l e handler * /
s i g s e t _ t sa_mask ; / * extra si g n a l s to block * /
int sa_flags ; / * signal options * /
/ * alternate , new−s t y l e handler * /
void ( * sa_sigaction ) ( int , s i g i n f o _ t * , void * ) ;
}
used instead.
signo is as before;
info is a rich structure giving information about the event that
caused the signal;
context can be cast to a ucontext_t structure that identifies
the context of the process at signal delivery time
ñ see getcontext (2)
The need of code like the following is gone! (and the behavior is no
longer implementation-dependent)
/ * signal handler * /
void my_handler ( i n t signo ) {
s i g n a l ( SIGINT , my_handler ) ; / * re−e s t a b l i s h handler * /
... / * process sign al * /
}
i n t main ( void ) {
...
s i g n a l ( SIGINT , my_handler ) ; / * e s t a b l i s h handler * /
...
}
Demo
/ * c r i t i c a l region * /
#include <signal.h>
int sigsuspend(const sigset_t *sigmask);
Returns: -1 with errno set to EINTR
set the signal mask to sigmask and put the process into sleep
sigsuspend will return after the handler of a caught signal
returns
ñ note: always return a EINTR failure
the signal mask is returned to its previous value before
returning
Stefano Zacchiroli (Paris Diderot) Signals 2013–2014 70 / 92
Helper — pr_mask
#include <errno . h>
#include <s i g n a l . h>
errno_save = errno ;
i f ( sigprocmask ( 0 , NULL , &sigs ) < 0)
err_sys ( " sigprocmask e r r o r " ) ;
p r i n t f ( "%s : " , l a b e l ) ;
i f ( sigismember(& sigs , SIGINT ) ) p r i n t f ( " SIGINT " ) ;
i f ( sigismember(& sigs , SIGQUIT ) ) p r i n t f ( " SIGQUIT " ) ;
i f ( sigismember(& sigs , SIGUSR1 ) ) p r i n t f ( " SIGUSR1 " ) ;
i f ( sigismember(& sigs , SIGUSR2 ) ) p r i n t f ( " SIGUSR2 " ) ;
i f ( sigismember(& sigs , SIGALRM ) ) p r i n t f ( " SIGALRM " ) ;
/ * etc . . . * /
p r i n t f ( " \n " ) ;
errno = errno_save ;
}
Stefano Zacchiroli (Paris Diderot) Signals 2013–2014 71 / 92
sigsuspend — example
#include <stdio . h>
#include < s t d l i b . h>
#include <s i g n a l . h>
#include " helpers . h "
void s i g _ i n t ( i n t signo ) {
pr_mask ( " \ nin s i g _ i n t " ) ;
}
i n t main ( void ) {
s i g s e t _ t newmask, oldmask , waitmask ;
$ . / sigsuspend
program s t a r t :
< c r i t i c a l region >: SIGINT
^C
</ c r i t i c a l region >: SIGINT
in s i g _ i n t : SIGINT SIGUSR1
a f t e r return from sigsuspend : SIGINT
done :
$
$ . / sigsuspend2
^C
interrupt
^C
interrupt
^C
interrupt
^\
$
5
we’ll also have TELL_WAIT in both processes, for initialization
Stefano Zacchiroli (Paris Diderot) Signals 2013–2014 76 / 92
Reminder — TELL/WAIT intended usage
i n t main ( void ) {
pid_t pid ;
TELL_WAIT();
In particular:
1 what if the process calling system is catching SIGCHLD?
In particular:
1 what if the process calling system is catching SIGCHLD?
1 Signal concepts
4 Real-time signals
void s i g _ r t ( i n t signo ) {
p r i n t f ( " caught s i g n a l %d\n " , signo ) ;
}
i n t main ( void ) {
s i g s e t _ t newmask, oldmask ;
i f ( s i g n a l ( SIGRTMIN , s i g _ r t ) == SIG_ERR
| | s i g n a l ( SIGRTMIN+1, s i g _ r t ) == SIG_ERR )
err_sys ( " s i g n a l e r r o r " ) ;
sigemptyset (&newmask ) ;
sigaddset (&newmask, SIGRTMIN ) ;
sigaddset (&newmask, SIGRTMIN +1);
i f ( sigprocmask ( SIG_BLOCK , &newmask, &oldmask ) < 0) / * block SIGRT * * /
err_sys ( " SIG_BLOCK e r r o r " ) ;
sleep ( 5 ) ;
i f ( sigprocmask ( SIG_SETMASK , &oldmask , NULL ) < 0) / * unblocks SIGRT * * /
err_sys ( " sigprocmask e r r o r " ) ;
i f ( s i g n a l ( SIGRTMIN , SIG_DFL ) == SIG_ERR ) err_sys ( " s i g n a l er ror " ) ;
i f ( s i g n a l ( SIGRTMIN+1, SIG_DFL ) == SIG_ERR ) err_sys ( " s i g n a l er ror " ) ;
p r i n t f ( " SIGRT * unblocked\n " ) ;
while ( 1 ) {
sleep ( 5 ) ;
}
e x i t ( EXIT_SUCCESS ) ;
} / * queue . c * /
Demo
#include <signal.h>
int sigqueue(pid_t pid, int sig, const union sigval value);
Returns: 0 if OK, -1 on error
The first two arguments are as in kill. The 3rd argument allow to
send signals with payloads that can be either integers or pointers:
union s i g v a l {
int sival_int ;
void * s i v a l _ p t r ;
}
Target process can retrieve the payload via the si_value field of
struct siginfo_t (only for sigaction new style handlers).
$ . / sigqueue−recv &
[ 1 ] 11546
$ . / sigqueue−send 11546 < / etc / issue
Debian GNU/ Linux wheezy/ sid \n \ l
$