[英]Confusion in the output of fork()
过去几天,我一直在学习fork()函数,并进行了一些实验以了解它的实际工作方式。 这样做的时候,我浏览了我没看懂的这段有趣的代码。 这是代码:
int main(int argc, char *argv[])
{
int p,m;
p = getppid();
printf("%d\n",p);
if(fork() == 0) {
p = getppid();
printf("%d\n",p);
}
if(fork() == 0) {
p = getppid();
printf("%d\n",p);
}
if(fork() == 0) {
p = getppid();
printf("%d\n",p);
}
if(fork() == 0) {
p = getppid();
printf("%d\n",p);
}
return 0;
}
输出为:
$./a.out
6117
6460
1
user@ubuntu:~/forkbomb$ 1
1
1
1
1
1
1
1
1
1
1
1
6473
如果您向我解释,为什么init的pid是1出现在输出中,对您真是太好了。 如果有帮助,我想澄清一下,我试图从给定的过程中创建5个过程。 那么,能否请您告诉我正确的做法呢? 谢谢
父级首先打印其父级的PID。 然后继续分叉四个子项(C1..4),然后退出。
C1打印其父PID,然后继续派生自己的三个子PID。 C2打印其父PID,然后继续派生自己的两个孩子。 C3打印其父PID ...
每个派生的子代在创建它的if块之后继续运行,因此将创建很多子代。
当父进程退出时,子进程将重新绑定到具有进程ID 1
init
。 每次运行的确切输出将有所不同,具体取决于孩子安排时间和方式以及父母退出的确切时间。
如果只想创建五个流程,请确保子流程完成后退出!
if(fork() == 0) {
p = getppid();
printf("%d\n",p);
exit(0);
}
如果要让父项等待其子项在退出自身之前全部完成,则应考虑使用wait
函数族。
如果子进程的父进程在该子进程之前退出或死亡,则该子进程称为孤立进程,并由init
。 这意味着子代的PPID(父PID)将更改为1。这解释了您的输出,因为它来自getppid()
。
为了解释显示的行数,让我们对代码行进行编号:
1 int p,m;
2 p = getppid();
3 printf("%d\n",p);
4
5 if(fork() == 0) {
6 p = getppid();
7 printf("%d\n",p);
8 }
9
10 if(fork() == 0) {
11 p = getppid();
12 printf("%d\n",p);
13 }
14
15 if(fork() == 0) {
16 p = getppid();
17 printf("%d\n",p);
18 }
19
20 if(fork() == 0) {
21 p = getppid();
22 printf("%d\n",p);
23 }
现在,让我们计算执行每个printf()
所有进程。 第3行的printf()
显然仅由原始父进程执行。 第7行的printf()
仅由原始父进程的第一个子进程执行,因为fork()
在该子进程中返回0。 现在,第9行有两个过程:原始父级和第一个子级。 他们两个都派生了他们的两个孩子(即,原始父级的第二个孩子和原始父级的第一个孩子的第一个孩子printf()
在第12行执行printf()
。第4行到达第14行(原始父级) ,它既是孩子又是原父母的第一个孩子的第一个孩子)。 它们全都在第15行产生一个子级,所有四个子级在第17行执行printf()
。多达18个进程到达第19行。每个分支和最年轻的8个结果子级执行最后的printf()
在第22行。
第一个printf()
执行1次。 第二个printf()
执行1次。 第三次printf()
执行两次。 第四个printf()
执行4次。 第五次printf()
执行8次。
这是总共16,并且与您的输出一致。 显示的某些PPID等于1,表示给定的父级执行得太快,以至于子级在到达给定的printf()
之前被init所采用。 其他大于1表示在子进程中达到printf()
时,给定进程的父进程仍在运行。 程序的多次执行极有可能导致输出有所不同。
因此,您不会创建4个子进程,而是创建15个子进程。这是由于您的子进程从产生它们的fork()
返回时继续执行。 这意味着一些fork()
不仅将由原始父级执行,还将由其子级执行,从而创建一系列新进程。 如果只想创建4个子代,则应确保仅在父代中发生剩余的分叉。
您错过的是,当您进行分叉时,有两个进程具有相同的代码副本,从您分叉的位置开始。
父级返回孩子的pid,子级返回0。 因此,您要分叉16个进程(4个阶乘),因为在每个分叉处,您都会使进程数加倍。
如果在末尾添加sleep()以确保父进程挂起足够长的时间,则您将在子进程中获得实际的父pid(只需在返回前添加sleep(2))。
我的输出是:
> ./x
19291
21686
21686
21687
21688
21687
21687
21688
21689
21691
21690
21689
21695
21686
21686
21694
首先, 1
来自已经退出的父进程。 父级然后成为系统初始化过程1
。
如果您也对1
s的数量感到烦恼:初始过程分叉4个子节点,此分叉的第一个子节点3,有两个子节点分别分叉2,三个子节点分叉1,而四个子节点不分叉。进一步分叉。 总共1 + 1 + 4 + 3 + 2 + 1 + 4 = 16个进程。
该程序将在四个fork()部分中创建1 + 2 + 4 + 8 = 15个进程(如果加上原始的16个)。 也许您想做的是这样的:
if(fork() == 0) {
p = getppid();
printf("%d\n",p);
} else
return 0;
如果其父项不存在,getppid()将返回1。 由于我们不知道系统如何安排这些流程,因此您可以在程序中看到这种情况。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.