fork()输出中的混乱

时间:2011-12-03 12:54:30

标签: c operating-system posix fork system-calls

过去几天我一直在学习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个进程。所以你能告诉我这样做的正确方法吗? 感谢

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。由于我们不知道系统如何安排这些过程,您可以在程序中看到这种情况。