为什么要有文件缓冲区的存在?
假设甲在云南,甲的朋友乙在北京,甲想给乙送个东西就需要跑到北京去:这时候有菜鸟驿站了,甲就不用跑了,直接把包裹交给菜鸟驿站就可以了。缓冲区就类似于菜鸟驿站,缓冲区可以提高使用者的效率。
菜鸟不会因为你一个包裹就专门送一趟,而是一次性送大量包裹,这样可以节约成本。缓冲区也是如此,正是因为有缓冲区,才可以积累一部分在统一发送,可以提高发送的效率。
菜鸟会给不同日期的包裹分组,按组发货。缓冲区也是如此,有不同的刷新方式。
大致分为以下几种普通情况:
普通情况
1.无缓冲(立即刷新)
2.行缓冲(行刷新)
3.全缓冲(缓冲区满了,再刷新)
特殊情况
1.菜鸟为了节约成本采用全缓冲,等仓库满了才发货,但是这样太慢了,收到了用户的投诉,要求立刻发货,这个就称之为强制刷新。
2.菜鸟倒闭了,但是要在关门之前把所有仓库里的存货全部发出,这个叫做在进程结束前刷新。
实验1
在实现进行之前,我们需要了解两个常识:
1.在显示器中一般采用行刷新
2.对于磁盘文件一般采用全刷新。
实验开始。
我们写一段代码,通过printf,fputs,fprintf,write,向显示器打印一些内容:
#include<stdio.h>
#include<string.h>
#include<unistd.h>
int main()
{
fprintf(stdout,"c:hello fprintf\n");
printf("c:hello printf\n");
fputs("c:hello fputs\n",stdout);
const char *str="system call:hello wirte\n";
write(1,str,strlen(str));
return 0;
}
fprintf和printf,fputs都为c语言接口,其中fprintf和puts都需要显式指示向哪个流输出,printf是隐式的。write是系统调用接口。
现在我们加一句代码:
我们发现一个现象,将输出重定向到log.txt之后c语言的一共打印了七次,其中c语言的调用每个都打印了两次,系统调用的接口只打印了1次。
我们都知道fork()之后父子进程共享,所以这里如果fork()在最前面,那么可以理解后面为什么每个输出都输出两次。但是这里fork()在最后面,为什么每个输出还会每个输出两次呢?
原因
因为向显示器输出是按行刷新的,文件重定向之后向log.txt文件输出,log.txt属于磁盘文件,磁盘文件是全刷新,读入的内容不足以把缓冲区写满,在执行fork的时候,缓冲区还没有满,没有刷新,执行完fork之后因为进行要结束了,即便此时不满足刷新条件,但是进程结束时还是要把缓冲区里面的内容刷新。
但是我们发现,系统调用接口并没有受影响,还是只打印了一次,因此我们可以确定,缓冲区是和操作系统没有关系的。
总结:我们日常用的缓冲区由c语言提供,和操作系统没有关系,称为语言级别缓冲区。
问题1
c/c++的缓冲区里一定存放的是用户自己的数据,这些数据属于进程在运行时自己的数据吗?
答案是属于。
如果我们把数据交给了OS,这个数据就属于OS,不属于我自己的。
问题2
当进程退出的时候,需要清空缓冲区,即便你的数据没有满足刷新条件!请问刷新缓冲区属于清空或写入操作吗?
答案是算。
在fork()的时候缓冲区里面的数据还在,fork()之后,父子进程总有一个要先结束,进程结束的时候会刷新缓冲区,就相当于清空或写入操作。父子进程共享数据,所以另一个进程会产生写时拷贝,所以才会有两份数据。
但是根据程序运行结果来看,只有c语言的接口打印了两份,系统调用接口不受影响,因此可以说明write()接口不使用c语言的缓冲区。
问题3
什么叫做刷新?