Fork() and how signals are delivered to processes

forkprocesssignals

I program that I wrote in C fork()'s off a child process. Neither process will terminate. If I launch the program from the command line and press control-c which process(es) will receive the interrupt signal?

Best Answer

Why don't we try it out and see? Here's a trivial program using signal(3) to trap SIGINT in both the parent and child process and print out a message identifying the process when it arrives.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void parent_trap(int sig) {fprintf(stderr, "They got back together!\n");}
void child_trap(int sig) {fprintf(stderr, "Caught signal in CHILD.\n");}
int main(int argc, char **argv) {
    if (!fork()) {
        signal(SIGINT, &child_trap);
        sleep(1000);
        exit(0);
    }
    signal(SIGINT, &parent_trap);
    sleep(1000);
    return 0;
}

Let's call that test.c. Now we can run it:

$ gcc test.c
$ ./a.out
^CCaught signal in CHILD.
They got back together!

Interrupt signals generated in the terminal are delivered to the active process group, which here includes both parent and child. You can see that both child_trap and parent_trap were executed when I pressed Ctrl-C.

There is a lengthy discussion of interactions between fork and signals in POSIX. The most material part of it here is that:

A signal sent to the process group after the fork() should be delivered to both parent and child.

They also note that some systems may not behave in exactly the correct way, in particular when the signal arrives very close to the time of the fork(). Figuring out whether you're on one of those systems is probably going to require reading the code or a lot of luck, because the interactions are vanishingly unlikely in each individual attempt.

Other useful points are that:

  • A signal manually generated and sent to an individual process (perhaps with kill) will be delivered only to that process, regardless of whether it is the parent or child.
  • The order that the signal handlers run between processes is not defined, so you can't rely on either executing first.
  • If you don't define an interrupt handler (or explicitly ignore the signal), both processes would just exit with a SIGINT code (the default behaviour).
  • If one handles the signal non-fatally and the other doesn't, the one without the handler will die and the other will continue.
Related Question