在for循环中理解fork()

3

我在理解fork()方面遇到了问题。 有人可以解释一下,这个程序会打印什么吗? 因为我正在为考试做准备,并且这是典型的问题。 在这种情况下:

#include <stdio.h>

int main(int argc, char **argv) {
    int i;
    for(i = fork(); i < fork(); i++)
        execlp(“echo”, “sono”, argv[0], 0);
    system(“echo i+$i”);
}

对我来说,难以理解的是这一行

for(i = fork(); i < fork(); i++)

这是什么意思?提前感谢所有人。

10
如果这是典型情况,那么你的教练有些扭曲。 - WhozCraig
fork 返回两个东西:对于新的子进程是 0,对于原始的 "父" 进程是子进程的 pid。这段代码基本上就是一个巨大的 fork 炸弹。 - Marc B
我认为无法预测输出,因为fork()将子进程的pid返回给i - Aseem Goyal
@anon是在父进程中的子进程ID,但在子进程中为零(0),这使得它变得有趣(虽然仍然扭曲,=P)。 - WhozCraig
1
system(“echo i+$i”) 对我来说没有意义,除非它只是用来生成 i+ - Joseph Quinsey
2个回答

5
从初始化到所有进程完成第一次循环上下文,以下是完整迭代的过程:
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");终止

不可能发生,但人们总认为中彩票会发生在他们身上。


非常感谢您详尽的回答! - skryvets
@Sergeys_K 没问题,很高兴你理解了。 - WhozCraig
据我所理解 - 这将是一个正确的图表 http://files.devit.by/graph.jpg。这对我来说非常完美,现在我更好地理解了!再次感谢您! - skryvets

3

那段代码非常复杂和错误。 但是当你不知道代码的作用时,可以手动运行它。

int main(int argc, char **argv) {
    int i;
    for(i = fork(); i < fork(); i++)
        execlp(“echo”, “sono”, argv[0], 0);
    system(“echo i+$i”);
}

父进程:

i = fork();

这里的i包含了新创建进程的PID。

execlp(“echo”, “sono”, argv[0], 0);

正如@Whozcraig在评论中指出的那样(我犯了一个大一错误),它运行echo并在输出上输出“sono a.out”(考虑到prog的名称是a.out)。execlp()用其参数替换当前进程并在此处停止执行。

在编辑之前,我说它一直在fork()

子进程:

i = fork();

这里的i包含值0,表示这是一个新创建的进程。

execlp(“echo”, “sono”, argv[0], 0);

又出现了......用它的参数替换当前进程,并在 echo 结束后停止执行。

*编辑之前,我说它又 fork() 了。

所以乍一看,这似乎是一个扭曲而疯狂的 fork 炸弹,它将呈指数增长。但最终,你只得到了一个扭曲而疯狂的单个 fork,它仅执行两次 execlp() 内容。

N.B.:system(“echo i+$i”); 是无意义的,因为$i 在此代码的上下文中没有任何含义,即使它永远不会被执行。

最后,你的教练真是非常的恶毒。


1
如果您记得execlp()函数替换当前正在运行的进程,那么您对此的fork炸弹分析可能会有很大不同。 - WhozCraig

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接