为什么该程序打印“分叉!” 4次?


76

为什么该程序打印“分叉!” 4次?

Answers:


86

fork()一个在调用过程中返回一个非零值(称为p0),在子进程中返回0(称为p1)。

在p1中,发生短路,&&过程调用printf并终止。在p0中,过程必须评估表达式的其余部分。然后fork()再次调用,从而创建一个新的子进程(p2)。

在p0中fork()返回一个非零值,并进行短路||,因此该过程调用printf并终止。

在p2中,fork()返回0,所以||的其余部分 必须评估,这是最后一个fork();导致为p2创建一个孩子(称为p3)。

然后printf,P2执行并终止。

然后printf,P3执行并终止。

printf然后执行4 s。


3
你能解释一下“ &&短路了吗?”
Rawan Lezzeik 2014年

8
@ rona-altico,检查我的答案中有关短路的链接。这基本上意味着,如果的第一个操作数&&为零,则不计算其他操作数。在相同的逻辑上,如果a的操作数||为1,则其余操作数不需要求值。发生这种情况是因为其余操作数无法更改逻辑表达式的结果,因此不需要执行它们,因此可以节省时间。现在更好罗纳?顺便问一个好问题,我看不出为什么要投票。您向我+1致谢。
gsamaras 2014年

3
@ rona-altico:我很难理解为什么要使用它fork(),但是甚至不知道短路是什么。这是学校的问题吗?
塞巴斯蒂安·马赫

2
@ G.Samaras:我猜它被否决了,因为它是许多重复项中的一个,即使在询问之前在谷歌上也没有付出任何努力。
chrk 2014年

4
@ G.Samaras:你看不明白为什么要投票?这是一个可怕的问题。有一个完全相同的副本,他没有理会解释他期望得到什么不同的输出。为什么要进行41次投票完全超出了我的范围;通常这种事情很快会达到-3或-4。
Lightness Races in Orbit

210

一个来自main()每个,另外三个来自每个fork()

注意,这三个forks()都将被执行。您可能想看看ref

返回值

成功完成后,fork()应将0返回给子进程,并将子进程的进程ID返回给父进程。这两个过程均应继续从fork()函数执行。否则,将-1返回给父进程,不创建任何子进程,并设置errno来指示错误。

请注意,进程ID不能为零,如规定在这里


那么到底发生了什么?

我们有:

因此,第一个fork()将其非零进程ID返回给父进程,而它将向子进程返回0。这意味着逻辑表达式的第一个fork将在父进程中被评估为true,而在子进程中将被评估为false,并且由于短路评估,它将不调用其余两个fork()s。

因此,现在我们知道至少要打印两张照片(一张来自main,一张来自1st fork())。

现在,fork()将执行父进程中的2nd ,它会执行此操作,并且它将向父进程返回一个非零值,在子进程中返回一个零。

因此,现在,父进程将不会继续执行到最后一个fork()(由于短路),而子进程将执行最后一个派生,因为第一个操作数||为0。

因此,这意味着我们将再获得两张印刷品。

结果,我们总共得到四张印刷品。


短路

在此,短路基本上意味着,如果&&的第一个操作数为零,则不评估其他一个或多个操作数。在相同的逻辑上,如果||的操作数 为1,则其余操作数无需求值。发生这种情况是因为其余操作数无法更改逻辑表达式的结果,因此不需要执行它们,因此可以节省时间。

请参见下面的示例。


处理

请记住,父流程会创建后代流程,而后代流程又会创建其他流程,依此类推。这导致了流程的层次结构(或者可以说是一棵树)。

考虑到这一点,这是值得考虑看看这个类似的问题,以及这个答案。


描述性图像

我猜我也做了这个数字,可以提供帮助。我假设fork()每次调用返回的pid分别为3、4和5。

叉节点 请注意,有些fork()s上方有一个红色的X,这意味着由于逻辑表达式的短路求值而未执行它们。

fork()顶部的s不会被执行,因为运算符的第一个操作数&&为0,因此整个表达式的结果为0,因此执行其余操作数没有本质&&

fork()底部将不会被执行,因为它的第二个操作数||,在其第一个操作数是一个非零数字,因此表达式的结果已经被评估为真,无论第二个操作数是什么。

在下一张图片中,您可以看到流程的层次结构: 流程层次 基于上图。


短路示例

输出:


14

对于所有反对者来说,这是一个合并但又不同的问题。怪SO。谢谢。

您可以将问题分解为三行,第一行和最后一行都只是使过程数加倍。

操作员正在短路,所以这是您得到的:

因此,这总共4 * 5 = 20处理每个打印一行。

注意:如果由于某些原因fork()失败(例如,您对进程数有一定限制),它将返回-1,然后您可以获得不同的结果。


谢谢@yi_H的回复,如果在我们的代码中只有这一行,那输出将是5倍的分支?
阿米特·辛格·托马尔

还有fork1()|| fork2()&& fork3(); 此语句导致16个过程。
阿米特·辛格·托马尔

@yi_H,您好,只是混淆了“ fork1()|| fork2()&& fork3()”总共得到16个结果。我的意思是,我能否得到您上面提到的图表
Amit Singh Tomar,

没有任何括号,您可能会错误地解析逻辑树。来自第一个fork()的false(0)路径是否会导致整个表达式在&&处短路?我认为&&和||的关联优先级 C中的C是从右到左,因此从左到右的简单求值可以使其余子表达式(在任何包含括号内)短路。它与fork()&&(fork()|| fork())相同,这将仅从该行解释总共4个(而非5个)过程,总共16个。它可以是C ++或C#不同,但这种问题是在C
罗布帕克

3
这回答了使用的问题fork() && fork() || fork();,而这里的问题使用了fork() && (fork() || fork());。如此处讨论的那样存在一个合并:“ meta.stackoverflow.com/questions/281729/… ”。您可能需要编辑答案,以通知将来的读者。
gsamaras 2015年

9

执行中fork() && (fork() || fork()),会发生什么

每个fork给定2个进程,其值分别为pid(父级)和0(子级)

第一叉:

  • 父级返回值为pid不为null =>执行 && (fork() || fork())
    • 第二个fork父值是pid不为null停止执行||部件=>打印forked
    • 第二个fork子值= 0 =>执行 || fork()
      • 第三叉父级打印 forked
      • 第三叉儿童版画 forked
  • 子级返回值为0停止执行&& part =>打印 forked

合计:4 forked


8

我喜欢已经提交的所有答案。也许,如果您在printf语句中添加了一些其他变量,则可以更轻松地了解正在发生的情况。

在我的机器上,它产生了以下输出:


5
每次调用fork()返回的值如何?怎么样: long f1,f2,f3; (f1 = fork()) && ((f2 = fork()) || (f3 = fork()));然后打印PID和三个单独的值。
罗伯·帕克

5

这段代码:

自身获得20个进程,而Printf将运行20次。

而对于

printf将总共执行5次。


2
这回答了使用的问题fork() && fork() || fork();,而这里的问题使用了fork() && (fork() || fork());。如此处讨论的那样存在一个合并:“ meta.stackoverflow.com/questions/281729/… ”。您可能需要编辑答案,以通知将来的读者。
gsamaras 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.