为什么会调用两次main函数?

5

我刚学习了关于fork的知识,据我理解,子进程从调用fork开始执行(否则fork会递归?)。

但是在这段代码中(ideone link):

int main() {
  printf("%d: Common code1\n", getpid());
  if (fork() != 0) {
    printf("%d: Parent code\n", getpid());
  } else {
    printf("%d: Child code\n", getpid());
  }
  printf("%d: Common code\n", getpid());
}

输出为:
27380: Common code1
27380: Parent code
27380: Common code
27380: Common code1
27383: Child code
27383: Common code

我不明白为什么第四行会被打印出来?如果它是从子进程中打印并且fork调用了main,我可以理解,但它是从父进程中打印的,而fork没有调用main。


可能是重复问题:为什么输出会打印两次? - leppie
4
你真的需要选择两个极长,仅在一个小字符上有所不同的字符串吗?你这么讨厌我们吗? - Kerrek SB
1
这几乎肯定是一个重复的问题,但不是那个问题的重复。在那里,fork在任何printf之前被调用,并且缓冲不是问题。 - William Pursell
@Jukurrpa,我在ideone上运行它(问题中的链接),这是我得到的输出。 - Jonathan.
1
如果您在终端中运行代码,则输出可能与在Ideone等环境中运行它时不同,因为Ideone和类似服务使用文件重定向。 - millinon
显示剩余4条评论
3个回答

9

不错的问题!一开始我有点困惑。

当您使用 printf 时,输出是缓冲的。这意味着由 printf 打印的内容直到达到换行符或程序终止(如果 stdout 被重定向)才会实际刷新到控制台。

在这种情况下,父进程 PID 的 stdout 缓冲区在 fork 期间被复制到子进程。然后,父进程和子进程都在终止之前向它们的缓冲区写入。这会导致重复的数据被打印。

这里 是一个包含在调用 fork() 之前加上 fflush(stdout); 的 ideone 链接。


标准输出(stdout)不是一个“控制台单元”。请勿将tty与stdout混淆,因为它们是不同的东西。 - William Pursell
我并不是试图解释stdout背后的魔法;我试图解释观察到的效果与问题相关,并提供解决方案。感谢澄清! - millinon

3
问题出在缓冲上。当stdout没有与终端相关联时,它不是行缓冲的。你写的字符串足够短以至于可以保留在缓冲内,只有当程序终止时才被写出。因此,您首先看到进程27380倾倒其缓冲区,然后看到进程27383倾倒其缓冲区。行27380:共同代码1在分叉之前还没有被刷新,因此它位于原始进程和分叉进程的缓冲区中。调用fflush()就在分支之前解决这个问题。

2

缓冲。Printf通常不会向stdout输出任何内容,它只是更新一些内部数据结构。这些数据结构在子进程中被复制,在流刷新时写入数据。在fork之前,应该使用flush(stdout)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接