Reputation: 371
I see a code illustrating how to do safe signal handling. A point I don't understand that why the signal handler calls again signal (sig, catch_alarm);
. What's the reason to do that? Without it, the code works too.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/* This flag controls termination of the main loop. */
volatile sig_atomic_t keep_going = 1;
/* The signal handler just clears the flag and re-enables itself. */
void
catch_alarm (int sig)
{
keep_going = 0;
signal (sig, catch_alarm); // <----- ???
}
void
do_stuff (void)
{
puts ("Doing stuff while waiting for alarm....");
}
int
main (void)
{
/* Establish a handler for SIGALRM signals. */
signal (SIGALRM, catch_alarm);
/* Set an alarm to go off in a little while. */
alarm (2);
/* Check the flag once in a while to see when to quit. */
while (keep_going)
do_stuff ();
return EXIT_SUCCESS;
}
Upvotes: 2
Views: 2671
Reputation: 91
The short answer is the original Unix implementation would reset the signal handler to it's default value after signal is received. The code here simply demonstrate this.
signal (sig, catch_alarm);
Upvotes: 0
Reputation: 9894
The problem is that the behaviour of signal()
varies across UNIX versions, and has also varied historically across different versions of Linux (quoted from Linux man). Especially:
In the original UNIX systems, when a handler that was established using signal() was invoked by the delivery of a signal, the disposition of the signal would be reset to SIG_DFL, and the system did not block delivery of further instances of the signal. This is equivalent to calling sigaction(2) with the following flags:
sa.sa_flags = SA_RESETHAND | SA_NODEFER;
So in such a system you have to call signal()
again after a signal has been delivered. Because of these portability issues, the man page starts with:
The behavior of signal() varies across UNIX versions, and has also varied historically across different versions of Linux. Avoid its use: use sigaction(2) instead. See Portability below.
Upvotes: 4
Reputation: 133919
To put it simply: You're wrong and the code is wrong.
Or, you assume that the signal
call is superfluous - it isn't, and exists there to make the code behave correctly on platforms that use THE other allowed signal
semantics.
Also, the excerpt is not about safe signal handling with signal
function;
it is about how to pass an event out from a signal handler - for which there is only one portable way - by changing a variable of type volatile sig_atomic_t
. To write safe portable code now, you'd use the function sigaction
.
The Linux signal(2)
manuals say:
The behavior of
signal()
varies across UNIX versions, and has also varied historically across different versions of Linux. Avoid its use: usesigaction(2)
instead.
and
The only portable use of
signal()
is to set a signal's disposition toSIG_DFL
orSIG_IGN
. The semantics when usingsignal()
to establish a signal handler vary across systems (and POSIX.1 explicitly permits this variation); do not use it for this purpose.POSIX.1 solved the portability mess by specifying
sigaction(2)
, which provides explicit control of the semantics when a signal handler is invoked; use that interface instead ofsignal()
.
And
The situation on Linux is as follows:
- The kernel's signal() system call provides System V semantics.
- By default, in glibc 2 and later, the
signal()
wrapper function does not invoke the kernel system call. Instead, it callssigaction(2)
using flags that supply BSD semantics. This default behavior is provided as long as a suitable feature test macro is defined:_BSD_SOURCE
on glibc 2.19 and earlier or_DEFAULT_SOURCE
in glibc 2.19 and later. (By default, these macros are defined; see feature_test_macros(7) for details.) If such a feature test macro is not defined, thensignal()
provides System V semantics.
Now the question is which one is defined. If you compile with -std=c11
you will get the resetting semantics, because it doesn't set the _DEFAULT_SOURCE
! And then you need to rearm the SIG_ALARM
every time.
The purpose of resetting the signal in the signal handler is that some Unixen clear the handler whenever the signal is triggered. There are also other interesting edge cases - the only reason to use this function is that it is in the C standard, but its behaviour isn't well-specified there either. Never use it to set a custom signal handler.
As the code says, both of these signal
calls should be frowned upon. Good modern code shouldmust use sigaction
instead, for example
struct sigaction newsigfunc;
newsigfunc.sa_handler = catch_alarm;
sigemptyset(&newsigfunc.sa_mask);
newsigfunc.sa_flags = 0;
sigaction(SIGALRM, &newsigfunc, NULL);
sigaction
, unlike signal
, will guarantee portability here; wherever it doesn't exist, signal
is likely to misbehave too...
Upvotes: 1