在程序 1 中Hello world只打印一次,但是当我删除 \n并运行它时(程序 2),输出被打印了 8 次。有人可以向我解释一下\n这里的重要性以及它如何影响fork()?
方案一
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("hello world...\n");
fork();
fork();
fork();
}
Run Code Online (Sandbox Code Playgroud)
输出 1:
hello world...
Run Code Online (Sandbox Code Playgroud)
方案二
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("hello world...");
fork();
fork();
fork();
}
Run Code Online (Sandbox Code Playgroud)
输出 2:
hello world... hello world...hello world...hello world...hello world...hello world...hello world...hello world...
Run Code Online (Sandbox Code Playgroud)
Kus*_*nda 102
当使用 C 库的printf()函数输出到标准输出时,输出通常被缓冲。在您输出换行符、调用fflush(stdout)或退出程序(_exit()虽然不是通过调用)之前,缓冲区不会被刷新。默认情况下,标准输出流在连接到 TTY 时以这种方式行缓冲。
当您在“程序 2”中 fork 进程时,子进程继承父进程的每个部分,包括未刷新的输出缓冲区。这有效地将未刷新的缓冲区复制到每个子进程。
当进程终止时,缓冲区被刷新。您总共启动了八个进程(包括原始进程),并且未刷新的缓冲区将在每个进程终止时刷新。
它是8 个,因为在每个过程中,fork()您获得的进程数量是之前的两倍fork()(因为它们是无条件的),并且您拥有其中的三个 (2 3 = 8)。
edc*_*c65 19
它不会以任何方式影响前叉。
在第一种情况下,您最终有 8 个进程没有可写,因为输出缓冲区已经被清空(由于\n)。
在第二种情况下,您仍然有 8 个进程,每个进程都有一个包含“Hello world...”的缓冲区,缓冲区在进程结束时写入。
Hon*_*dek 12
@Kusalananda 解释了为什么重复输出。如果您好奇为什么输出重复8 次而不是 4 次(基础程序 + 3 个分叉):
int main()
{
printf("hello world...");
fork(); // here it creates a copy of itself --> 2 instances
fork(); // each of the 2 instances creates another copy of itself --> 4 instances
fork(); // each of the 4 instances creates another copy of itself --> 8 instances
}
Run Code Online (Sandbox Code Playgroud)
这里的重要背景是作为默认设置stdout需要由标准行缓冲。
这会导致\n刷新输出。
由于第二个示例不包含换行符,因此不会刷新输出,并且在fork()复制整个过程时,它还复制了stdout缓冲区的状态。
现在,fork()您示例中的这些调用总共创建了 8 个进程——所有进程都带有stdout缓冲区状态的副本。
根据定义,所有这些进程调用exit()的返回时main()和exit()通话fflush(),然后fclose()在所有活动标准输入输出流。这包括stdout,因此,您会看到八次相同的内容。
fflush()在调用之前调用所有带有挂起输出的流,fork()或者让分叉子调用显式地_exit()只退出进程而不刷新 stdio 流,这是一种很好的做法。
请注意,调用exec()不会刷新 stdio 缓冲区,因此如果您(在调用之后fork()) callexec()和(如果失败) call ,则可以不关心 stdio 缓冲区_exit()。
顺便说一句:要理解错误的缓冲可能会导致,这里是 Linux 中最近修复的一个以前的错误:
stderr默认情况下,该标准要求无缓冲,但 Linux 忽略了这一点,stderr并在 stderr 通过管道重定向的情况下将行缓冲并(甚至更糟)完全缓冲。因此,为 UNIX 编写的程序在 Linux 上输出没有换行符的内容为时已晚。
请参阅下面的评论,现在似乎已修复。
为了解决这个 Linux 问题,我是这样做的:
/*
* Linux comes with a broken libc that makes "stderr" buffered even
* though POSIX requires "stderr" to be never "fully buffered".
* As a result, we would get garbled output once our fork()d child
* calls exit(). We work around the Linux bug by calling fflush()
* before fork()ing.
*/
fflush(stderr);
Run Code Online (Sandbox Code Playgroud)
此代码不会在其他平台fflush()上造成损害,因为调用刚刚刷新的流是一个 noop。