0

代码如下:

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 成功编译了代码,没有任何警告,与我的期望相反,这两行都输出到标准输出,而不是 /tmp/out 文件中的“hello world”和“重定向完成!” 在标准输出中。当取消注释 fflush(stdout) 时,它可以工作!

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

gets() 输出字符串,尾随 '\n',当遇到 '\n' 时,stdout 缓冲区将自动刷新。为什么需要手动调用 fflush(stdout)?

4

2 回答 2

0

man 3 setvbuf says:

Normally all files are block buffered. If a stream refers to a terminal (as stdout normally does), it is line buffered.

Since puts() uses stdout we should expect a flush (because of the \n).

However, since in your example stdout has not been used before the redirection, I guess that the buffering behaviour is not chosen yet. At your first write attempt to stdout, the underlying file descriptor is not a terminal anymore but a regular file. I guess the buffering behaviour is chosen at this moment.

If you add another call to puts() in your example before the redirection, then the buffering behaviour is chosen for the terminal and then does not change afterwards when the redirection is performed. In this case, your example works as you expect (without the explicit fflush()).


edit

Still in man 3 setvbuf:

When the first I/O operation occurs on a file, malloc(3) is called, and a buffer is obtained.

and further:

The setvbuf() function may only be used after opening a stream and before any other operations have been performed on it.

On linux, this is consistent with your example.

In the MSDN page for setvbuf:

stream must refer to an open file that has not undergone an I/O operation since it was opened.

于 2020-09-04T16:42:02.223 回答
0

stdout文件(写入的地方puts)只有在连接到终端时才进行行缓冲(即在换行符上刷新缓冲区)(基本上是在isatty(fileno(stdout))为真时)。

当连接到另一个非终端输出时,它是完全缓冲的,这意味着您需要完全填充缓冲区,或者fflush显式调用以刷新缓冲区。

于 2020-09-04T16:29:53.713 回答