当子进程fork时,fork()函数是如何工作的?

3
我已经执行了一段代码。代码如下所示:

#include<stdio.h>

main() {
int i=0;
fork();
printf("The value of i is:%d\n",++i);
fork();
printf("The value of j is:%d\n",++i);
fork();
wait();
}

我得到了以下输出:

The value of i is:1
The value of j is:2
The value of i is:1
The value of j is:2
The value of j is:2
pckoders@ubuntu:~$ The value of j is:2

有人能解释一下fork()和wait()函数在这里扮演的角色吗?

你不理解 man 手册的哪一部分?具体是什么问题? - johannes
4
@johannes,那么我们可以从 Stack Overflow 中删除大部分 API 相关的问题吗? - Prof. Falken
4个回答

11

fork()调用就像字面意义上的“分叉”。它完成后,您将拥有两个完全相同的进程,具有相同的堆栈和所有描述符都引用相同的对象。您可以通过返回值来区分它们。对于子进程,fork()返回0,而对于父进程,则返回子进程的进程ID。

因此,

main() {
int i=0;
fork(); 
// at this point you are having 2 processes. stdout and stdin are basically just dupplicates.
//          (P)
//        /     \
//     (P)       (C)
//   prints1    prints 1
printf("The value of i is:%d\n",++i); // so 2 processes with print 1
fork();
// now you are having 4 processes( both parent and children forked)
//                   (P)
//                 /     \
//               /         \
//           (P)            (C)
//         /     \         /   \
//      (PP)     (PC)    (CP)  (CC)
//   prints 2  prints 2  prints 2  prints 2
printf("The value of j is:%d\n",++i);
fork();
// now 4 processes are forking. now you have 8 processes
//                                   (P)
//                                /       \
//                            /              \
//                         /                    \
//                   (P)                           (C)             
//                 /     \                       /     \           
//               /         \                   /         \          
//          (PP)           (PC)             (CP)          (CC) 
//         /     \        /     \          /    \       /     \        
//     (PPP)    (PPC)  (PCP)   (PCC)   (CPP)   (CPC)  (CCP)  (CCC)   

wait();
}

GreenScape,我很高兴你用这样一个整洁的图解释了这个概念。我明白了,谢谢你的关切和回复。但是请再澄清一个疑问,为什么它交替打印i和j的值?在第一次fork()之后,它应该连续两次显示i的值。但是在打印了i的值一次之后,它会显示j的值... - Babanna Duggani
这完全取决于操作系统的调度程序。想象一下进程P分叉成P和C。CPU正在处理P进程。它打印1。继续进行下一个分叉。执行它。进程PP打印2。调度程序切换到进程C,现在CPU正在处理C。C打印1。以此类推... - GreenScape
如果没有wait()会发生什么? - jdyg
你可能会看到完全不同的进程树。而不是等待每个进程尽快退出,它取决于哪个进程有机会首先退出。 - GreenScape

6
该程序生成一个进程树。在每次fork时,这棵树会分成两个分支。如果你拿一张纸,画这棵树并不难;唯一困难的是由于使用了前缀++,需要正确获取i值。如果你让每个进程最后都sleep几秒钟,你还可以使用pstree程序观察这棵树。
每个进程都运行wait系统调用,等待其任何一个子进程(进程树中的子节点)完成。

3
要严谨的话,不仅是第一个孩子,而是“任何”孩子,对吧? - Prof. Falken
@AmigableClarkKant:严格来说,是的。已更新答案。 - Fred Foo
1
我认为关键词在于它等待一个子进程。这就是为什么它在最后一行被打印之前退出的原因。 - Per Johansson

1

在第一次fork()之后,就有了两个进程(当前进程和它的精确副本——子进程),它们都打印了1。

这两个进程中的每一个都通过第二次fork()调用进行了复制,然后就有了4个进程,它们每个都打印了2。

它们的输出以随机顺序出现,因为并行执行总是如此。


谢谢你,尤金。我明白了流程。再次感谢你的关心。 - Babanna Duggani

-1
Forking进程以树状方式创建子进程。将每个fork视为二叉树的不同层级。当您不发出fork()时,您只有一个根节点的进程树。当您发出单个fork()时,现在您有两个级别的二叉树,第一级将包含父进程,第二级将包含两个进程-父进程和子进程。
当您想要找出手头有多少进程时,只需继续构建二进制/进程树并查看最后一级有多少节点,最后一级仅是进程/树的当前状态。等待函数使您的父进程等待子进程完成执行。在您不希望有僵尸进程的应用程序中,您需要发出等待,否则这些僵尸进程将继续占用系统... link

记住,当您希望父进程在子进程完成后才结束时,等待操作也是非常有用的。分叉并不总是产生相同的输出,顺序会打乱,因此要始终获得相同的输出,请使用wait()。要等待特定的子进程,请使用wait(pid),其中pid是特定子进程的pid,该pid可以通过子进程空间内的getpid获得。


1
如果去掉所有荒谬的省略号和“u”的愚蠢,以及其他使这个完全无法阅读的非答案的东西,用适当的段落、拼写和标点符号重写成标准英语,那么这个downvote将被取消。 请仅返回翻译后的文本。 - tchrist

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