C的全缓冲、行缓冲和无缓冲

1.简介

基于流的操作最终会调用read或者write函数进行I/O操作。为了使程序的运行效率最高,流对象通常会提供缓冲区,以减少调用系统I/O库函数的次数。

在Linux中,缓冲方式存在三种,分别是: (1)全缓冲。输入或输出缓冲区被填满,会进行实际I/O操作。其他情况,如强制刷新、进程结束也会进行实际I/O操作。

对于读操作来说,当读入内容的字节数等于缓冲区大小或者文件已经到达结尾,或者强制刷新,会进行实际的I/O操作,将外存文件内容读入缓冲区;对于写操作来说,当缓冲区被填满或者强制刷新,会进行实际的I/O操作,缓冲区内容写到外存文件中。磁盘文件操作通常是全缓冲的。

(2)行缓冲。输入或输出缓冲区遇到换行符会进行实际I/O操作。其他与全缓冲相同。

(3)无缓冲。没有缓冲区,数据会立即读入内存或者输出到外存文件和设备上。标准错误输出stderr是无缓冲的,这样保证错误信息能够及时反馈给用户,供用户排除错误。

三种缓冲类型的宏定义在头文件<stdio.h>

缓冲类型

全缓冲

_IOFBF

行缓冲

_IOLBF

无缓冲

_IONBF

在学习APUE这本书时,程序8-1中,就很好的体现了全缓冲和行缓冲的区别,代码如下:

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

int glob=6; 
int main(int argc, char** argv) {
    int var;  
    pid_t pid;  
    printf("a write to stdout\n");  
    if(pid=fork()<0){  
        printf("fork error");  
    }else{  
        if(pid==0){  
            glob++;  
            var++;  
        }  
        else{  
            sleep(2);  
        }  
    }  
    printf("pid=%d,glob=%d,var=%d\n",getpid(),glob,var);  
    exit(0);  
}

编译成功后,我这里生成的二进制文件默认为a.out,运行结果如下:

./a.out 
a write to stdout
pid=4823,glob=7,var=4195873
pid=4824,glob=7,var=4195873

./a.out > temp.txt
cat temp.txt
a write to stdout
pid=4864,glob=7,var=4195873
a write to stdout
pid=4865,glob=7,var=4195873

可见printf在输出到标准输出(显示器)时,是行缓冲,遇到换行符时会将缓冲区内容输出到显示器,并清空缓冲区。当使用重定向命令时,标准输出被重定向到磁盘文件,此时标准输出变成全缓冲,遇到换行符不输出,而是被拷贝至子进程中,在父子进程结束后,各有一份输出。

2.缓冲区的设置

(1)设置是否开启缓冲区,可使用函数setbuf或者setbuffer。setbuf和setbuffer函数具有打开和关闭缓冲机制。为了带缓冲进行I/O,参数buf指向缓冲区。通常在此之后该流就是全缓冲的,但是如果该流与一个终端设备相关,那么某些系统也可以将其设置为行缓冲。为了关闭缓冲,可以将buf参数设置为NULL。注意,setbuffer()非C标准库函数,见于BSD系统。

setbuf()的缓冲区长度必须为BUFSIZ(定义在stdio.h),否则可能会出现缓冲区溢出。setbuffer可以指定缓冲区大小。

//@header:stdio.h
//@brief:设置指定的缓冲区或关闭缓冲
//@param:stream:文件指针;buffer:缓冲区地址
//@notice:使用默认缓冲大小BUFSIZ(在stdio.h中定义)
void setbuf ( FILE * stream, char * buffer );

//@notice:同setbuf,但可指定缓冲区大小
void setbuffer(FILE *stream, char *buf, size_t size);

将buffer指定为NULL,关闭标准输出缓冲。

setbuf(stdout,NULL)

指定新的缓冲区。

static char newBuffer[BUFSIZ];//必须是BUFSIZ(定义在stdio.h),否则存在缓冲溢出可能
setbuf(stdout,(char*)&newBuffer);

//或者指定缓冲区大小
static char newBuffer[512];
setbuffer(stdout,(char*)&newBuffer,512);

(2)更改缓冲模式,可使用函数setvbuf。

