fork()让我感到困惑

4

我很难理解在不同情况下fork()命令的作用。这是来自我的书籍的一些示例代码:

int main() {
    int a = 12;
    int b = 9;
    int fid = fork();

    if (fid == 0) {
        a++;
    }
    else {
        wait(NULL);
        b = b - 5;
    }

    printf("program exit. a = %d, b = %d\n", a, b);
    return 0;
}

有人能为我详细解释一下这种情况下fork()命令正在做什么,或者提供更多示例以澄清吗?

请注意,在处理fork()时不存在多线程(标签已删除)。您正在处理多个进程(多处理),而不是单个进程内的多个线程(多线程)。 - Jonathan Leffler
fork()函数也可能返回-1,你可以使用switch(fid) {...}来处理这种情况。 - ott--
4个回答

12
                 [main]
                 a = 12
                 b = 9

                 fork()
                   |
                   |
    +--------------+--------------+
    |                             |  
    |                             |
[parent]                       [child]

fid = 1234                     fid = 0
wait(NULL)                     a++
   ...                         printf("a = 13, b = 9");
   ...                         return 0
   ...                         <exit>
b = b - 5
printf("a = 12, b = 4");
return 0
<exit>
< p > 在执行 < code > fork() 后,程序会创建两个拷贝。每个进程都有自己的变量副本,因此现在有了两个 < code > a ,两个 < code > b 等等。两个程序之间唯一的区别在于 < code > fork() 返回的值:在子进程中,< code > fork() 返回 0;在父进程中,它返回子进程的 PID。

子进程递增 < code > a ,打印 < code > a 和 < code > b 的值,然后退出。

父进程首先等待子进程终止。只有在子进程完成后,才会继续执行,将 < code > b 减去 5,打印出 < code > a 和 < code > b ,然后退出。

< code > wait(NULL) 确保子进程的打印总是在父进程之前,因此您始终可以获得可靠顺序的相同输出。如果没有它,您将无法依赖两个打印输出的顺序。它们将是相同的消息,只是顺序不可预测。


非常感谢!我一直在想如何处理那些要求输出的问题。现在这个问题已经很清晰了。所以,这个程序的正确输出应该是 program exit. a = 13, b = 9,然后是 program exit. a = 12, b = 4,对吗? - raphnguyen

4
  1. a 被设置为 12,b 被设置为 9。

  2. 调用 fork,我们现在有两个进程。

  3. 父进程获取子进程的 PID,并进入 else 语句。子进程得到 0,并进入 if 语句。

  4. 父进程等待子进程执行完毕。

  5. 子进程递增其拷贝的 a。因此,在子进程中 a 现在是 13,在父进程中是 12。

  6. 子进程退出,输出 139

  7. 父进程从其拷贝的 b 中减去 5,因此在父进程中,b 现在是 4。

  8. 父进程退出,输出 124

请注意,在 fork 后,子进程和父进程的确切执行顺序不能保证,但这不会改变结果,因为父进程在进行任何操作之前等待子进程完成。

还要注意,让两个进程都正常退出是不好的做法。当一个进程正常退出时,它会运行清理处理程序,这可能会使其他进程混乱,例如通过更改共享文件描述符上的文件位置,导致数据损坏。


非常感谢您提供的逐步指导。在这种情况下,父进程调用wait()后,您认为它会被阻塞直到子进程完成(即子进程输出“13”和“9”)吗? - raphnguyen
不客气。是的,完全正确。wait 调用的目的是阻塞父进程,直到子进程完成(或者一个不可重启的信号中断了调用)。 - David Schwartz
清晰明了的解释。再次感谢!我希望我的书有更多的例子可以练习。 - raphnguyen

3
当您调用fork()时,整个进程,包括所有内存/变量等都会被复制。
因此,在fork调用之后,每个进程都有自己的ab副本,它们的初始值分别为129
进程0将增加其自己的a副本。进程1将减少(5)其自己的b副本。
所以它们应该打印:
Process 0: a = 13, b = 9
Process 1: a = 12, b = 4

0

它的输出将是 父类 :: a=12,b=4 子类 :: a=13,b=9

由于两个进程同时执行,但都有不同的本地变量a、b的副本, 因此一个进程影响的本地变量对另一个进程不可见,因为它们都在处理自己的副本。


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