问题描述
代码如下:
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操作的打开文件。