使用C语言分叉进程

5
我正在遵循有关 fork()这篇指南,但有些地方对我来说不太清晰。

两个进程将从其后的下一条语句开始执行。 在本例中,两个进程将从分配语句开始执行,如下所示:   enter image description here

根据此句话,这个脚本

printf("before ");
fork();
printf("after ");

应该打印出这个: (因为子进程将从printf("after")开始)
before after after

但实际打印出的是这个:
before after before after

那么子进程是从文件的第一行开始运行的吗?你能告诉我我的代码有什么问题吗?是我误解了那句话吗?

编辑

脚本在OS X上被编译并执行。

4个回答

7
当你创建一个新进程时,它会“继承”原始进程的所有变量,包括所有缓冲区。因此,“before”还没有被清空并且仍然在缓冲区中,子进程也将在缓冲区中包含此字符串并打印它。因此,在fork进程之前必须调用fflush(stdout);

他们是否使用相同的缓冲区?还是子进程只是从父进程“继承”缓冲区并为自己创建一个缓冲区? - Eray
它有自己的缓冲区,但该缓冲区将包含父进程在分叉时拥有的相同值。例如,如果创建变量 int i = 10;,则子进程将具有变量 i,其值为10(直到您覆盖它)。但它们仍然是不同的变量,只是在分叉时恰好包含相同的值。 - Philipp Murry
我想选择这个作为“采纳答案”,因为你先回答了。但是我建议每个遇到和我一样问题的人,也应该阅读Ed Heal、b4hand和user3386109的答案。选择其中一个答案真的很困难。 - Eray
我认为fork操作在内存级别上运行,因此将其描述为“继承变量”有点误导人。最好将整个内存空间视为被克隆,即使在实践中,直到其中一个进程写入内存之前内存是共享的。 - b4hand

3
您理解这句话的意思是正确的,但是...
当您调用fork时,它会对进程进行快照,并创建一个精确的副本。因此,如果有数据在stdout中缓冲等待写入控制台,则该数据将在分支后出现在子进程的输出缓冲区中,以及父进程的缓冲区中。
fork之前清除输出缓冲区有两种方法。您可以在printf结尾添加一个换行符\n
printf( "before\n" );
fork();
printf( "after\n" );

或者您可以使用fflush函数。
printf( "before " );
fflush( stdout );
fork();
printf( "after " );

太好了。现在我有4个很棒的答案,我必须选择其中一个作为“被接受的答案”。 - Eray
实际上,你的回答格式更好。而且很难做出公正的决定。 - Eray
1
@Eray 不用担心。我在其他论坛上提问时也遇到过同样的情况,所以我知道那是什么感觉。你做出了一个好决定 :) - user3386109

2

这是因为“before”被缓冲,只有当该缓冲区被刷新时才会输出。这发生在两个进程终止时。


所以,实际上我的问题不是关于分叉,而是关于“打印”,对吧?我应该使用哪个函数/方法来将某些内容打印到屏幕上而不进行缓冲? - Eray
还有一个问题。子进程不知道 printf("before") 的任何信息,并且在子进程缓冲区中没有 "before" 字符串。那么为什么当子进程终止时,它也打印 "before" 字符串呢? - Eray
好的,@Philipp Murry刚刚解释了我的上一个问题的答案。 - Eray

1
如果你在调用fork之前对stdout使用fflush,那么你将看到预期的输出。一般来说,C缓冲IO在进行操作时与操作系统不兼容。
与标准输出相关联的内存缓冲区在fork时被克隆,包括任何先前缓冲的输出。这就是为什么你会看到两次“before”的原因。

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