【问题标题】:Why are std[in,out] not line buffered even when connected to terminal?为什么即使连接到终端,std[in,out] 也没有行缓冲?
【发布时间】:2021-06-07 08:50:06
【问题描述】:

根据unix advanced programming

We can see that the default for this system is to have standard input and standard
output line buffered when they’re connected to a terminal.

从这个例子:

#include <stdio.h>
#include <stdlib.h>

long buffer_size(FILE *fp)
{
    return (fp->_IO_buf_end - fp->_IO_buf_base);
}

int is_linebf(FILE *fp)
{
    return (fp->_flags & _IOLBF);
}

int is_unbf(FILE *fp)
{
    return (fp->_flags & _IONBF);
}

void stream(const char *name, FILE *fp)
{
    printf("stream = %s, ", name);
    if (is_unbf(fp))
    {
        printf("unbuffered");
    }
    else if (is_linebf(fp))
    {
        printf("line buffered");
    }
    else
    {
        printf("fully buffered");
    }
    printf(", buffer size = %ld\n", buffer_size(fp));
}

int main()
{
    FILE *fp;

    fputs("enter any character: ", stdout);
    if (getchar() == EOF)
    {
        perror("getchar error");
    }
    fputs("one line to standard error\n", stderr);

    stream("stdin", stdin);
    stream("stdout", stdout);
    stream("stderr", stderr);

    if ((fp = fopen("/etc/passwd", "r")) == NULL)
    {
        perror("fopen error");
    }
    if (fgetc(fp) == EOF)
    {
        perror("fgetc error");
    }
    stream("/etc/passwd", fp);
    exit(0);
}

如果我运行它:

$ ./a.out
enter any character: a
one line to standard error
stream = stdin, fully buffered, buffer size = 1024
stream = stdout, fully buffered, buffer size = 1024
stream = stderr, unbuffered, buffer size = 1
stream = /etc/passwd, fully buffered, buffer size = 4096

即使将 std[in,out] 连接到 tty,它们也是完全缓冲,而不是行缓冲。那么这本书是错的还是我遗漏了什么?

uname -a:

Linux 5.8.0-44-generic #50-Ubuntu SMP Tue Feb 9 06:29:41 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

书中的例子使用了 4 个操作系统(solaris、openBSD、linux 和 macos)的操作系统特定的宏,所以我的操作系统也在其中。

编辑: 在 my 机器中,宏和 FILE 结构的实现:

/* The possibilities for the third argument to `setvbuf'.  */
#define _IOFBF 0        /* Fully buffered.  */
#define _IOLBF 1        /* Line buffered.  */
#define _IONBF 2        /* No buffering.  */

struct _IO_FILE
{
  int _flags;       /* High-order word is _IO_MAGIC; rest is flags. */

  /* The following pointers correspond to the C++ streambuf protocol. */
  char *_IO_read_ptr;   /* Current read pointer */
  char *_IO_read_end;   /* End of get area. */
  char *_IO_read_base;  /* Start of putback+get area. */
  char *_IO_write_base; /* Start of put area. */
  char *_IO_write_ptr;  /* Current put pointer. */
  char *_IO_write_end;  /* End of put area. */
  char *_IO_buf_base;   /* Start of reserve area. */
  char *_IO_buf_end;    /* End of reserve area. */

  /* The following fields are used to support backing up and undo. */
  char *_IO_save_base; /* Pointer to start of non-current get area. */
  char *_IO_backup_base;  /* Pointer to first valid character of backup area */
  char *_IO_save_end; /* Pointer to end of non-current get area. */

  struct _IO_marker *_markers;

  struct _IO_FILE *_chain;

  int _fileno;
  int _flags2;
  __off_t _old_offset; /* This used to be _offset but it's too small.  */

  /* 1+column number of pbase(); 0 is unknown. */
  unsigned short _cur_column;
  signed char _vtable_offset;
  char _shortbuf[1];

  _IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

struct _IO_FILE_complete
{
  struct _IO_FILE _file;
#endif
  __off64_t _offset;
  /* Wide character stream stuff.  */
  struct _IO_codecvt *_codecvt;
  struct _IO_wide_data *_wide_data;
  struct _IO_FILE *_freeres_list;
  void *_freeres_buf;
  size_t __pad5;
  int _mode;
  /* Make sure we don't get into trouble again.  */
  char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
};

FILE 就是typedef struct _IO_FILE FILE

【问题讨论】:

  • default for this system -- 您运行的系统与书中的系统相同吗?您是否希望 您的 系统默认为行缓冲?
  • @Oka 查看编辑,linux(我的操作系统)使用正确的 FILE 结构实现,就像本书一样。
  • 在您的实现中,_IONBUF 等是单个位吗?我可以看到一个使用多个位的合理实现,这会使您的辅助函数受到怀疑。
  • @WilliamPursell 这些宏_IO* 定义在&lt;stdio.h&gt;
  • 为什么你甚至期望像return (fp-&gt;_IO_buf_end - fp-&gt;_IO_buf_base) 这样的东西能正常工作?没有“标准”FILE 结构,在某些系统上您甚至无法访问其成员。

标签: c unix io stdin


【解决方案1】:

上一点:

作为stated in the comments,您的辅助函数可能无法准确描述系统文件缓冲的真实默认状态。


也就是说,文件缓冲是实现定义的,来自 C11 N1570:

§7.21.3

3 - [...] 当一个流被完全缓冲时,当缓冲区被填满时,字符将作为一个块传输到主机环境或从主机环境传输。当流被行缓冲时,当遇到换行符时,字符将作为块传输到主机环境或从主机环境传输。此外,当缓冲区被填满时,当在非缓冲流上请求输入时,或者当在需要从主机环境传输字符的行缓冲流上请求输入时,字符将作为块传输到主机环境. 对这些特性的支持由实现定义,并且可能会受到 setbuf 和 setvbuf 函数的影响。

7 - 在程序启动时,预定义了三个文本流,不需要显式打开——标准输入(用于读取常规输入)、标准输出(用于写入常规输出)和标准错误(用于编写诊断输出)。 最初打开时,标准错误流没有完全缓冲;当且仅当可以确定流不指向交互式设备时,标准输入和标准输出流才会被完全缓冲

我的重点。

从我在 Linux 和 MacOS 的个人经验来看,stdinstdout 在与终端关联时是行缓冲的,否则是完全缓冲的,而 stderr 是非缓冲的,所以这是一致的与书中描述的行为不同,显然,在您的特定情况下,但同样,这是实现定义的。

我不会说这本书是错的,但默认行为绝对可以解释为这是规范,并且对于上述系统来说总是正确的。

【讨论】:

  • 虽然 C 标准说支持是实现定义的,但 Linux 似乎不太可能不支持它们。这不太可能解释 OP 的观察结果。
  • @EricPostpischil,我从来没有遇到过这样的问题,无论如何,关键是没必要。
猜你喜欢
  • 1970-01-01
  • 2021-08-05
  • 2013-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-11
  • 2013-01-19
相关资源
最近更新 更多