为什么在重定向到文件时stdout需要显式刷新?
的行为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。因为我们在第一个中强制将stdout的缓冲区初始化,所以
printf()
stdout获得了行缓冲模式。当我们换掉fd进入文件时,它仍然是行缓冲的,因此数据会自动刷新。
一些实际的实现
每个libc都具有如何解释这些要求的自由度,因为C99没有指定“交互设备”是什么,POSIX的stdio条目也没有对此进行扩展(除了要求打开stderr以便读取之外)。
Glibc。参见filedoalloc.c:L111。在这里,我们用于
stat()
测试fd是否为tty,并相应地设置缓冲模式。(这是从fileops.c中调用的。)stdout
最初有一个空缓冲区,并根据fd 1的特性在第一次使用流时分配它。BSD库 非常相似,但是要遵循的代码更简洁!请在makebuf.c中查看此行
以上是 为什么在重定向到文件时stdout需要显式刷新? 的全部内容, 来源链接: utcz.com/qa/403226.html