在程序 1Hello world
中只打印一次,但是当我删除 \n
并运行它(程序 2)时,输出会打印 8 次。有人可以解释一下\n
这里的意义以及它如何影响fork()
吗?
程序 1
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("hello world...\n");
fork();
fork();
fork();
}
输出 1:
hello world...
节目二
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("hello world...");
fork();
fork();
fork();
}
输出 2:
hello world... hello world...hello world...hello world...hello world...hello world...hello world...hello world...
当使用 C 库的
printf()
函数输出到标准输出时,输出通常是缓冲的。fflush(stdout)
在您输出换行符、调用或退出程序(虽然不是通过调用)之前,缓冲区不会被刷新_exit()
。默认情况下,标准输出流在连接到 TTY 时以这种方式进行行缓冲。当您在“程序 2”中 fork 进程时,子进程会继承父进程的每个部分,包括未刷新的输出缓冲区。这有效地将未刷新的缓冲区复制到每个子进程。
当进程终止时,缓冲区被刷新。您总共启动了八个进程(包括原始进程),并且未刷新的缓冲区将在每个单独的进程终止时被刷新。
它是8 个,因为在每个
fork()
过程中,您获得的进程数量是之前的两倍fork()
(因为它们是无条件的),并且您拥有其中的三个(2 3 = 8)。它不会以任何方式影响前叉。
在第一种情况下,您最终会得到 8 个进程而没有任何内容可写,因为输出缓冲区已经被清空(由于
\n
.在第二种情况下,您仍然有 8 个进程,每个进程都有一个包含“Hello world...”的缓冲区,并且缓冲区在进程结束时写入。
@Kusalananda 解释了为什么重复输出。如果你好奇为什么输出重复8 次而不是 4 次(基本程序 + 3 个分叉):
这里的重要背景是标准
stdout
需要将行缓冲作为默认设置。这会导致 a
\n
刷新输出。由于第二个示例不包含换行符,因此不会刷新输出,并且在
fork()
复制整个过程时,它也会复制stdout
缓冲区的状态。现在,
fork()
您的示例中的这些调用总共创建了 8 个进程 - 它们都带有stdout
缓冲区状态的副本。根据定义,所有这些进程在从所有活动的stdio流
exit()
返回时调用main()
并随后调用。这包括,因此,您会看到八次相同的内容。exit()
fflush()
fclose()
stdout
fflush()
在调用之前调用所有具有未决输出的流是一种很好的做法,fork()
或者让分叉子显式调用_exit()
仅退出进程而不刷新 stdio 流。请注意,调用不会刷新 stdio 缓冲区,因此如果您(在调用之后)调用和(如果失败)调用
exec()
,则可以不关心 stdio 缓冲区。fork()
exec()
_exit()
顺便说一句:要了解可能导致错误的缓冲,这里是 Linux 中最近修复的一个以前的错误:
默认情况下,该标准要求
stderr
不缓冲,但 Linux 忽略了这一点,并使stderr
行缓冲和(更糟)完全缓冲,以防 stderr 通过管道重定向。因此,为 UNIX 编写的程序在 Linux 上确实输出没有换行符的东西太晚了。请参阅下面的评论,现在似乎已修复。
这就是我为了解决这个 Linux 问题所做的:
此代码不会对其他平台造成伤害,因为调用
fflush()
刚刚刷新的流是一个 noop。