【问题标题】:Understanding C's fork() through a simple example通过一个简单的例子理解C的fork()
【发布时间】:2013-02-22 03:49:54
【问题描述】:
#include <stdio.h>
int num = 0;
int main(int argc, char*argv[]){
    int pid;
    pid = fork();
    printf("%d", num);  
    if(pid == 0){       /*child*/
        num = 1;
    }else if(pid > 0){  /*parent*/
        num = 2;
    }
    printf("%d", num);
}

我无法理解为什么可能的输出是 0102 或 0012 或 0201 或 0021。

这是我(认为)它应该产生的结果。它命中第一个 printf 语句,无论哪个子或父首先执行, num 都没有被修改,所以首先为 0。 THEN next 是 1 或 2,然后执行下一个进程,因此再次从 0 开始(从父级复制),然后再次为 1 或 2。所以可能的输出应该是:

0101 或 0102 或 0201 或 0202

【问题讨论】:

  • argc 后面的分号是干什么的。这是一个语法错误。
  • int main(int argc ,char*argv[]) 这应该是主要的
  • 你应该包括#include &lt;unistd.h&gt;。这是fork() 的正确头文件。

标签: c fork concept


【解决方案1】:

在父子节点中,第一个printf 的num 为0。在父级和子级中,打印 0 后跟另一个值。在父进程中,另一个值为2。在子进程中,另一个值为1。

但是,需要注意的重要一点是,尽管每个进程都有一个强制顺序,即必须在另一个数字之前打印零,但对于两个进程相对于彼此的打印没有限制。

这是一个现实生活中的类比:假设我的同事和我同时下班,在杂货店停下来,然后回到家。我们知道我在我在家之前就在商店,我们知道他在他在家之前就在杂货店。但我们不知道谁先到杂货店,或者谁先到家。我们可以每个人大约在同一时间到达杂货店,然后每个人大约在同一时间到家,或者他可能延误了,我什至在他到达商店之前就到了杂货店和家。

不会发生的情况是多次打印 1 或 2。尽管在fork 返回之后,我们有两个进程在概念上同时运行,并且它们的事件相对于彼此的时间是未指定的,但每个进程中事件的顺序是明确定义的。每个进程将在再次打印之前将num 设置为 1 或 2,并且因为 fork 被定义为在子进程中返回 0 并且在父进程中返回子进程的 pid,所以它们将各自将其设置为不同的值。

实际上,还有另一个合理的输出:00。如果fork 无法创建新进程,则返回-1。在这种情况下程序将打印0ifelse ifs 将失败,因为 -1 既不是 0 也不是大于 0,num 没有改变,程序再次打印 0

如果你想深入了解C程序中效果排序的定义,搜索的关键词是“序列点”。在这个程序中,它相当简单(除了我们同时运行两个副本),但有时可能不太明显。

【讨论】:

  • .. 现在解释互斥?!
  • "在父进程中,其他值为2。"好的,因为 pid > 0 如果它的父进程。 “在子进程中,其他值为0”什么?!当我们点击子进程时,pid 将等于 0,因此将 num 更改为 1
  • 对,那我怎么会有像 0012 这样的输出呢?还是 0021?
  • @SamuelEdwinWard - 你的解释非常好。我猜洛克的下一个问题是关于互斥的。以我自己的方式,我试图给你一个赞美,因此 +1
  • @LockeMcDonnell,想象一下如果两个进程都轮流执行一行代码。这只是可能发生的许多顺序之一,但它会产生 0012 或 0021。一个打印 0,然后另一个打印 0,然后每个打印它的编号。
【解决方案2】:

这不是fork() 的问题。它是printf(),因为printf() 已被缓冲。通常,当缓冲区在末尾遇到换行符'\n'时会刷新缓冲区。但是,由于您省略了这一点,因此缓冲区的内容会保留并且不会被刷新。最后,两个进程(原始进程和子进程)都会有输出缓冲区,其中包含 0 或 1。当它最终被刷新时,您会在两个进程中看到这一点。

printf() 之后添加fflush(stdout); 并尝试。

【讨论】:

  • printf 输出到通常是缓冲的,但不是必须的。无论如何,无法保证进程之间fflush 调用的相对顺序。也不能保证在缓冲输入时fflush 只会导致一次write 调用,尽管在缓冲区中有两个字符时几乎肯定会出现这种情况。
  • 如果 FILE 缓冲区只有两个字节,我相当肯定它会导致 PC 中常用的任何 libc 上的一次写入系统调用。因此,虽然没有任何标准保证,但所有现有代码仍然可能保证这样做。
  • 因此,在实践中,如果不添加刷新,输出将是 0102 或 0201。OTOH,在轻负载的多核系统中,如果添加了刷新,在第一次打印后,它会足够慢,其他进程也会这样做,因此可以预测 then 输出通常是 0012 或 0021。结论:如果重要,请使用同步。
  • @hyde,我同意在现实世界中可能会有一个写调用;我几乎把那部分遗漏了。虽然现在我想起来了,那个 write 调用可能会返回 1。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-14
  • 1970-01-01
  • 2011-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-01
相关资源
最近更新 更多