目录
一.缓冲区概念介绍
(一).基本概念
所谓缓冲区,就是当我们在编程向文件中输入数据时,数据不会直接写入文件,而是先写入一个名为缓冲区的区域,经过对缓冲区的刷新后才能写入文件中。
可以通过此例作以证明:
下例中,我们打开一个文件,使用fwrite向其中输入数据,第一次采用直接使用close关闭文件,第二次采用刷新缓冲区fflush后close关闭文件。看看有什么不同:
//代码一
int main()
{
FILE* fp = fopen("log.txt", "w");
const char* str = "hello world\n";
fwrite(str, strlen(str), 1, fp);
close(fp->_fileno);//系统调用接口,fileno:即文件描述符fd
return 0;
}
//代码二
int main()
{
FILE* fp = fopen("log.txt", "w");
const char* str = "hello world\n";
fwrite(str, strlen(str), 1, fp);
fflush(fp);//刷新缓冲区
close(fp->_fileno);
return 0;
}
我们发现,如果没有刷新缓冲区那么数据将无法写入数据,这说明fwrite确实是先将数据写入缓冲区再写入文件。
(二).为什么需要缓冲区?
因为当我们向文件中写入数据时,这是一个IO的过程。IO的次数越多,那么时间浪费越多,效率越低。如果使用一个东西预先存储这些数据,当到达一定规模时统一写入文件,那么IO的次数就会减少,进而效率提升。
因此,缓冲区存在的意义就是通过减少IO次数达到效率上的提升。
二.剖析缓冲区本质
在上述文件中,我们可能会好奇,为什么写入操作使用c语言接口fwrite,而关闭操作使用系统调用接口close?
回答这个问题,需要我们一点点搞清楚缓冲区的本质:
(一). 缓冲区刷新规则
严格来讲,缓冲区共有四种刷新规则:立即刷新、行刷新、满刷新、强制刷新。
①立即刷新
所谓立即刷新就是字面意义,每当向缓冲区中写入数据就刷新一次。
②行刷新
当在数据中检测到换行符'\n'时刷新,这叫行刷新。
比如向显示器printf("hello world\n");时会执行行刷新。
③满刷新
当缓冲区中数据已满时刷新,这叫满刷新。
④强制刷新
即由人强制缓冲区执行刷新操作,例如fflush就是一种强制刷新。
(二).不同属性文件的默认刷新规则
不同文件对应刷新规则不同,这是主要是由文件使用性质决定的。
对于标准输入输出流(显示器、键盘)来说,其采用行刷新策略。
这是因为它们在使用时,需要考虑使用者的感受。试想一下,如果你写了超大一片文章,显示器一口气显示出来便于阅读还是一行行显示便于阅读?
对于磁盘文件来说,其采用满刷新策略。
这是因为磁盘文件数据并不是及时阅读的,刷新的次数越少效率越高。
因此,即便我们写入的文字中有换行符,缓冲区也不会立即刷新。
(三).谁在制造缓冲区?
本小节将回答大标题二开头的那个问题。
在回答这个问题前,请大家跟我一起看一段代码:
这两段代码唯一的区别就是一个使用close关闭文件,一个使用fclose关闭文件:
//代码一
int main()
{
FILE* fp = fopen("log.txt", "w");
const char* str = "hello world\n";
fwrite(str, strlen(str), 1, fp);
close(fp->_fileno);//系统调用接口
return 0;
}
//代码二
int main()
{
FILE* fp = fopen("log.txt", "w");
const char* str = "hello world\n";
fwrite(str, strlen(str), 1, fp);
fclose(fp);//c接口
return 0;
}
看看结果有什么不同:
通过结果我们发现,当使用系统接口时,缓冲区并没有被刷新;而使用c语言接口,缓冲区被刷新。根据我们已知的认识,fclose内部封装了close。那么就是说缓冲区是在fclose内部被刷新后,再调用的close系统接口。
因此,所谓缓冲区,其实是c语言提供的!
这样之前的问题也就有了答案:我们之所以没有使用c语言接口而是使用系统的close,是因为fclose内部会刷新缓冲区,根本无法观察到差异。而close作为系统调用接口,其并没有缓冲区,会直接关闭文件,进而缓冲区内的数据也就无法写入了。
那么问题又来了,缓冲区在哪里呢?
(四).缓冲区的位置
开门见山直接回答:在FILE结构体中!
每当我们打开一个文件时,c语言都会为其创建一个FILE类型的结构体,用以记录该文件的相关信息,比如文件大小,文件描述符之类的。当然,其内部也会有一个字符型指针指向一片动态开辟的空间,而这个空间就是缓冲区!
一切请以实物为准,我们直接看FILE结构体源代码:
(五).总结
缓冲区是由语言层面提供,在c语言中在FILE结构体内部,本质上是在堆上开辟的一段空间,通过字符型指针对其操作。每当打开一个文件时,都会创建一个FILE结构体,因此每一个文件都有与之对应的缓冲区。
最初的90%的代码用去了最初90%的开发时间,余下的10%的代码用掉另外90%的开发时间
——Tom Cargill
如有错误,敬请斧正