//@header:stdio.h
//@brief:更改缓冲模式并设置缓冲区
//@param:stream:文件指针;buf缓冲区地址;type:缓冲区模式;size:缓冲区大小
//@ret:0成功,非0失败
int setvbuf(FILE *stream, char *buf, int type, unsigned size);

例如,将流缓冲区设置为行缓冲,调用setvbuf时,缓冲区地址设为NULL,缓冲区大小设为0。注意,前提是存在缓冲区。

setvbuf(stream,NULL,_IOLBF,0);  //实测,VS2015中,size需要大于0,且只能设为全缓冲

//上面的代码等价于
setlinebuf(stream);             //for BSD

如果调用setvbuf指定了缓冲区大小size大于0,缓冲区buf为NULL,则交由setvbuf进行malloc申请缓冲区。

//间接申请1024字节全缓冲区
setvbuf ( stream , NULL , _IOFBF , 1024); //实测Linux无效,VS2015可以设置缓冲区大小

//以下在Windows和Linux均有有效,显示指明全缓冲区
static char buf[1024];
setvbuf ( stream , buff, _IOFBF , 1024);

对于上面的函数,Linux和Windows平台对setvbuf()的具体实现不尽相同,使用时也请大家注意,总结为以下几点: (1)Linux平台对设备文件默认开启行缓冲,对磁盘文件默认开启全缓冲。

开启关闭缓冲用setbuf()。在存在缓冲区的前提下,改变缓冲模式用setvbuf()。注意,不能使用setvbuf()来间接申请缓冲区,只能显示指明缓冲区。

//不存在缓冲区时,间接申请缓冲区无效
setvbuf(stdout,NULL,_IOFBF,100);

//可以显示指明缓冲区
static char buf[100];
setvbuf(stdout, buf,_IOFBF,100);

(2)Windows默认关闭缓冲,只能开启全缓冲,开启方式如下:

static char buf[100];
setbuf(stdout,buf);  //不安全,不建议使用
setvbuf(stdout, buf,_IOFBF,100);

//间接申请缓冲区
setvbuf(stdout,NULL,_IOFBF,100);

参考资料

[1]setvbuf

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏应兆康的专栏

每天一个Linux命令(1)—— ls

ls ls命令用来显示目标列表,在Linux中是使用率较高的命令。ls命令的输出信息可以进行彩色加亮显示,以分区不同类型的文件。 语法 ls(选项)(参数) ...

393100
来自专栏叁金大数据

自学Python四 爬虫基础知识储备

  首先,推荐两个关于python爬虫不错的博客:Python爬虫入门教程专栏   和 Python爬虫学习系列教程 。写的都非常不错,我学习到了很多东西!在此...

12210
来自专栏鹅厂少年的奇妙之旅

Go语言源码笔记 --- netpoller

总览:Go中网络交互采用多路复用的技术,具体到各个平台,即Kqueue、Epoll、Select、Poll等,下面以Linux下的Epoll实现为例进行分析。

1.2K80
来自专栏我是攻城师

Solr配置maxBooleanClauses属性不生效原因分析

26260
来自专栏较真的前端

[译] 调试 RxJS 第1部分: 工具篇

19440
来自专栏晓晨的专栏

IdentityServer Topics(2)- 定义资源

12330
来自专栏小白课代表

文件搜索利器——Everything

40720
来自专栏北京马哥教育

Shell的18条常用命令整理

? 作者 | junmail 来源 | CSDN社区 云豆贴心提醒,本文阅读时间8分钟,文末有秘密! 1、 ls: 类似于dos下的dir命令 ...

42440
来自专栏维C果糖

史上最简单的 MySQL 教程(四十二)「代码执行结构」

在 MySQL 中,没有for循环,仅有while循环、loop循环和repeat循环,呃,还有一种非标准的goto循环,在此我们仅介绍while循环,其基本语...

49170
来自专栏WeaponZhi

Python爬虫入门(二)

上一篇文章大概的讲解了 Python 爬虫的基础架构,我们对 Python 爬虫内部运行流程有了一定的理解了,我们这节将用一些简单的 Python 代码实现Py...

43660

扫码关注云+社区

领取腾讯云代金券