关闭后stdio到终端(STDOUT_FILENO)的行为

时间:2017-05-17 20:14:19

标签: c linux buffer stdio

我想知道为什么在以下程序中取消注释第一个printf语句会改变其后续行为:

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

int main() {
  //printf("hi from C \n");

  // Close underlying file descriptor:
  close(STDOUT_FILENO);

  if (write(STDOUT_FILENO, "Direct write\n", 13) != 13) // immediate error detected.
    fprintf(stderr, "Error on write after close(STDOUT_FILENO): %s\n", strerror(errno));

  // printf() calls continue fine, ferror(stdout) = 0 (but no write to terminal):
  int rtn;
  if ((rtn = printf("printf after close(STDOUT_FILENO)\n")) < 0 || ferror(stdout)) 
    fprintf(stderr, "Error on printf after close(STDOUT_FILENO)\n");
  fprintf(stderr, "printf returned %d\n", rtn);
  // Only on fflush is error detected:
  if (fflush(stdout) || ferror(stdout))
    fprintf(stderr, "Error on fflush(stdout): %s\n", strerror(errno));
}

没有第一个printf,后续的printf rtns 34好像没有发生错误,即使从stdout用户缓冲区到底层fd的连接已经关闭。仅在手动fflush(stdout)上才会报告错误。 但随着第一个printf打开,下一个printf报告错误,正如我所料。 当然,在任何一种情况下关闭STDOUT_FILENO fd之后,没有任何内容写入终端(通过printf)。

我知道close(STDOUT_FILENO)首先在这里是愚蠢的;这是我偶然发现的一个实验,并认为在这些方面更有知识的人可能会看到一些对我们有益的东西..

我在Linux上使用gcc。

1 个答案:

答案 0 :(得分:4)

如果您strace这两个程序,stdio似乎有效,那么在第一次写入时,它会使用fstat检查描述符以找出某种文件已连接到stdout - 如果它是终端,则stdout行缓冲,如果是其他内容,则stdout将被阻止缓冲。如果您在第一个close(1);之前致电printf,则现在初始fstat将返回EBADF,而1不是指向字符设备的文件描述符,stdout是块缓冲的。

在我的计算机上,缓冲区大小为8192字节 - 可以缓冲许多字节,以便在发生第一次失败之前写入stdout

如果您取消注释第一个printf,则fstat(1, ...) 成功,并且Glibc会检测到stdout已连接到终端; stdout设置为行缓冲,因此printf after close(STDOUT_FILENO)\n以换行符结束,缓冲区将立即刷新 - 这将导致立即错误。