从初始化到所有进程完成第一次循环上下文,以下是完整迭代的过程:
1. 初始化
从 `i = fork();` 开始,变量 `i` 被初始化。
- 父进程:`i = pid(child1)`
- 子进程 child1:`i = 0`
在每个进程中,当满足条件测试时会发生一些有趣的事情。
2. 进程:父进程
当 `i < fork()` 时,将会创建另一个子进程 child2。如果返回的 `pid(child2)` 大于 `pid(child1)`,则条件测试被满足,父进程继续执行循环体。
3. 进程:child1
- 变量 `i` 是从初始化器中得到的零值。
- 当 `i < fork()` 时,将会创建一个子进程 child3。如果返回的 `pid(child3)` 大于 `i` 的值(即零),则条件测试被满足,child1 进程继续执行循环体。
4. 进程:child2
- 继承了父进程中的变量 `i`,其中 `i` 的值为 `pid(child1)`。
- 这个进程是通过评估 `i < fork()` 得到的,因此 `fork()` 将会被计算为 0。因此...
- `i < fork()` 将会计算为 `child1(pid) < 0`,这将永远不会为真。条件测试失败,for 循环被终止。
5. 进程:child3
- 继承了它的父进程 child1 中的 `i = 0`。
- 这个进程是通过评估 `i < fork()` 得到的,因此 `fork()` 将会被计算为 0。因此...
- `i < fork()` 将会计算为 `0 < 0`,这将永远不会为真。条件测试失败,for 循环被终止。
此时,只有父进程和 child1 进程成功通过循环体。另外两个进程(child2 和 child3)都未能通过条件检查。因此,以下事情发生:
6. 最后的步骤
- 父进程被替换为
execlp("echo", "sono", argv[0], 0);
- 子进程1被替换为
execlp("echo", "sono", argv[0], 0);
- 子进程2调用
system("echo i+$i");
并 终止。
- 子进程3调用
system("echo i+$i");
并 终止。
这很重要:在任何时候,上述的任何进程都不会评估for条件超过一次。通过条件测试的任何内容都将被替换为execlp()
进程启动。未通过条件测试的任何内容都将在system()
调用后离开循环并终止。因此,一旦任何进程通过条件(无论成功还是失败),它将永远不会再fork()
另一个进程。
换句话说,这不是一个fork()
炸弹。如果循环体或后缀代码中的任何一个分叉出另一个此进程的实例,它将很容易成为一个fork()
炸弹,但是都没有这样做。
注意:可能会发生pid溢出,进程id重置并开始“填补空缺”,这将导致第一个初始fork引入一个比child1 pid小的child2 pid。如果发生这种情况(但是不太可能),结果只会改变为:
- 父进程调用
system(“echo i+$i”);
并终止。
- 子进程1被替换为
execlp("echo", "sono", argv[0], 0);
- 子进程2调用
system("echo i+$i");
并 终止。
- 子进程3调用
system("echo i+$i");
并 终止。
不可能发生,但人们总认为中彩票会发生在他们身上。
0
,对于原始的 "父" 进程是子进程的 pid。这段代码基本上就是一个巨大的 fork 炸弹。 - Marc Bfork()
将子进程的pid
返回给i
。 - Aseem Goyal0
),这使得它变得有趣(虽然仍然扭曲,=P)。 - WhozCraigsystem(“echo i+$i”)
对我来说没有意义,除非它只是用来生成i+
。 - Joseph Quinsey