缓冲IO和非缓冲IO的区别(转载)

这两天在项目原有版本的基础上增加了一段新的协议实现代码,因此需要和平台进行联调。考虑到更好地进行调试,我在代码中添加了一段类似日志记录的代码,已获取通讯的报文内容和当时的环境参数内容,就是创建一个文件,使用标准IO的fopen、fprintf进行输出记录。但是在调试中,刚开始我就傻眼了,文件创建成功了,但是实时查看竟然没有任何数据记录。经过半天的担惊受怕和反复排查,发现是被标准IO的缓冲机制摆了一道,惭愧呀。。。

下面给出一个示例程序,模拟我的项目程序:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <io.h> //windows下编译要加
 
int main()
{
         FILE * fp=NULL;
         const char *filename_1= "test_fprintf.log" ;
         const char *filename_2= "test_write.log" ;
         int fd;
 
         fp = fopen (filename_1, "wb" );
         if (fp == NULL)
         {
                 printf ( "open %s failed, %s\n" , filename_1, strerror ( errno ));
                 return -1;
         }
         //setbuf(fp, NULL);
         //setvbuf(fp, NULL, _IONBF, 0);
         fd = open(filename_2, O_WRONLY|O_CREAT|O_EXCL, 0666);
         if (fd < 0)
         {
                 printf ( "open %s failed, %s\n" , filename_2, strerror ( errno ));
                 return -1;
         }
         while (1)
         {
                 fprintf (fp, "test fprintf.\n" );
                 fprintf (fp, "-------test fprintf.\n" );
                 fprintf (fp, "=======test fprintf.\n" );
                 //fflush(fp);
                 write(fd, "test open.\n" , sizeof ( "test open.\n" ));
                 write(fd, "--------test open.\n" , sizeof ( "--------test open.\n" ));
                 write(fd, "--------test open.\n" , sizeof ( "--------test open.\n" ));
                 sleep(1);
         }
 
         return 0;
}

后台运行上面的示例程序,然后实时查看两个日志文件,会发现test_frpintf.log文件一开始一直都是空的,而test_write.log则是不断有数据写入,如下状态: 
缓冲IO和非缓冲IO

我当时就是奇怪为什么文件会是空的。可以看出标准IO会缓冲4096Bytes的数据,当达到这么多数据时才会进行实际的磁盘写入,而系统调用write则是直接写入,不进行缓冲。

标准IO库提供缓冲的目的是尽可能减少使用read和write调用的次数,降低执行IO的时间,它提供三种类型的缓冲:

  1. 全缓冲。在填满标准IO缓冲区后才进行实际IO操作,对于磁盘文件通常就是全缓冲,上面的示例就是采用缓冲。
  2. 行缓冲。在输入和输出中遇到换行符时进行实际的IO操作,当涉及到一个终端时,通常使用行缓冲。使用最频繁的printf函数就是采用行缓冲,所以感觉不出缓冲的存在。
  3. 不带缓冲。标准IO库不对字符进行缓冲存储。标准出错流stderr通常是不带缓冲的。

ISO C要求下列缓冲特征:

  • 当且仅当标准输入和标准输出并不涉及交互式设备时,它们才是全缓冲的。
  • 标准出错决不会是全缓冲。

很多系统默认使用下列类型的缓冲:

  • 标准出错是不带缓冲的。
  • 如若是涉及终端设备的其它流,则他们是行缓冲的;否则是全缓冲的。

当然,对于标准IO流,我们也可以更改缓冲类型,或者是直接刷新。ISO C中提供下面两个函数以更改缓冲类型:

?
1
2
void setbuf ( FILE *fp, char *buf); //buf为NULL,表示关闭缓冲
int setvbuf ( FILE *fp, char *buf, int mode, size_t size); //成功返回0,出错返回非0值

setvbuf函数中的mode参数可以为:IOBUF 全缓冲, IOLBF 行缓冲, _IONBF 不带缓冲,如果buf为NULL, 则标准IO库将自动地为该流分配适当长度(常量BUFSIZ)的缓冲区。一般而言,应由系统选择缓冲区的长度,并自动分配缓冲区,这样关闭流时,标准IO库将自动释放缓冲区。

强制冲洗一个流,使用函数:

?
1
int fflush ( FILE *fp); //成功返回0, 出错返回EOF

项目中我是使用这个函数解决郁闷的。

?
1
fflush (NULL); //冲洗所有输出流

参考: 《APUE》 第2版 第5章 标准IO库 5.4 缓冲

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值