Several system calls exist in Linux to allow the various actions to be set up on an individual signal basis For compatibility with other UNIX flavors, Linux supports the signal() system call, in addition to the POSIX specified calls that we shall see later. The prototype for the signal() call is:
#include <signal.h> #include <unistd.h> void (*signal(int signum, void (*handler)(int)))(int);
This prototype looks horribly complicated, but all it says is that signal() is a call that takes two parameters: the first (signum) is the signal number from the previous list for which an action is being set, and the second (handler) is a pointer to a function which takes a single integer parameter and returns nothing (void). The return value from signal() is itself a pointer to a function which takes a single integer parameter and returns nothing (void). A simple code fragment should help to illustrate the point:
#include <stdio.h> #include <signal.h> #include <unistd.h> int ctrl_c_count = 0; void (* old_handler)(int); void ctrl_c(int); main() { int c; old_handler = signal(SIGINT, ctrl_c); while ((c = getchar())!='\n'); printf("ctrl-c count = %d\n", ctrl_c_count); (void) signal(SIGINT, old_handler); for (;;); } void ctrl_c(int signum) { (void) signal(SIGINT, ctrl_c); ++ctrl_c_count; }
This program effectively performs the trivial operation of getting characters until a newline character is typed and then going into an infinite loop. Breaking out of such a program could normally be achieved with a keyboard generated interrupt signal (such as Ctrl-c). However, in this case the program arranges to catch the Ctrl-c signal (SIGINT) and use it to execute a signal handler function called ctrl_c(). Only after a newline is entered at the keyboard is the previous (probably default) action of SIGINT restored.
Setting up the signal handler is done by the first statement in the main() function:
old_handler = signal(SIGINT, ctrl_c);
The two parameters to signal() are the signal number whose action is to be set (here SIGINT, the keyboard interrupt signal), and a pointer to the function that will be called when this interrupt signal occurs. The signal() call returns the old signal handler address, which in this case is being assigned to the variable oldAiandler so that the original signal handler can be restored later.
Once the signal handler is in place, any interrupt (SIGINT) signals that the process receives will cause the execution of the handler function. This function just increments the value of the ctrl_c_count variable to keep a count of the number of SIGINT events that occur. Notice that the handler function also executes another signal() call which re-establishes the connection between the SIGINT signal and the ctrl_c() function. This is necessary because signal handlers set up with the signal() call are automatically reset when the signal occurs so that a subsequent signal of that type would otherwise just perform the signal's default action.
This is the main problem with the early UNIX signal() call (and the reason why POSIX signals are different). The problem is that there is a small (but real) time interval between a signal's action being reset to the default when the signal occurs, and the handler being executed so that the signal's action of calling the handler can be re-established. In this interval if another signal of the same type were to be received then the default action would take place, which would usually terminate the process.