为什么我的终端[stdin/out]完全缓冲而不是行缓冲?

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

根据APUE:

ISO C requires the following buffering characteristics:
  • Standard input and standard output are fully buffered, if and only if they do not
refer to an interactive device.
  • Standard error is never fully buffered.
This, however, doesn’t tell us whether standard input and standard output are
unbuffered or line buffered if they refer to an interactive device and whether standard
error should be unbuffered or line buffered. Most implementations default to the
following types of buffering:
  • Standard error is always unbuffered.
  • All other streams are line-buffered if they refer to a terminal device; otherwise,
they are fully buffered.

对于我的例子(操作系统:ubuntu20.64,编译器:gcc):

#include<stdio.h>
#include"apue.h"

void pr_stdio(const char*, FILE *);
int is_unbuffered(FILE*);
int is_linebuffered(FILE*);
int buffer_size(FILE*);

int main()
{
    FILE* fp;
    char buf[1024];
    fputs("enter any char\n", stdout);
    
    if(getchar() == EOF)
        err_sys("getchar error");
    fputs("one line to standard error\n", stderr);

    pr_stdio("stdin", stdin);

    //set stdout -> IOLBF
    if(setvbuf(stdin, NULL, _IOLBF, 555) == 0)
        printf("setvbuf success\n");

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

    if((fp = fopen("/etc/passwd", "r")) == NULL)
        err_sys("fopen error");
    if(getc(fp) == EOF)
        err_sys("gets error");
    pr_stdio("/etc/passwd", fp);
    exit(0);

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

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

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

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

其他一些可以帮助解决此问题的信息:

<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>:
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
};

我运行它:

enter any char
l
one line to standard error
stream = stdin, fully buffered, buffer size = 1024
setvbuf success
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

奇怪的是,我设置之前它已经满缓冲了:

setvbuf(stdin, NULL, _IOLBF, 555)

返回0,表示成功,设置后还是这样

还有一件事要补充。 还有一个奇怪的现象: 如果我以这种方式调用 setvbuf() :

char mybuf[1024];
//I use the buf in user space, instead of the space which OS offers. 
setvbuf(stdin, mybuf, _IOLBF, 555) 

结果如下:

//***
stream = stdin, fully buffered, buffer size = 1024
setvbuf success
stream = stdin, line buffered, buffer size = 555
//***

哦,我用用户空间就成功了! 但为什么第一个场景会失败呢?

c linux terminal std
1个回答
0
投票

ISO C 要求

使用ISO C。ISO C 是抽象的。您的代码正在使用该单个特定实现的特定细节。它并不抽象。您与正在使用的特定单一实现紧密相关。最重要的是,您正在使用 GNU glibc。

return (fp->_flags & _IONBF);

这不是检查 glibc 中

FILE
流缓冲的方法。 Glibc 具有一些功能 https://www.gnu.org/software/libc/manual/html_node/Controlling-Buffering.html .

在glibc中(在这个特定的单个特定实现中,而不是在任何其他实现中)在

_flags
结构中有一个字段
FILE
,并且在
libio.h
https://github.com/lattera中定义了标志/glibc/blob/master/libio/libio.h#L74 。请注意,文件 libio.h not 安装在系统上。此外,还有 glibc 的扩展 https://www.gnu.org/software/libc/manual/html_node/Controlling-Buffering.html .

int is_unbuffered(FILE* fp) {
#define _IO_UNBUFFERED        0x0002  /* copied */
    return fp->_flags & _IO_UNBUFFERED;
}

int is_linebuffered(FILE* fp) {
    return __flbf(fp);
}

int buffer_size(FILE* fp) {
    return __fbufsize(fp);
}

此外,您可能对此处定义的普通文件上的 FILE 操作的 setvbuf 调度函数感兴趣 https://codebrowser.dev/glibc/glibc/libio/iosetvbuf.c.html#33 .

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