gcc编译器中的fork()函数是如何工作的?

3

我有一个包含fork()调用的C程序。

#include <stdio.h>
#include <unistd.h>

main ( )
{
    fork () ;
    printf ( "\nHello" ) ;
    fork () ;
    printf ( "\nWorld" ) ;
}

这是我在gcc上得到的输出:

Hello Hello World World World World 

这应该是:

Hello Hello World World Hello Hello World World

由于以下原因:
Line1: First fork creates a child process.
1.2: Prints 'Hello'
1.3:Creates a child process because of 2nd fork in it.
1.3.2:Prints 'Hello' and 'World'.
1.4:Prints 'World'.
Line2: Prints 'Hello'.
Line3: Second fork() creates child process
3.1:creates a child process
3.2: Prints 'Hello'
3.4: Prints 'World'
Line 4: Prints 'World'

这是我认为该程序的运作方式。请问您能否解释我哪里出错了?这就是为什么我想要了解函数如何运作的原因。
注意:我查看了手册,但我不太理解其中的大部分内容。
此外,数字表示main函数中的行号。例如,1.2对应于由main()函数的第一行fork()调用创建的子进程的第二行。
我无法弄清楚它是如何产生特定输出的。所以我希望了解fork()在简单步骤中发生了什么?

1
顺便提一下,在每个 printf 后面添加 fflush(stdout),否则由于 C 库缓冲,输出可能变得更加复杂难懂。 - Matteo Italia
3个回答

11

所以:

  1. 没有确切的保证事情会按照什么顺序发生(这完全取决于调度器),除了第一件事肯定是“Hello”,最后一件事肯定是“World”。
  2. 当你到达“Hello”行时,程序中有两个副本(一个是fork出来的,另一个是原始的),因此“Hello”将打印两次。每个副本都fork出另一个副本,因此当你到达“World”时,会有四个副本,所以“World”将打印四次。
  3. 当你fork()一个副本时,它从那里开始执行;它不会从头开始执行。

但我总是得到相同的输出。如果 fork 创建了一个从那里开始的副本,那么输出应该是:Hello World World Hello World World 吗? - BharathYes
1
@Bharath是的,我并不感到惊讶;系统可能会按照相同的顺序安排相同的事件(即每次运行程序都执行相同的操作,因此可能每次都以相同的方式进行调度)。一个线程领先于其他线程太多的情况非常非常非常惊人(fork需要一些时间,与printf相比),但理论上一个进程可以在另一个进程打印任何内容之前完成。 - tabstop
1
所以你的意思是,由于线程彼此并行执行,它们会稍微有时间差地打印出“Hello”和“World”。我现在明白了。感谢您的耐心解释。 - BharathYes
2
@BharathYes,回答额外的问题:一旦你分叉,你就不再有一个线性路径,而是有平行路径。原始版本和第一个副本都会打印“Hello”和“World”;第二个和第三个副本将各自只打印“World”--但这四个进程都是独立的进程,你无法控制它们发生的顺序。它们也不必在新的进程接管之前运行到完成;它们可以根据操作系统的自由裁量进行切换控制。 - tabstop

3
当你使用fork()创建一个子进程时,它会从代码的下一行开始执行,而不是从开头开始执行。
main():
Line1 Creates child one
Line2 prints "Hello"
Line3 Creates child two
Line4 prints "World" 

子元素1:

Line2 prints "Hello"
Line3 Creates child three
Line4 prints "World"

子元素二:

Line4 prints "World"

第三个子节点:

Line4 prints "World"

因此,一个可能的答案是:
Hello Hello World World World World 

分别来自主函数、子函数一、主函数、子函数一、子函数二和子函数三。

然而,执行顺序可能会改变(竞争条件)。


1
当调用fork时,无法确定哪个进程会先执行。因此,实际上找不到正确的输出顺序。
唯一的保证是所有进程(包括派生的子进程和父进程)都将被执行。
现在,在第一次fork之后,有两个进程,因此可以确信至少会打印两个“Hello”和两个“World”。尽管在打印两个“Hello”之后,还有另一个fork。
现在,这个fork将由两个进程执行,因此现在有四个就绪状态的进程,每个进程执行时都会打印“World”。因此,这就是为什么在最终输出中看到了两个“Hello”和四个“World”的原因。

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