Forking, Signals and how they interact with global variables in C
I am trying to understand how fork()/Linux Kernel deals with global variables.
Given code:
#include<signal.h>
#include<unistd.h>
#include<stdio.h>
#include<errno.h>
#include <sys/types.h>
pid_t pid;
int counter = 2;
void handler1(int sig)
{
counter = counter - 1;
printf("%d", counter);
exit(0);
}
int main()
{
signal(SIGUSR1, handler1); //Install Handler
printf("%d", counter); //Print Parent global variable
pid = fork( ); //Fork(), child pid = 0, parent's pid = positive int.
if (pid == 0) //Parent skips this, child goes into infinite loop
{
while(1) {}; // simulate doing some work
}
kill(pid, SIGUSR1); //While child is the loop, parents calls to terminate the child.
//Child will stop the inf开发者_如何学Cinite loop, and will not proceed any
//Will it call handler1 ???
wait(NULL); //Wait for child to be ripped
//Will it call handler1 second time ???
counter = counter + 1; //This will surely increment global variable
printf("%d", counter);
exit(0);
}
The output is 2123
How does Unix/Linux kernel deals with global variables after fork() and signal handlers are called ??? Do they get shared between child & parent ?
Another issues I have with this code, is how kill() & wait() will deal with global variables and what set will they use - parent's or child's ?? And will they call signal handler ???
Thanks !
The child gets an independent copy of the global variables. The two copies are not shared.
After fork()
, the entire process, including all global variables, is duplicated. The child is an exact replica of the parent, except that it has a different PID, a different parent, and fork()
returned 0.
A signal handler in the child will use the child's independent copy of the global variable.
The reason you're seeing 2
printed twice is that you haven't flushed standard output after printing it. This is what happens:
counter
is equal to 2.- Parent process executes
printf("%d", counter);
, which puts"2"
into thestdout
output buffer, but does not flush it. No output appears yet. fork()
is called, which duplicates the process. There are now two copies of thecounter
variable, and both are set to 2. There are also two instances of thestdout
output buffer, both of which contain the string"2"
. No output appears yet.- The parent sends
SIGUSR1
to the child, and blocks onwait()
. - The child executes
handler1()
, which decrements the child's copy ofcounter
to 1, and puts"1"
into the child'sstdout
output buffer (which now contains"21"
). - The child executes
exit(0)
, which as a side-effect flushesstdout
. The output"21"
appears now, written by the child, and the child exits. wait()
returns in the parent process. The parent increments its copy ofcounter
to 3, and then prints"3"
into itsstdout
output buffer (which now contains"23"
).- The parent executes
exit(0)
, which as a side-effect flushesstdout
. The output"23"
appears now, and the parent exits.
If you put fflush(stdout);
before the fork()
, the 2
will only be printed once, and the output will be "213"
. It is good practice to flush all buffered output streams before calling fork()
.
fork
creates a copy of the process in its current state. Nothing is shared except explicitly-mapped shared memory resources (anonymous shared maps, shared file maps, sysv shared memory blocks, and POSIX shared memory blocks).
You should also be aware that while the new process has its own copy of the file descriptor table, these file descriptors refer to the same "open file descriptions" in the kernel. They share a current seek position, among other things.
For further details, see:
http://www.opengroup.org/onlinepubs/9699919799/functions/fork.html
精彩评论