为什么 std[in,out] 即使连接到终端也没有行缓冲?

问题描述 投票:0回答:2

根据unix高级编程

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

c unix io stdin
2个回答
1
投票

上一点:

正如评论中所述,您的辅助函数可能无法准确描述系统文件缓冲的真实默认状态。


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

§7.21.3

3 - [...]

当流完全缓冲时,当缓冲区已满时,字符将作为块传输到主机环境或从主机环境传输。当流是行缓冲时,当遇到换行符时,字符将作为块传输到主机环境或从主机环境传输。此外,当缓冲区已满、在无缓冲流上请求输入或在需要从主机环境传输字符的行缓冲流上请求输入时,字符旨在作为块传输到主机环境。 对这些特性的支持是实现定义的,并且可能会受到 setbuf 和 setvbuf 函数的影响。

7 -

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

我的重点。

根据我在 Linux 和 MacOS 中的个人经验,就其价值而言,

stdin

stdout
 在与终端关联时是行缓冲的,否则是完全缓冲的,而 
stderr
 是无缓冲的,因此这与书中描述的行为显然与您的特定情况不同,但同样,这是实现定义的。

我不会说这本书是错误的,但

默认行为绝对可以解释为这是规范,并且对于上述系统始终如此。


0
投票
我也遇到过这个问题。请问出现这种现象的原因是否是终端不是交互设备?

© www.soinside.com 2019 - 2024. All rights reserved.