One additional, important note: if you're using "reliable signals" (POSIX sigaction
with the corresponding sa_mask
field), you get control over how signals behave in a single-thread-single-process situation.
Consider the case of single process P1, with a signal-handler like the one you show above. Suppose that you are catching signal SIGUSR1
and having that enter the function signal_handler
. While you are inside signal_handler
, some other process P2 sends another SIGUSR1
to P1 (e.g., via kill
). This signal is "blocked" (temporarily) via sa_mask
until signal_handler
returns in P1. This is true even if you don't set any bits in sa_mask
(as long as you don't set SA_NODEFER
in sa_flags
, see below).
But, suppose you've also decided to catch SIGUSR2
with function signal_handler
. Suppose that P2 also sends a SIGUSR2
. In this case, the SIGUSR2
is (or may be) caught, starting another instance of signal_handler
running, this time on behalf of the SIGUSR2
signal.
You can prevent this by making sure that when SIGUSR1
is being handled, SIGUSR2
is temporarily blocked as well. In general you'd probably want to make SIGUSR1
blocked while SIGUSR2
is being handled. To do this, set both corresponding bits in sa_mask
:
struct sigaction sa;
memset(&sa, 0, sizeof sa);
sa.sa_flags = SA_RESTART | SA_SIGINFO; /* (decide for yourself which flags) */
sigaddset(&sa.sa_mask, SIGUSR1);
sigaddset(&sa.sa_mask, SIGUSR2);
sa.sa_sigaction = signal_handler;
error = sigaction(SIGUSR1, &sa, NULL);
if (error) ... handle error ...
error = sigaction(SIGUSR2, &sa, NULL);
if (error) ... handle error ...
The two sigaddset
calls make sure that both SIGUSR1 and SIGUSR2 are held-off (blocked, temporarily) for the duration of the function.
If you're only catching one signal, there is no need for this extra complexity, because as long as SA_NODEFER
is not set, the OS automatically adds whatever signal triggered entry to your signal handler to the "currently blocked signals" set at entry.
(Note that the OS's automatic blocking and unblocking of signals at entry and exit to your signal handler is done with sigprocmask
, using SIG_BLOCK
and SIG_SETMASK
—not SIG_UNBLOCK
—with the mask for the SIG_SETMASK
at exit set by saving the previous mask filled in via SIG_BLOCK
. Well, it's normally done inside kernel code, rather than actually calling sigprocmask
, but the effect is the same, just more efficient.)