Note that you never finish handling the first signal because your signal handler doesn't return timely because of the pause()
. You are probably going to have to use a loop (maybe, or maybe not, an infinite loop) to get repetition.
When the first signal arrives, your handler is called. Your function illicitly uses printf()
to record this. It sets a new alarm (which is valid), and then pauses. That means that the signal handler hasn't returned, and signals blocked by the use of the signal handler remain blocked.
You need to return from the signal handler to unblock the signals.
See the POSIX specification of sigaction()
for details, or your system's manual page for it.
POSIX says:
When a signal is caught by a signal-catching function installed by sigaction()
, a new signal mask is calculated and installed for the duration of the signal-catching function (or until a call to either sigprocmask()
or sigsuspend()
is made). This mask is formed by taking the union of the current signal mask and the value of the sa_mask for the signal being delivered, and unless SA_NODEFER or SA_RESETHAND is set, then including the signal being delivered. If and when the user's signal handler returns normally, the original signal mask is restored.
So, further alarm signals are blocked until your handler returns unless you've specified SA_RESETHAND (which would not help you — it would set the signal handler back to SIG_DFL
) or SA_NODEFER.
If you used plain signal()
, you end up with lots of system-specific behaviour — POSIX allows a lot of different behaviours because existing systems exhibited widely divergent behaviour when POSIX was standardized. You'll need to read your system's manual page on signal()
very carefully.
Undefined behaviour
You say your code for setting the signal handling is:
void signal_handling(){
struct sigaction sa1;
struct sigaction sa2;
struct sigaction sa3;
struct sigaction sal;
sa1.sa_handler = handle_sigterm;
sa2.sa_handler = handle_sigusr1;
sa3.sa_handler = handle_sigusr2;
sigemptyset(&sal.sa_mask);
sal.sa_handler = handle_sigalarm;
sal.sa_flags = SA_RESTART;
sigaction(SIGTERM, &sa1, NULL);
sigaction(SIGUSR1, &sa2, NULL);
sigaction(SIGUSR2, &sa3, NULL);
sigaction(SIGALRM, &sal, NULL);
}
This code invokes undefined behaviour because the structures are not properly initialized. You neither set the sa_mask
values nor the sa_flags
for sa1
, sa2
or sa3
. That means you've no idea what will happen if SIGTERM, SIGUSR1 or SIGUSR2 arrives.
I suggest using this notation to initialize the structures fully:
struct sigaction sa1 = { 0 };
Tangential observation
I also note a weirdness on macOS that the sig*set()
functions are implemented as macros with the form (…change bits…, 0)
, which then generates compiler warnings about 'unused value':
…/signal.h:120:44: error: right-hand operand of comma expression has no effect [-Werror=unused-value]
120 | #define sigemptyset(set) (*(set) = 0, 0)
It's a nuisance — either test the result or explicitly use a (void)
cast. I use the GCC options:
gcc -g -O3 -std=c18 -pedantic -Wall -Wextra -Werror -Wshadow -Wmissing-prototypes \
-Wpointer-arith -Wold-style-definition -Wcast-qual -Wstrict-prototypes \
…