前言
不知道大家是否有过这样的疑惑,为什么读写文件时通常需要开辟一块内存缓冲区?内存缓冲区为何能够提升文件读写的效率?其实这一类问题都可以归入到操作系统中,接下来请跟随我的脚步,为大家揭秘背后的原理。
原理
首先我们要知道操作系统对各种硬件资源提供了虚拟化。例如操作系统通过时钟机制和轮询调度让一个CPU看起来像是分身成了多个CPU一样,以及原本是共享介质的内存也因为操作系统的虚拟化能够让每个进程独占内存,让进程只能访问自己的内存地址空间。通过虚拟化硬件,上层的用户可以方便的操控这些资源,同时系统资源的利用率和安全也得到了保障。磁盘也不例外,操作系统也对它进行了虚拟化,在其基础上虚拟出了文件系统、块等概念,今天我们要探讨的便是其中的文件块。
操作系统访问磁盘并不是一个字节一个字节的访问,所以不能得出如果一个文件有5个字符,那么它的磁盘访问耗时就是5s这样的结论(假设一次磁盘访问时间是1s)。操作系统访问磁盘的基本单位是文件块,是一个块一个块的访问,所以读写文件所产生的磁盘访问耗时大小是依赖于文件块的大小。例如系统中的文件块大小是4kb,那么读/写1b-4kb数据所产生的磁盘访问耗时都是1s。因此我们可以得出以下两个公式:
1.直接读/写磁盘时:
磁盘访问耗时=(读/写次数*(一次磁盘访问时间*n))
2.使用缓冲区读/写磁盘时:
磁盘访问耗时=(读/写次数一次内存访问时间)+(一次磁盘访问时间n)
(其中n为文件所占用的文件块的个数)
有了上述的两个公式,相信能够很容易的计算出磁盘访问耗时。假设系统中的文件块大小是4kb,一次内存访问时间是0.5s,一次磁盘访问时间是1s,给定一个要读取的大小50字节的文件b,每次读取10字节,请问直接读取完文件b和采用内存缓冲区读取完文件b分别所产生的磁盘访问耗时是多少呢?没错,答案就是5s(5s=5*(1s1))和3.5s(3.5s=50.5s+1s*s)。通过结果明显可以看出内存缓冲区所带来的的高效率了吧。
实践
准备工作:
1.用于计算读取文件耗时的c程序:
arduino
#include<stdio.h>
#include<stdlib.h>
#include<sys/time.h>
#include<unistd.h>
#include<fcntl.h>
int main(){
struct timeval starttv,endtv;
long sum=0;
int n,size;
char* fname=(char*)malloc(sizeof(char) * 100);;
printf("实验次数: ");
scanf("%d", &n);
printf("请输入缓冲区大小: ");
scanf("%d", &size);
printf("请输入要读取的文件名: ");
scanf("%s", fname);
FILE *fp = fopen(fname,"r");
for(int i=0;i<n;i++){
char buffer[size];
int readByte = 0;
gettimeofday(&starttv,NULL);
long startTime = starttv.tv_sec*1000*1000+starttv.tv_usec;
while((readByte=fread(buffer,sizeof(char),sizeof(buffer),fp))>0){
}
gettimeofday(&endtv,NULL);
long endTime = endtv.tv_sec*1000*1000+endtv.tv_usec;
sum+=endTime-startTime;
fseek(fp, 0L, SEEK_SET);
}
fclose(fp);
printf("磁盘访问耗时:%f微秒\n",(float)(sum)/n);
return 0;
}
实践证明1:操作系统访问磁盘的基本单位是文件块(4kb),读/写1字节所产生的耗时和读/写4096字节产生的耗时是相同的。
1.准备好三个文件a.txt(4096字节),b.txt(1字节),c.txt(4097字节)。
2.查看系统的文件块大小stat -c '%o' 文件名,可以看见块大小为4kb
3.调用测量程序计算读取a.txt和b.txt的磁盘耗时,双方耗时都是0.7微秒左右,即读/写1字节所产生的耗时和读/写4096字节产生的耗时是相 同的。
4.调用测量程序计算读取a.txt和c.txt的磁盘耗时,a.txt耗时0.7微秒,c.txt耗时1微秒。仅仅相差了一个字节就多出了这么多耗时,这是因为c.txt占用两个文件块,而a.txt只占用了一个块,同时也能看出操作系统访问磁盘的基本单位是文件块(4kb)。
实践证明2:使用内存缓冲区访问磁盘的效率高于直接访问磁盘
1.准备一个文件a.txt(4096字节)
2.调用测量程序,设置缓冲区大小为1(模拟不使用内存缓冲区,逐字节读取),耗时达到了惊人的37微秒!
3.调用测量程序,设置缓冲区大小为4096,效率大幅提升
总结
操作系统访问磁盘的基本单位是文件块,数据大小在一个文件块的范围内的文件产生的磁盘耗时都是相同的,通过内存缓冲区我们可以一次从磁盘中读取多个字节,提升程序性能。既然一次读1个字节磁盘耗时是1s,一次读100个字节磁盘耗时也是1s,那我们为什么不选择使用内存缓冲区一次读100个字节呢。