calling signal after fork
Asked Answered
B

3

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?

Bufordbug answered 28/1, 2011 at 13:15 Comment(8)
I've just tested it, works fine for me. Both processes exit gracefully via stopChild() call in both cases.Lucilucia
Are you per chance calling fork() from within a thread?Farmhand
Can you provide a full example program which exhibits the behaviour?Balustrade
who sends the SIGTERM? It's not the parent, is it? cough race cough (if the answer is yes)Titan
Not able to reproduce it with the same code today! Not sure what happened.Bufordbug
@Sabya, maybe ninjalj is right and you send the TERM signal from the parent process? In this case you really have a race here, and it could work only sometimes.Lucilucia
Exactly, it is the parent who is sending the SIGTERM.Bufordbug
@Sabya, but then it could happen that it sends the signal before the signal() call in the child is completed. That is what ninjalj meant by a race.Lucilucia
T
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.

Titan answered 31/1, 2011 at 21:41 Comment(1)
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.Bufordbug
F
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.

Farmhand answered 28/1, 2011 at 13:54 Comment(2)
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.Balustrade
@Balustrade - that's why I'm very suspicious that fork() is being called in a thread.Farmhand
B
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.

Balustrade answered 28/1, 2011 at 13:20 Comment(5)
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?Lucilucia
@Sergey, exactly that's my doubt!Bufordbug
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.Farmhand
@Sergey, @Sabya; indeed, still not sure what can cause the problem.Balustrade
I'm at a loss, leaving the (edited) answer for reference on the fork.Balustrade

© 2022 - 2024 — McMap. All rights reserved.