为什么这个程序打印"分叉!"4次?
#include#include int main(void) { fork() && (fork() || fork()); printf("forked!\n"); return 0; }
gsamaras.. 205
一个来自,main()
另外三个来自每个fork()
.
请注意,这三个forks()
都将被执行.你可能想看一下ref:
返回值
成功完成后,fork()将向子进程返回0,并将子进程的进程ID返回给父进程.两个进程都将继续从fork()函数执行.否则,-1应返回到父进程,不应创建子进程,并设置errno以指示错误.
请注意,进程ID不能为零,如规定在这里.
那真的发生了什么?
我们有:
fork() && (fork() || fork());
因此,第一个fork()
将返回父级非零进程ID,而它将返回0到子进程.这意味着逻辑表达式的第一个fork将在父进程中被评估为true,而在子进程中它将被评估为false,并且由于短路评估,它将不会调用剩余的两个fork()
.
因此,现在我们知道将至少获得两个打印件(一个来自main,一个来自1st fork()
).
现在,fork()
父进程中的第二个将被执行,它确实会向父进程返回一个非零值,在子进程中返回一个零值.
所以现在,父进程不会继续执行到最后一次fork()
(由于短路),而子进程将执行最后一个fork,因为第一个操作数||
是0.
这意味着我们将再获得两次打印.
结果,我们总共获得了四张照片.
短路
这里,短路基本上意味着如果&&的第一个操作数为零,则不评估其他操作数.在相同的逻辑上,如果是||的操作数 是1,那么其余的操作数不需要评估.发生这种情况是因为其余的操作数不能改变逻辑表达式的结果,因此不需要执行它们,因此我们节省了时间.
见下面的例子.
处理
请记住,父进程会创建后代进程,从而创建其他进程等等.这导致了流程的层次结构(或者可以说是树).
考虑到这一点,值得看看这个类似的问题,以及这个答案.
描述性图像
我猜这个数字也可以提供帮助.我认为fork()
每次通话时返回的pid 为3,4和5.
请注意,有些fork()
s在它们上方有一个红色X,这意味着由于逻辑表达式的短路评估,它们不会被执行.
fork()
顶部的s不会被执行,因为运算符的第一个操作数&&
是0,因此整个表达式将导致0,因此执行其余操作数时没有本质&&
.
该fork()
底部将不会被执行,因为它的第二个操作数||
,在其第一个操作数是一个非零数字,因此表达式的结果已经被评估为真,无论第二个操作数是什么.
在下一张图片中,您可以看到流程的层次结构: 基于上图.
短路的例子
#includeint 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
Jean-Baptist.. 84
第fork()
一个在调用进程中返回一个非零值(称之为p0),在子节点中返回0(称之为p1).
在p1中,采用短路&&
并且过程调用printf
并终止.在p0中,该过程必须评估表达式的其余部分.然后它fork()
再次调用,从而创建一个新的子进程(p2).
在p0中fork()
返回一个非零值,并且采用shortcircuit ||
,因此进程调用printf
并终止.
在p2中,fork()
返回0,因此||的其余部分 必须评估,这是最后一个fork()
; 这导致为p2创建一个孩子(称之为p3).
P2然后执行printf
并终止.
然后P3执行printf
并终止.
printf
然后执行4 秒.
对于所有的downvoters来说,这是一个合并但不同的问题.责备SO.谢谢.
您可以将问题分解为三行,第一行和最后一行都只是将进程数加倍.
fork() && fork() || fork();
操作员是短路的,所以这就是你得到的:
fork() / \ 0/ \>0 || fork() && fork() /\ / \ / \ 0/ \>0 * * || fork() * / \ * *
所以这总共是4*5 = 20个进程,每个进程打印一行.
注意:如果由于某种原因fork()失败(例如,您对进程数有一些限制),它返回-1然后您可以得到不同的结果.
执行fork() && (fork() || fork())
,会发生什么
每个fork
给出2个进程,分别是值pid(父)和0(子)
第一叉:
父返回值是pid not null =>执行 && (fork() || fork())
第二个fork父值是pid not null停止执行||
part => printforked
second fork child value = 0 =>执行 || fork()
第三叉父母打印 forked
第三叉儿童版画 forked
子返回值为0停止执行&& part =>打印 forked
总计:4 forked
这段代码:
fork(); fork() && fork() || fork(); fork();
为自己获得20个进程,并且将获得20次Printf.
并为
fork() && fork() || fork();
printf将共计5次.
一个来自,main()
另外三个来自每个fork()
.
请注意,这三个forks()
都将被执行.你可能想看一下ref:
返回值
成功完成后,fork()将向子进程返回0,并将子进程的进程ID返回给父进程.两个进程都将继续从fork()函数执行.否则,-1应返回到父进程,不应创建子进程,并设置errno以指示错误.
请注意,进程ID不能为零,如规定在这里.
那真的发生了什么?
我们有:
fork() && (fork() || fork());
因此,第一个fork()
将返回父级非零进程ID,而它将返回0到子进程.这意味着逻辑表达式的第一个fork将在父进程中被评估为true,而在子进程中它将被评估为false,并且由于短路评估,它将不会调用剩余的两个fork()
.
因此,现在我们知道将至少获得两个打印件(一个来自main,一个来自1st fork()
).
现在,fork()
父进程中的第二个将被执行,它确实会向父进程返回一个非零值,在子进程中返回一个零值.
所以现在,父进程不会继续执行到最后一次fork()
(由于短路),而子进程将执行最后一个fork,因为第一个操作数||
是0.
这意味着我们将再获得两次打印.
结果,我们总共获得了四张照片.
短路
这里,短路基本上意味着如果&&的第一个操作数为零,则不评估其他操作数.在相同的逻辑上,如果是||的操作数 是1,那么其余的操作数不需要评估.发生这种情况是因为其余的操作数不能改变逻辑表达式的结果,因此不需要执行它们,因此我们节省了时间.
见下面的例子.
处理
请记住,父进程会创建后代进程,从而创建其他进程等等.这导致了流程的层次结构(或者可以说是树).
考虑到这一点,值得看看这个类似的问题,以及这个答案.
描述性图像
我猜这个数字也可以提供帮助.我认为fork()
每次通话时返回的pid 为3,4和5.
请注意,有些fork()
s在它们上方有一个红色X,这意味着由于逻辑表达式的短路评估,它们不会被执行.
fork()
顶部的s不会被执行,因为运算符的第一个操作数&&
是0,因此整个表达式将导致0,因此执行其余操作数时没有本质&&
.
该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
我喜欢已经提交的所有答案.也许如果你在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()
一个在调用进程中返回一个非零值(称之为p0),在子节点中返回0(称之为p1).
在p1中,采用短路&&
并且过程调用printf
并终止.在p0中,该过程必须评估表达式的其余部分.然后它fork()
再次调用,从而创建一个新的子进程(p2).
在p0中fork()
返回一个非零值,并且采用shortcircuit ||
,因此进程调用printf
并终止.
在p2中,fork()
返回0,因此||的其余部分 必须评估,这是最后一个fork()
; 这导致为p2创建一个孩子(称之为p3).
P2然后执行printf
并终止.
然后P3执行printf
并终止.
printf
然后执行4 秒.