Issue
The codes is as below, and is the same as the one in book apue3e:
#include "apue.h"
#include "sys/wait.h"
static void sig_int(int);
int
main(int argc, char *argv[]) {
pid_t pid;
char buf[MAXLINE];
int status;
if (signal(SIGINT, sig_int) == SIG_ERR) {
err_sys("signal error");
}
printf("%% ");
while (fgets(buf, MAXLINE, stdin) != NULL) {
if (buf[strlen(buf)-1] == '\n') {
buf[strlen(buf)-1] = '\0';
}
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) {
execlp(buf, buf, (char *)NULL);
err_ret("couldn't execlvp: %s\n", buf);
exit(127);
}
if ((pid = waitpid(pid, &status, 0)) < 0) {
err_sys("waitpid_error");
}
printf("%% ");
}
exit(0);
}
static void
sig_int(int signo/* arguments */) {
/* code */
printf("Interrupted\n%%3 ");
}
So, my question is why this signal handler doesn't handle the SIGINT
signal and exit immediately after pressing the Ctrl+c which i was testing on archlinux.
Solution
[W]hy this signal handler doesn't handle the
SIGINT
signal and exit immediately after pressing the Ctrl+c which i was testing on archlinux.
Given
static void
sig_int(int signo/* arguments */) {
/* code */
printf("Interrupted\n%%3 ");
}
and
signal(SIGINT, sig_int)
Your process doesn't exit when you press CTRL-C for the simple reason your signal handler doesn't cause the process to exit.
You replaced the default SIGINT
handler with your own, so the default action of exiting the process no longer happens.
Since you're running on Linux, I'll refer to the GNU glibc documentation on termination signals:
24.2.2 Termination Signals
These signals are all used to tell a process to terminate, in one way or another. They have different names because they’re used for slightly different purposes, and programs might want to handle them differently.
The reason for handling these signals is usually so your program can tidy up as appropriate before actually terminating. For example, you might want to save state information, delete temporary files, or restore the previous terminal modes. Such a handler should end by specifying the default action for the signal that happened and then reraising it; this will cause the program to terminate with that signal, as if it had not had a handler. (See Termination in Handler.)
The (obvious) default action for all of these signals is to cause the process to terminate.
...
Macro: int SIGINT
The
SIGINT
(“program interrupt”) signal is sent when the user types the INTR character (normally C-c).
The Termination in Handler glibc documentation states:
24.4.2 Handlers That Terminate the Process
Handler functions that terminate the program are typically used to cause orderly cleanup or recovery from program error signals and interactive interrupts.
The cleanest way for a handler to terminate the process is to raise the same signal that ran the handler in the first place. Here is how to do this:
volatile sig_atomic_t fatal_error_in_progress = 0; void fatal_error_signal (int sig) { /* Since this handler is established for more than one kind of signal, it might still get invoked recursively by delivery of some other kind of signal. Use a static variable to keep track of that. */ if (fatal_error_in_progress) raise (sig); fatal_error_in_progress = 1; /* Now do the clean up actions: - reset terminal modes - kill child processes - remove lock files */ … /* Now reraise the signal. We reactivate the signal’s default handling, which is to terminate the process. We could just call exit or abort, but reraising the signal sets the return status from the process correctly. */ signal (sig, SIG_DFL); raise (sig); }
Also, note that there can be significant differences between signal()
and sigaction()
. See What is the difference between sigaction and signal?
Finally, calling printf()
from with a signal handler is undefined behavior. Only async-signal-safe functions can be safely called from within a signal handler. See POSIX 2.4 Signal Concepts for the gory details.
Answered By - Andrew Henle Answer Checked By - Senaida (WPSolving Volunteer)