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_FA开发者_运维技巧ILURE;
}
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?
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.
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.
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.
精彩评论