为什么当重定向到文件时,stdout 需要显式刷新?

2024-10-21 09:14:00
admin
原创
287
摘要:问题描述:的行为printf()似乎取决于的位置stdout。如果stdout被发送到控制台,那么printf()就会进行行缓冲,并且在打印换行符后刷新。如果stdout重定向到文件,则除非fflush()调用,否则缓冲区不会被刷新。此外,如果在重定向到文件printf()之前使用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
");
    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!
");
    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
");
    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
");
    RedirectStdout2File(argv[0]);
    printf("Does go to file!
");
    RedirectStdout2File("/dev/null");
}

解决方案 1:

刷新stdout由其缓冲行为决定。缓冲可以设置为三种模式:(_IOFBF完全缓冲:等待直到fflush()可能为止)、_IOLBF(行缓冲:换行触发自动刷新)和(始终使用直接写入)。“对这些特性的支持由实现定义,并且可能通过和函数_IONBF受到影响。”[C99:7.19.3.3]setbuf()`setvbuf()`

“在程序启动时,三个文本流是预定义的,不需要明确打开 - 标准输入(用于读取常规输入)、标准输出(用于写入常规输出)和标准错误(用于写入诊断输出)。最初打开时,标准错误流未完全缓冲;当且仅当可以确定流不引用交互式设备时,标准输入和标准输出流才会完全缓冲。” [C99:7.19.3.7]

观察到的行为的解释

因此,实现会执行一些特定于平台的操作来决定是否stdout要进行行缓冲。在大多数 libc 实现中,此测试是在首次使用流时进行的。

  1. 行为#1很容易解释:当流用于交互式设备时,它是行缓冲的,并且printf()会自动刷新。

  2. 现在也预计会出现情况 #2:当我们重定向到一个文件时,流会被完全缓冲,并且不会被刷新fflush(),除非您向其中写入大量数据。

  3. 最后,对于仅对底层 fd 执行一次检查的实现,我们也理解了案例 #3。由于我们强制在第一个 stdout 的缓冲区进行初始化printf(),因此 stdout 获得了行缓冲模式。当我们将 fd 换出以转到文件时,它仍然是行缓冲的,因此数据会自动刷新。

一些实际的实现

每个 libc 在解释这些要求方面都有自己的自由,因为 C99 没有指定什么是“交互式设备”,POSIX 的 stdio 条目也没有对此进行扩展(除了要求 stderr 处于打开状态以供读取之外)。

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

  2. BSD libc。非常相似,但代码更简洁!请参阅makebuf.c 中的这一行

解决方案 2:

您错误地组合了缓冲和非缓冲 IO 函数。这种组合必须非常小心,尤其是当代码必须是可移植的时。(编写不可移植的代码是不好的……)

最好避免在同一个文件描述符上组合缓冲和非缓冲 IO。

缓冲 I/O: fprintf() , fopen(), fclose(), freopen()...

缓冲 IO : write(),,,...open()`close()`dup()

当您使用dup2()重定向 stdout 时。该函数不知道由 填充的缓冲区fprintf()。因此,当dup2()关闭旧描述符 1 时,它不会刷新缓冲区,并且内容可能会刷新到不同的输出。在您的案例 2a 中,它被发送到/dev/null

解决方案

对于你的情况,最好使用freopen()而不是dup2()。这解决了你所有的问题:

  1. 它刷新原始流的缓冲区FILE。(情况 2a)

  2. 它根据新打开的文件设置缓冲模式。(情况 3)

这是您的函数的正确实现:

void RedirectStdout2File(const char* log_path) {
    if(freopen(log_path, "a+", stdout) == NULL) err(EXIT_FAILURE, NULL);
}

不幸的是,使用缓冲 IO 您无法直接设置新创建文件的权限。您必须使用其他调用来更改权限,或者可以使用不可移植的 glibc 扩展。请参阅fopen() man page

解决方案 3:

您不应该关闭文件描述符,因此如果您希望仅在文件中打印消息,请删除close(fd)并关闭它。stdout_bak_fd

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用