printf()
的行为似乎取决于 stdout
的位置.
- 如果
stdout
发送到控制台,然后printf()
是行缓冲的,并在打印换行符后刷新。 - 如果
stdout
重定向到文件,缓冲区不会刷新,除非fflush()
被称为。 - 此外,如果
printf()
之前使用过stdout
被重定向到文件,后续写入(到文件)是行缓冲的,并在换行符后刷新。
什么时候 stdout
行缓冲,什么时候fflush()
需要调用吗?
每个的最小示例:
void RedirectStdout2File(const char* log_path) {
int fd = open(log_path, O_RDWR|O_APPEND|O_CREAT,S_IRWXU|S_IRWXG|S_IRWXO);
dup2(fd,STDOUT_FILENO);
if (fd != STDOUT_FILENO) close(fd);
}
int main_1(int argc, char* argv[]) {
/* Case 1: stdout is line-buffered when run from console */
printf("No redirect; printed immediately\n");
sleep(10);
}
int main_2a(int argc, char* argv[]) {
/* Case 2a: stdout is not line-buffered when redirected to file */
RedirectStdout2File(argv[0]);
printf("Will not go to file!\n");
RedirectStdout2File("/dev/null");
}
int main_2b(int argc, char* argv[]) {
/* Case 2b: flushing stdout does send output to file */
RedirectStdout2File(argv[0]);
printf("Will go to file if flushed\n");
fflush(stdout);
RedirectStdout2File("/dev/null");
}
int main_3(int argc, char* argv[]) {
/* Case 3: printf before redirect; printf is line-buffered after */
printf("Before redirect\n");
RedirectStdout2File(argv[0]);
printf("Does go to file!\n");
RedirectStdout2File("/dev/null");
}
最佳答案
stdout 的刷新由其缓冲行为决定。缓冲可以设置为三种模式:_IOFBF
(完全缓冲:如果可能的话,等待直到 fflush()
)、_IOLBF
(行缓冲:换行符)触发自动刷新)和_IONBF
(始终使用直接写入)。 “对这些特性的支持是实现定义的,并且可能会受到 setbuf()
和 setvbuf()
函数的影响。” [C99:7.19.3.3]
“程序启动时,预定义了三个文本流,无需显式打开 — 标准输入(用于读取常规输入)、标准输出(用于写入 常规输出)和标准错误(用于写入诊断输出)。和最初一样 打开时,标准错误流未完全缓冲;标准输入和标准 当且仅当可以确定该流不引用时,输出流才被完全缓冲 到交互式设备。”[C99:7.19.3.7]
观察到的行为的解释
因此,实现会执行一些特定于平台的操作来决定 stdout 是否将进行行缓冲。在大多数 libc 实现中,此测试是在首次使用流时完成的。
- 行为 #1 很容易解释:当流用于交互式设备时,它是行缓冲的,并且
printf()
会自动刷新。 - 情况 #2 现在也是预期的情况:当我们重定向到文件时,流将被完全缓冲,除非使用
fflush()
,否则不会刷新,除非您向其中写入大量数据。 - 最后,对于仅对底层 fd 执行一次检查的实现,我们也了解案例 #3。因为我们在第一个 printf() 中强制初始化了 stdout 的缓冲区,所以 stdout 获得了行缓冲模式。当我们将 fd 换出到文件时,它仍然是行缓冲的,因此数据会自动刷新。
一些实际实现
每个 libc 在如何解释这些要求方面都有自由度,因为 C99 没有指定什么是“交互式设备”,POSIX's stdio entry 也没有指定。扩展它(超出要求 stderr 开放阅读的范围)。
Glibc。请参阅filedoalloc.c:L111 。这里我们使用
stat()
来测试fd是否是一个tty,并相应地设置缓冲模式。 (这是从 fileops.c 调用的。)stdout
最初有一个空缓冲区,并且根据 fd 1 的特征在第一次使用流时分配它。BSD libc。非常相似,但代码更清晰!请参阅this line in makebuf.c
关于c - 为什么 stdout 在重定向到文件时需要显式刷新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50725719/