为何连接终端时stdin、stdout未采用行缓冲?与《UNIX高级编程》描述不符的技术疑问
终端下stdin/stdout缓冲模式与《UNIX高级编程》描述不符的疑问
我最近在验证《UNIX高级编程》里关于标准IO缓冲的描述时遇到了困惑:书中明确提到,当标准输入(stdin)和标准输出(stdout)连接到终端时,系统默认应为行缓冲模式。为了验证这一点,我直接运行了书中提供的示例代码:
#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
明明我的stdin和stdout已经连接到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
书中的示例针对Solaris、OpenBSD、Linux和macOS这4种操作系统使用了特定于系统的宏,我的Linux系统也在覆盖范围内。
另外,我特意查看了本地系统中相关宏和FILE结构体的实现:
<stdio.h>中的缓冲模式定义:
/* The possibilities for the third argument to `setvbuf'. */ #define _IOFBF 0 /* Fully buffered. */ #define _IOLBF 1 /* Line buffered. */ #define _IONBF 2 /* No buffering. */
<struct_FILE.h>中的_FILE结构体实现:
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
内容的提问来源于stack exchange,提问作者milanHrabos
相关产品推荐
相关产品推荐

