分叉,信号以及它们如何与C中的全局变量进行交互

时间:2010-12-16 16:57:37

标签: c++ c signals

我试图了解fork()/ Linux Kernel如何处理全局变量。

给定代码:

#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 infinite 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);
}

输出为2123 在fork()和信号处理程序被调用之后,Unix / Linux内核如何处理全局变量?他们是否在儿童和儿童之间分享?父母?

我对此代码的另一个问题是kill()&amp; wait()将处理全局变量以及它们将使用的集合 - 父级或子级?他们会调用信号处理程序???

谢谢!

3 个答案:

答案 0 :(得分:5)

孩子获得全局变量的独立副本。这两个副本不共享

答案 1 :(得分:3)

fork在当前状态下创建进程的副本。除了显式映射的共享内存资源(匿名共享映射,共享文件映射,sysv共享内存块和POSIX共享内存块)之外,不会共享任何内容。

您还应该知道,虽然新进程有自己的文件描述符表副本,但这些文件描述符引用内核中相同的“打开文件描述”。除其他外,他们共享当前的寻求职位。

有关详细信息,请参阅:

http://www.opengroup.org/onlinepubs/9699919799/functions/fork.html

答案 2 :(得分:3)

fork()之后,整个过程(包括所有全局变量)都是重复的。子项是父项的完全副本,但它具有不同的PID,不同的父项,并且fork()返回0。

子进程中的信号处理程序将使用子进程的全局变量的独立副本。

您看到2打印两次的原因是打印后没有刷新标准输出。这就是:

  • counter等于2.
  • 父进程执行printf("%d", counter);,将"2"放入stdout输出缓冲区,但刷新它。尚未显示任何输出。
  • 调用
  • fork(),它复制了该过程。现在有两个counter变量副本,两者都设置为2.还有两个stdout输出缓冲区实例,两个实例都包含字符串"2"。尚未显示任何输出。
  • 父母将SIGUSR1发送给孩子,并阻止wait()
  • 子进程执行handler1(),将子进程的counter副本减1,并将"1"放入子进程的stdout输出缓冲区(现在包含{{1} }})。
  • 孩子执行"21",作为副作用冲洗exit(0)。输出stdout现在出现,由孩子写,孩子退出。
  • "21"在父进程中返回。父级将其wait()的副本增加到3,然后将counter输出到其"3"输出缓冲区(现在包含stdout)。
  • 父母执行"23",作为副作用冲洗exit(0)。输出stdout现在出现,父输出退出。

如果您将"23"放在fflush(stdout);之前,则fork()只会打印一次,输出将为2。最好在调用"213"之前刷新所有缓冲的输出流。