标准 IO 库的缓冲区

标准 IO 库的缓冲区

首先需要明确一点: 标准 IO 库提供缓冲区的目的是尽可能减少系统调用 read 和 write 函数的调用次数. 之后会给出例子直观的显示缓冲区的大小对程序性能的影响.

标准 IO 提供了 3 种类型的缓冲:

  1. 全缓冲: 其特点是需要填满缓冲区后才进行实际的 IO 操作(当然, 也可以使用 flush 对缓冲区进行冲洗), 一般对于磁盘上的文件实施的是全缓冲. 默认全缓冲的大小为 4096;
  2. 行缓冲: 当输入输出遇到换行符时, 才执行 IO 操作. 当流涉及一个终端时, 通常使用行缓冲. 行缓冲的长度是固定的, 稍微小一些, 默认是 1024;
  3. 不带缓冲: 标准 IO 库不对字符进行缓冲. 比如标准错误输出 stderr 通常是不带缓冲的, 这样即使没有换行符, 出错信息也能尽快显示. 此时缓冲的大小为 1.

测试缓冲大小对程序性能的影响

下面给出一个例子展示缓冲区的大小对程序性能的影响, 该程序实现对文件的读, 然后将内容显示到屏幕上. 根据上面介绍的内容, 使用标准 IO 库对磁盘上的文件进行处理时, 实施的全缓冲. 之后, 我们将缓冲类型设置为不带缓冲, 从而比较两种类型的缓冲对程序性能的影响.

/* test_buffer.c */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>

#define oops(msg) {perror(msg); exit(1);}

int main() {
    char buf[100];

    FILE *fp;

    if ((fp = fopen("file", "r")) == NULL)
        oops("fopen");

    /*setbuf(fp, NULL);*/
    struct timeval t_val, t_val_end, t_result;
    gettimeofday(&t_val, NULL);
    while (fgets(buf, 100, fp) != NULL)
        printf("%s", buf);
    gettimeofday(&t_val_end, NULL);
    timersub(&t_val_end, &t_val, &t_result);
    double consume = t_result.tv_sec + (1.0 * t_result.tv_usec)/1000000;
    printf("end.elapsed time= %fs \n", consume);
}

其中文件 file 有 20 行, 总共 238 个字符.

  • 当执行上述代码后(注意此时 setbuf(fp, NULL); 被注释了), 结果为:
end.elapsed time= 0.000092s
  • 当将 setbuf(fp, NULL); 的注释去掉, 此时 fp 对应的流是不带缓冲的, 代码的执行结果为:
end.elapsed time= 0.000265s

结果在我们的意料之中. 此时由于关闭缓冲后, 系统调用频繁, 所消耗的时间无疑会增多.

显示缓冲的类型以及大小

下面的例子展示了各个流的缓冲类型以及相应的大小:

/* buffer_info.c */
#include <stdio.h>
#include <stdlib.h>

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

#define oops(msg) {perror(msg); exit(1);}

int main() {
    FILE *fp;

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

    if ((fp = fopen("/etc/passwd", "r")) == NULL)
        oops("fopen");

    if (getc(fp) == EOF)
        oops("getc 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 & _IO_UNBUFFERED);
}

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

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

程序运行的结果为:

stream = stdin,  line buffered, buffer size = 1024                              
stream = stdout,  line buffered, buffer size = 1024                             
stream = stderr,  unbuffered, buffer size = 1
stream = /etc/passwd,  fully buffered, buffer size = 4096
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值