puts不刷新io重定向程序中的缓冲区

问题描述

代码如下:

int main(int argc,char **argv)
{
   const char *file = "/tmp/out"; 
   int fd = open(file,O_WRONLY | O_CREAT | O_Trunc,S_IRWXU);
   int stdout_tmp = dup(1);
   close(1);
   dup(fd);
   puts("Hello World!");
   // fflush(stdout);
   close(1);
   close(fd);
   dup(stdout_tmp);
   puts("redirect completed!");
   exit(0);
}

我使用gcc10.2.0成功编译了代码,没有任何警告,再次期望,两行都输出到stdout而不是/ tmp / out文件中的“ hello world”,并“重定向完成!”在标准输出中。当取消注释fflush(stdout)时,它就可以工作!

我猜puts()不会刷新用户空间中的缓冲区,在还原标准输出退出后,缓冲区会自动刷新。

当遇到'\ n'时,

gets()输出字符串以'\ n'结尾,并且stdout缓冲区将被自动刷新。为什么需要手动调用fflush(stdout)?

解决方法

仅在连接到终端时(基本上在stdout为true时,puts文件(写入isatty(fileno(stdout))的文件)才被行缓冲(即在换行符上刷新缓冲区)。 / p>

连接到另一个非终端输出时,将对其进行完全缓冲,这意味着您要么需要完全填充缓冲区,要么显式调用fflush以刷新缓冲区。

,

man 3 setvbuf说:

通常所有文件都是块缓冲的。 如果流指向终端(如stdout通常所做的那样),则会对其进行行缓冲。

由于puts()使用stdout,因此我们应该期待刷新 (由于\n)。

但是,由于在您的示例中stdout之前从未使用过 重定向,我猜测未选择缓冲行为 然而。 首次写入基础文件描述符stdout时 不再是终端,而是常规文件。 我猜测此时选择了缓冲行为。

如果您在示例中在之前puts()添加了另一个呼叫, 重定向,然后为终端选择缓冲行为 并且随后在执行重定向时不会更改。 在这种情况下,您的示例可以按您预期的方式工作( fflush()


修改

仍然停留在man 3 setvbuf

对文件进行第一次I / O操作时,将调用malloc(3),并获得一个缓冲区。

并进一步:

setvbuf()函数只能在打开流之后且尚未对其执行任何其他操作之前使用。

在linux上,这与您的示例一致。

在MSDN页面上的setvbuf

流必须引用自打开以来从未经历过I / O操作的打开文件。