Whatever book or article you are quoting is quite correct... for some compilation environments.
It would, generally speaking, be a bad idea if a signal handler function could be interrupted by the same signal. That would result in a recursive invocation of the signal handler, and make it much more difficult to write signal handlers.
So it is not permitted, at least by default. But Posix allows two radically different mechanisms to prevent nested handler calls. When signal
has been called with a signal handler function as an argument (that is, neither SIG_DFL
nor SIG_IGN
) and the corresponding signal sig
is raised:
… it is implementation-defined whether the equivalent of a:
signal(sig, SIG_DFL);
is executed or the implementation prevents some implementation-defined set of signals (at least including sig) from occurring until the current signal handling has completed. (From the Posix description of signal()
.)
Both block the signal during the execution of the signal handler, but the first option does not restore the signal handler, while the second one will leave the signal blocked if the signal handler does not return normally (eg. through longjmp
or execve()
).
If you are writing a program which might be run on different operating systems, or even on the same operating system with a different compiler, or even on the same machine with the same compiler but different compiler options, then you cannot count on one or the other behaviour. And there is no portable way to tell which one occurred.
For this reason, signal
should not be used to install signal handlers; use sigaction
, as recommended by Posix:
The sigaction()
function provides a more comprehensive and reliable mechanism for controlling signals; new applications should use sigaction()
rather than signal()
.
(from the same Posix page, in the "Application Usage" section. Note that this advice is 20 years old; "new applications" should be interpreted in that context.)
Just to demonstrate how difficult it is to make conclusions on the basis of a single test, I modified your program to use write
rather than printf
, in order to avoid the restriction on use of printf
in a signal handler, and compiled it on a reasonably modern Ubuntu system using gcc's default settings, and the --std=c11
option:
$ cat sig.c
#include <signal.h>
#include <unistd.h>
void sighup() {
write(2, "SIGHUP\n", 7);
}
int main(){
signal(SIGHUP, sighup);
while(1){
raise(SIGHUP);
sleep(1);
}
return 0;
}
$ gcc -Wall -o sig sig.c
$ ./sig
SIGHUP
SIGHUP
SIGHUP
SIGHUP
SIGHUP
^C
$ gcc -Wall -std=c11 -o sig sig.c
$ ./sig
SIGHUP
Hangup
$
The explanation for this behaviour is found in the Linux manpage for signal
, which you might want to read (to the end :-) ). Basically, Linux natively provides the SIG_DFL
semantics, but glibc's wrapper function actually calls sigaction
in order to implement the alternative handling, blocking the signal until the handler returns. Except that glibc only uses this alternative implementation if the feature-test macro _DEFAULT_SOURCE
has been set. Gcc sets this macro by default, but since it enables features outside of the C standard, it is not set if you request a standard C compilation environment.