fork()和输出

56

我有一个简单的程序:

int main()
{
    std::cout << " Hello World";
    fork();
}

程序执行后,我的输出是:Hello World Hello World。为什么不是一个单独的Hello world?我猜测子进程在后台重新运行,并且输出缓冲区在进程间共享,或者发生了其他事情,但这是否是真的呢?


2
这就是fork的作用,它使用父进程的内存来生成一个新的进程。http://linux.die.net/man/2/fork - v01d
2
最近确实有很多关于 fork() 的问题……嗯…… - Mysticial
嗯..在调用fork之后,子进程开始执行程序的代码。因此,子进程只能执行在fork之后的代码,无法执行在其上面的代码。 - Tebe
我不认为面试很重要,我认为操作系统课程更重要。请参考最新的refactormycode.com:http://refactormycode.com/codes/2011-linux-shell - Ants
8个回答

94

这并不是你最初想象的那样。输出缓存区不是共享的 - 当您执行fork时,两个进程都会获得相同缓冲区的副本。因此,在fork之后,两个进程最终会单独刷新缓冲区并将内容分别打印到屏幕上。

这仅在使用缓冲IO的cout时发生。如果您使用未缓冲的cerr,则应该只看到一条消息,即fork之前。


7
这种情况发生是因为cout是用户空间缓冲IO。^_^ - Lai Jiangshan

44

标准输出使用缓存IO。当调用 fork() 时,标准输出不会被刷新,缓冲内容会被复制到子进程中。这些缓冲区在进程退出时才会被刷新,导致你看到两个输出。

如果你将程序改为:

std::cout << " Hello World;" << std::endl;

你应该只看到一个。


17

因为您在调用fork()之前没有先刷新所有缓冲区。

cout.flush();
fork();

10

输出"Hello World"的代码只会执行一次。问题在于输出缓冲区没有被清空。所以当你fork进程后,"Hello World"仍然留存在输出缓冲区中。当两个程序退出时,它们的输出缓冲区将被清空,你将看到输出两次。

最简单的方法是在字符串末尾添加一个换行符,这将导致隐式刷新,或者使用std::cout.flush();显式刷新。然后你只会看到一次输出。


9
如果你使用:
std::cout << " Hello World" << std::flush;

您只看到了一个输出。我猜测fork()会复制std::cout写入的任何输出缓冲区。


6

字符串不会立即写入屏幕,而是写入内部缓冲区。子进程继承输出缓冲区的副本,因此当子进程的cout自动刷新时,Hello World将被打印到屏幕上。父进程也会打印Hello World

如果在fork()之前刷新cout,问题几乎肯定会消失。


1
我不会说这个分析完全正确。子进程并没有在后台“重新运行”。 - Michael Mior
@MichaelMior:你说得对。我错过了“重新运行”的部分。我已经编辑了语言。 - NPE

3

原因是当您调用std::cout<<时,它并没有真正执行输出,而是将数据留在系统的缓冲区中。当您执行fork时,代码和数据以及所有相关的缓冲区都会被复制。最后,父进程和子进程都会将它们刷新到标准输出中,因此您会看到输出重复。


2
您可能看到的是缓冲效应。通常情况下,输出会被缓冲,直到显式刷新或隐式地执行诸如输出新行之类的操作为止。因为输出被缓冲,所以分叉进程的两个副本都有缓冲输出,因此在进程终止和刷新缓冲时都会显示它。

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