c - 为什么 stdout 在重定向到文件时需要显式刷新?

标签 c linux stdout

printf() 的行为似乎取决于 stdout 的位置.

  1. 如果stdout发送到控制台,然后 printf()是行缓冲的,并在打印换行符后刷新。
  2. 如果stdout重定向到文件,缓冲区不会刷新,除非 fflush()被称为。
  3. 此外,如果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. 行为 #1 很容易解释:当流用于交互式设备时,它是行缓冲的,并且 printf() 会自动刷新。
  2. 情况 #2 现在也是预期的情况:当我们重定向到文件时,流将被完全缓冲,除非使用 fflush(),否则不会刷新,除非您向其中写入大量数据。
  3. 最后,对于仅对底层 fd 执行一次检查的实现,我们也了解案例 #3。因为我们在第一个 printf() 中强制初始化了 stdout 的缓冲区,所以 stdout 获得了行缓冲模式。当我们将 fd 换出到文件时,它仍然是行缓冲的,因此数据会自动刷新。

一些实际实现

每个 libc 在如何解释这些要求方面都有自由度,因为 C99 没有指定什么是“交互式设备”,POSIX's stdio entry 也没有指定。扩展它(超出要求 stderr 开放阅读的范围)。

  1. Glibc。请参阅filedoalloc.c:L111 。这里我们使用stat()来测试fd是否是一个tty,并相应地设置缓冲模式。 (这是从 fileops.c 调用的。)stdout 最初有一个空缓冲区,并且根据 fd 1 的特征在第一次使用流时分配它。

  2. BSD libc。非常相似,但代码更清晰!请参阅this line in makebuf.c

关于c - 为什么 stdout 在重定向到文件时需要显式刷新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50725719/

相关文章:

c++ - 无法访问 argv[0],如何获取程序名称?

linux - 修复 Asterisk 目录路径

Linux 命令和自动操作

c++ - 如何在 C++ 中刷新重定向的标准输出

ruby - 从 Ruby 运行交互式程序

c++ - 将标准输出重定向到缓冲区,而不是使用 >

c - 我很难使用引用传递

c - 将文件上传到 C 中的 apache 服务器(没有 Web header )

c - 错误消息 "undefined reference for ` CPU_ZERO'”

c - Ubuntu/Xfce 禁用特定应用程序的全局快捷方式