为啥这个程序打印“fork”! 4次?
Posted
技术标签:
【中文标题】为啥这个程序打印“fork”! 4次?【英文标题】:Why does this program print "forked!" 4 times?为什么这个程序打印“fork”! 4次? 【发布时间】:2014-12-30 05:28:28 【问题描述】:为什么这个程序打印“forked!” 4次?
#include <stdio.h>
#include <unistd.h>
int main(void)
fork() && (fork() || fork());
printf("forked!\n");
return 0;
【问题讨论】:
【参考方案1】:一个来自main()
,另外三个来自每个fork()
。
请注意,所有三个forks()
都将被执行。你可能想看看ref:
返回值
成功完成后,fork() 将返回 0 给子进程,并将子进程的进程 ID 返回给父进程。两个进程都应继续从 fork() 函数执行。否则返回-1给父进程,不创建子进程,设置errno表示错误。
请注意,进程 ID 不能为零,如 here 所述。
那么到底发生了什么?
我们有:
fork() && (fork() || fork());
所以第一个fork()
将返回其非零进程ID 给父进程,而它会返回0 给子进程。这意味着逻辑表达式的第一个 fork 将在父进程中被评估为 true,而在子进程中它将被评估为 false,并且由于 Short circuit evaluation,它不会调用剩余的两个 fork()
s。
所以,现在我们知道这将至少得到两张打印(一张来自主要的,一张来自第一张fork()
)。
现在,父进程中的第二个fork()
将被执行,它执行并向父进程返回一个非零值,在子进程中返回一个零值。
所以现在,父进程不会继续执行到最后一个fork()
(由于短路),而子进程将执行最后一个fork,因为||
的第一个操作数是0。
这意味着我们将获得另外两个打印件。
因此,我们总共得到了四张照片。
短路
这里,短路基本上意味着如果 && 的第一个操作数为零,则其他操作数不被评估。在相同的逻辑中,如果 || 的操作数为 1,则其余操作数不需要计算。这是因为剩下的操作数不能改变逻辑表达式的结果,所以不需要执行,这样可以节省时间。
请参阅下面的示例。
流程
请记住,父进程会创建子进程,而子进程又会创建其他进程,依此类推。这导致了进程的层次结构(或者可以说是一棵树)。
考虑到这一点,值得看看这个similar problem 和this 的答案。
描述性图片
我猜我也制作了这个可以提供帮助的图。我假设每次调用返回的 pid 的 fork()
是 3、4 和 5。
请注意,一些fork()
s 上面有一个红色的 X,这意味着它们因为逻辑表达式的短路评估而没有被执行。
顶部的fork()
s 不会被执行,因为&&
的第一个操作数是0,所以整个表达式的结果是0,所以执行剩下的操作数没有意义(s) &&
。
底部的fork()
不会被执行,因为它是||
的第二个操作数,它的第一个操作数是一个非零数,因此表达式的结果已经被计算为真,没有不管第二个操作数是什么。
在下一张图片中,您可以看到流程的层次结构: 根据上图。
短路示例
#include <stdio.h>
int main(void)
if(printf("A printf() results in logic true\n"))
;//empty body
if(0 && printf("Short circuiting will not let me execute\n"))
;
else if(0 || printf("I have to be executed\n"))
;
else if(1 || printf("No need for me to get executed\n"))
;
else
printf("The answer wasn't nonsense after all!\n");
return 0;
输出:
A printf() results in logic true
I have to be executed
【讨论】:
【参考方案2】:第一个fork()
在调用进程中返回一个非零值(称为p0),在子进程中返回0(称为p1)。
在 p1 中,&&
发生短路,进程调用 printf
并终止。在 p0 中,该过程必须计算表达式的其余部分。然后它再次调用fork()
,从而创建一个新的子进程(p2)。
在p0中fork()
返回一个非零值,对||
进行短路,所以进程调用printf
并终止。
在 p2 中,fork()
返回 0,所以 || 的余数必须进行评估,也就是最后一个fork()
;这导致为 p2 创建一个子项(称为 p3)。
P2 然后执行printf
并终止。
P3 然后执行printf
并终止。
然后执行 4 个printf
s。
【讨论】:
你能解释一下“&&短路”吗? @rona-altico,在我的回答中查看有关短路的链接。这基本上意味着如果&&
的第一个操作数为零,则不计算其他操作数。同样的逻辑,如果 ||
的一个操作数是 1,那么其余的操作数不需要计算。这是因为剩下的操作数不能改变逻辑表达式的结果,所以它们不需要被执行,这样我们就节省了时间。现在更好罗娜?顺便说一句,这是个好问题,我不明白为什么会投反对票。你得到了我的 +1。
@rona-altico:我很难理解你为什么要使用fork()
,但我什至不知道什么是短路。这是学校的问题吗?
@G.Samaras:我猜它被否决了,因为它是许多重复项之一,并且在问这里之前甚至没有努力谷歌它。
@G.Samaras:你看不出为什么投反对票?这是一个可怕的问题。有一个完全相同的副本,他没有费心解释他期望的不同输出。为什么这有 41 个赞成票完全超出了我的理解;通常这种事情很快就会达到-3或-4。【参考方案3】:
对于所有反对者,这是来自一个合并但不同的问题。责怪SO。谢谢。
您可以将问题分解为三行,第一行和最后一行都只是将进程数加倍。
fork() && fork() || fork();
运算符短路了,所以这就是你得到的:
fork()
/ \
0/ \>0
|| fork() && fork()
/\ / \
/ \ 0/ \>0
* * || fork() *
/ \
* *
所以这总共是 4 * 5 = 20 个进程,每个进程打印一行。
注意:如果由于某种原因 fork() 失败(例如,您对进程数有一些限制),它会返回 -1,然后您可以获得不同的结果。
【讨论】:
感谢@yi_H 的回复和一件事,如果我们的代码中只有这一行,那么输出将分叉 5 次?? 还有fork1() || fork2() && fork3();此语句产生 16 个进程。 嗨@yi_H 我只是混淆了 "fork1() || fork2() && fork3()" 总共有 16 个结果。我的意思是我可以有一个像你上面提到的这样的图表 如果没有任何括号,您可能会错误地解析逻辑树。第一个 fork() 的假 (0) 路径不会导致整个表达式在 && 处短路吗?我认为 && 和 || 的关联性优先级在 C 中是从右到左的,因此从左到右的简单评估可以使子表达式的其余部分短路(在任何包含括号内)。它与 fork() && (fork() || fork()) 相同,这将解释仅从这一行开始的 4 个(不是 5 个)进程,总共 16 个。在 C++ 或 C# 中可能会有所不同,但这个问题是在 C 中。 这回答了使用fork() && fork() || fork();
的问题,而这里的问题使用fork() && (fork() || fork());
。有一个合并,如此处所述:“meta.***.com/questions/281729/…”。您可能想要编辑您的答案,通知未来的读者。【参考方案4】:
执行fork() && (fork() || fork())
,会发生什么
每个fork
给出 2 个进程,其值分别为 pid(父)和 0(子)
第一次分叉:
父返回值为pid not null => 执行&& (fork() || fork())
第二个 fork 父值是 pid 不是 null 停止执行 ||
部分 => 打印 forked
second fork child value = 0 => 执行|| fork()
第三个叉父打印forked
第三个叉子打印forked
子返回值为 0 停止执行 && 部分 => 打印 forked
总数:4 forked
【讨论】:
【参考方案5】:我喜欢所有已经提交的答案。也许如果您在 printf 语句中添加更多变量,您会更容易看到发生了什么。
#include<stdio.h>
#include<unistd.h>
int main()
long child = fork() && (fork() || fork());
printf("forked! PID=%ld Child=%ld\n", getpid(), child);
return 0;
在我的机器上它产生了这个输出:
forked! PID=3694 Child = 0
forked! PID=3696 Child = 0
forked! PID=3693 Child = 1
forked! PID=3695 Child = 1
【讨论】:
每次调用 fork() 返回的值如何?怎么样:long f1,f2,f3; (f1 = fork()) && ((f2 = fork()) || (f3 = fork()));
,然后打印 PID 和三个单独的值。【参考方案6】:
这段代码:
fork();
fork() && fork() || fork();
fork();
为自己获得 20 个进程,Printf 将执行 20 次。
对于
fork() && fork() || fork();
printf 一共会执行 5 次。
【讨论】:
这回答了使用fork() && fork() || fork();
的问题,而这里的问题使用fork() && (fork() || fork());
。有一个合并,如此处所述:“meta.***.com/questions/281729/…”。您可能想要编辑您的答案,通知未来的读者。以上是关于为啥这个程序打印“fork”! 4次?的主要内容,如果未能解决你的问题,请参考以下文章