6

Is there any difference between "code listing 1" and "code listing 2"? Because in Code Listing 1, the child process is able to catch the SIGTERM signal and exit nicely. But code listng 2 is terminating abruptly on SIGTERM signal.

I am using Linux and C.

Code Listing 1

if (signal(SIGTERM, stopChild) == SIG_ERR) {
    printf("Could not attach signal handler\n");
    return EXIT_FAILURE;
}
pid = fork();

Code Listing 2

pid = fork();
if (signal(SIGTERM, stopChild) == SIG_ERR) {
    printf("Could not attach signal handler\n");
    return EXIT_FAILURE;
}

The strange part is that in Code Listing 2, both child and parent process sets the signal handler for SIGTERM. So, this is supposed to work. Isn't it?

Sabya
  • 11,534
  • 17
  • 67
  • 94

3 Answers3

6

If you are sending the SIGTERM from the parent, the end result depends on the order on which processes get scheduled.

If the child gets scheduled first, everything works:

                                                +---------------+
                                                | pid = fork(); |
                                                +-------+-------+
                   parent                               |                               child
                          +-----------------------------+-----------------------------+
                          |                                                           |
                          |                                 +-------------------------+--------------------------+
                          |                                 | if (signal(SIGTERM, stopChild) == SIG_ERR) {       |
                          |                                 |       printf("Could not attach signal handler\n"); |
                          |                                 |       return EXIT_FAILURE;                         |
                          |                                 | }                                                  |
                          |                                 +-------------------------+--------------------------+
                          |                                                           |
                          .                                                           .
                          .                                                           .
                          .                                                           .
                          |                                                           |
+-------------------------+--------------------------+                                |
| if (signal(SIGTERM, stopChild) == SIG_ERR) {       |                                |
|       printf("Could not attach signal handler\n"); |                                |
|       return EXIT_FAILURE;                         |                                |
| }                                                  |                                |
+-------------------------+--------------------------+                                |
                          |                                                           |
                          |                                                           |
                          |                                                           |
            +-------------+-------------+                                             |
            | if (pid > 0) {            |                                             |
            |       kill(pid, SIGTERM); |                                             |
            | }                         |                                             |
            +-------------+-------------+                                             |
                          |                                                           |
                          |                                                           |
                          |                                                           |

But if the paren gets scheduled first, the child may have not had time to setup the signal handler:

                                                +---------------+
                                                | pid = fork(); |
                                                +-------+-------+
                   parent                               |                               child
                          +-----------------------------+-----------------------------+
                          |                                                           |
+-------------------------+--------------------------+                                |
| if (signal(SIGTERM, stopChild) == SIG_ERR) {       |                                |
|       printf("Could not attach signal handler\n"); |                                |
|       return EXIT_FAILURE;                         |                                |
| }                                                  |                                |
+-------------------------+--------------------------+                                |
                          |                                                           |
                          |                                                           |
                          |                                                           |
            +-------------+-------------+                                             |
            | if (pid > 0) {            |                                             |
            |       kill(pid, SIGTERM); |                                             |
            | }                         |                                             |
            +-------------+-------------+                                             |
                          |                                                           |
                          .                                                           .
                          .                                                           .
                          .                                                           .
                          |                                                           |
                          |                                 +-------------------------+--------------------------+
                          |                                 | if (signal(SIGTERM, stopChild) == SIG_ERR) {       |
                          |                                 |       printf("Could not attach signal handler\n"); |
                          |                                 |       return EXIT_FAILURE;                         |
                          |                                 | }                                                  |
                          |                                 +-------------------------+--------------------------+
                          |                                                           |
                          |                                                           |
                          |                                                           |

This is called a race condition, because the end result depends on who gets to run first.

ninjalj
  • 42,493
  • 9
  • 106
  • 148
  • I don't think this was the problem because the parent process were given sleep of 10 seconds before it sends the signal. Anyway, I'm accepting this as the answer now as I can not reproduce the problem. – Sabya Feb 01 '11 at 06:57
3

First, signal() is deprecated, it's better to use sigaction(). I don't think fork() is in danger of vanishing altogether since so many things use it, but sigaction() does provide a much nicer interface.

The behavior you are experiencing is commonly caused by calling fork() from within a thread. POSIX addresses this specifically:

A process shall be created with a single thread. If a multi-threaded process calls fork(), the new process shall contain a replica of the calling thread and its entire address space, possibly including the states of mutexes and other resources. Consequently, to avoid errors, the child process may only execute async-signal-safe operations until such time as one of the exec functions is called. [THR] Fork handlers may be established by means of the pthread_atfork() function in order to maintain application invariants across fork() calls.

When the application calls fork() from a signal handler and any of the fork handlers registered by pthread_atfork() calls a function that is not asynch-signal-safe, the behavior is undefined.

This means, rather than inheriting a copy of the parent's entire address space, you inherit only a copy of the calling threads address space, which doesn't contain your handlers. It might be conceivable that you are, indeed (perhaps even unwittingly) calling fork() from within a thread.

A child process gets a carbon copy of the parent's address space. The only difference with signals would be pending signals, which the child will not receive as it gets a signal set initialized to zero. But yes, it does get a copy of the handlers.

Tim Post
  • 33,371
  • 15
  • 110
  • 174
  • calling signal before the fork is actually the succesful one, so signal dispositions do get copied to the child, calling it after fork is the case that breaks, i.e. somehow calling signal in the child does not work. – wich Jan 28 '11 at 13:59
  • @wich - that's why I'm very suspicious that fork() is being called in a thread. – Tim Post Jan 28 '11 at 14:45
1

Well, according to man fork:

The fork(), fork1(), and forkall() functions create a new process. The address space of the new process (child processcess) is an exact copy of the address space of the calling process (parent process). The child process inherits the following attributes from the parent process:

...

o signal handling settings (that is, SIG_DFL, SIG_IGN, SIG_HOLD, function address)

In the first example the signal handler will be copied from the context of the parent to the forked child. But I can not explain why in the second example setting the signal handler in the child would fail.

wich
  • 16,709
  • 6
  • 47
  • 72
  • 1
    But in the second example signal() is called for both processes. So it should set the signal handler for the child process as well, shouldn't it? – Sergei Tachenov Jan 28 '11 at 13:30
  • @Sergey, exactly that's my doubt! – Sabya Jan 28 '11 at 13:35
  • POSIX says that any _pending_ signals for the parent aren't sent to the child, it starts with a signal set initialized to zero. But, the child _should_ inherit the handlers. Calling fork() within a thread in the absence of pthread_atfork() might explain what the OP is experiencing, however. – Tim Post Jan 28 '11 at 13:47
  • @Sergey, @Sabya; indeed, still not sure what can cause the problem. – wich Jan 28 '11 at 14:01
  • I'm at a loss, leaving the (edited) answer for reference on the fork. – wich Jan 28 '11 at 14:15