过去几天我一直在学习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出现在输出中,那将是非常好的。 如果有帮助我想澄清我试图从给定的一个创建5个进程。所以你能告诉我这样做的正确方法吗? 感谢
答案 0 :(得分:3)
父母通过打印父母的PID开始。然后它继续分叉四个孩子(C1..4),然后退出。
C1打印其父PID,然后继续fork它自己的三个子节点。 C2打印其父PID,然后继续分叉它自己的两个子节点。 C3打印其父PID ...
每个分叉的子项在创建它的if块之后继续运行,因此将创建相当多的子项。
当父进程退出时,子进程将重新分配给init
,其进程标识为1
。确切的输出因运行而异,具体取决于孩子的计划时间和方式,以及父母的退出。
如果您只想创建五个进程,请确保子进程完成后退出!
if(fork() == 0) {
p = getppid();
printf("%d\n",p);
exit(0);
}
如果您希望父母等待其子女在退出之前完成所有孩子,您应该查看wait
系列函数。
答案 1 :(得分:2)
如果子进程的父进程在子进程之前退出或死亡,则该子进程称为孤立进程,并由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行由两个进程到达:原始父进程及其第一个子进程。他们俩分叉和他们的两个孩子(即原始父母的第二个孩子和原父母的第一个孩子的第一个孩子)在第12行执行printf()
。第14行到达第14行(原始父母,其子女和原父母第一个孩子的第一个孩子)。所有这些都在第15行产生一个孩子,并且所有四个孩子在第17行执行printf()
。多达8个进程到达第19行。每个进程中的每个进程和8个最终生成的子进程执行最终{第22行{1}}。
首次printf()
执行一次。
第二次printf()
执行一次。
第三次printf()
执行2次。
第四printf()
次执行4次。
第五个printf()
执行了8次。
这总共是16,与您的输出一致。显示的一些PPID等于1,表示给定的父项执行速度太快,以至于init在达到给定printf()
之前已采用该子项。其他大于1表示当在孩子中达到printf()
时,给定进程的父进程仍在运行。极有可能的是,多次执行程序会导致输出略有不同。
因此,您不会创建4个子进程,而是15。这是因为您的子进程从生成它们的printf()
返回时继续执行。这意味着一些fork()
不仅会由原始父级执行,还会由其子级执行,从而创建一系列新进程。如果您只想创建4个子节点,则应确保仅在父节点中发生剩余分叉。
答案 2 :(得分:0)
你错过的是,当你分叉时,有2个进程使用相同的代码副本从你分叉的位置继续。
父母得到孩子的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
答案 3 :(得分:0)
首先,1
来自已经退出的父进程。然后,父进程成为系统初始化进程1
。
如果您对1
的数量感到恼火:
最初的过程分叉4个孩子,这个叉子的第一个孩子3,有两个孩子,每个分叉2个,三个孩子分叉1个,四个不分叉。这使得1 + 1 + 4 + 3 + 2 + 1 + 4 = 16个进程总数。
答案 4 :(得分:0)
该程序将在四个fork()部分创建1 + 2 + 4 + 8 = 15个进程(如果我们加上原始部分,则为16个)。也许你想要做的是这样的事情:
if(fork() == 0) {
p = getppid();
printf("%d\n",p);
} else
return 0;
如果其父级已经消失,则getppid()将返回1。由于我们不知道系统如何安排这些过程,您可以在程序中看到这种情况。