To answer the two actual questions you had, the program continues running because your signal handler returns, and nothing you have written in your signal handler causes your program to terminate, so your program continues as normal. If you want your program to terminate, you'd need to call _exit()
or _Exit()
from it.
The reason a second SIGTSTP
causes your program to terminate is because signal()
is an outdated and unreliable interface that is also not standard across Unix-like platforms. The original behavior is that after registering your signal handler with signal()
, when that signal is actually delivered, first the disposition of that signal is reset to SIG_DFL
, and then your signal handler is run. So after the first delivery of that signal, your signal handler is not registered anymore, unless you call signal()
again. So, when the signal is delivered to your program a second time, your signal handler is not called, and the default action is taken, which in this case is to stop the process. This behavior is different on BSD-derived systems, where the signal handler remains registered. So really, the only reliable thing you can do with signal()
is to set the disposition to SIG_IGN
or SIG_DFL
. For anything else, you should use the modern reliable signals interface with sigaction()
.
There are some other problems with your program. First, printf()
, along with most standard library functions, are not safe to call from a signal handler. POSIX defines a complete list of asynchronous-signal-safe functions that can be safely called from a signal handler. Instead, you should in this case set a flag, of type volatile sig_atomic_t
, and set that in your handler.
Second, the way you use strcpy()
here in particular is dangerous. Suppose number
currently contains "Two"
. Then, on the next iteration of your loop, it copies in 'T'
, 'h'
, 'r'
, and 'e'
, the last character overwriting the existing terminating null character. Then the signal is delivered and this string gets passed to printf()
. Even without the problems of calling printf()
from a signal handler, you're sending it an array of char
which is not terminated by a null character, and you'll incur undefined behavior and probably segfault.
Here's a better version of your example, using the sigaction()
interface, and not calling unsafe functions from the signal handler:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
char * nums[] = {"One", "Two", "Three", "Four", "Five"};
volatile sig_atomic_t stop;
void handler(int n)
{
stop = 1;
}
int main(void)
{
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
if ( sigaction(SIGTSTP, &sa, NULL) == -1 ) {
perror("Couldn't set SIGTSTP handler");
exit(EXIT_FAILURE);
}
int n = 0;
while ( !stop ) {
puts(nums[n]);
n = (n + 1) % 5;
}
puts("Signal handler called, exiting.");
return 0;
}
with output:
paul@horus:~/src/sandbox$ ./sig
One
Two
Three
Four
Five
One
^ZTwo
Three
Four
Five
One
Signal handler called, exiting.
paul@horus:~/src/sandbox$
You can see there's a delay between inputting Ctrl-Z
and the actual delivery of the signal, which is perfectly normal and to be expected based on how the kernel actually delivers signals.