Linux IO流程中的各级缓存

发布于 2019-10-31  53 次阅读


buffer cache

一般情况下,进程在io的时候,要依赖于内核中的一个buffer模块来和外存发生数据交换行为。另一个角度来说,数据从应用进程自己的buffer流动到外存,中间要先拷贝到内核的buffer中,然后再由内核决定什么时候把这些载有数据的内核buffer写出到外存。

“buffer cache”仅仅被内核用于常规文件(磁盘文件)的I/O操作。

当前系统上第一次读一个文件时,Read系统调用触发内核以block为单位从磁盘读取文件数据,并把数据blocks存入内核buffer,然后read不断地从这个buffer取需要的数据,直到buffer中的数据全部被读完,接下来,内核从磁盘按顺序把当前文件后面的blocks再读入内核buffer,然后read重复之前的动作…

一般的文件访问,都是这种不断的顺序读取的行为,为了加速应用程序读磁盘,unix的设计者们为这种普遍的顺序读取行为,设计了这样的机制—-预读,来保证进程在想读后续数据的时候,这些后续数据已经的由内核预先从磁盘读好并且放在buffer里了。这么做的原因是磁盘的io访问比内存的io访问要慢很多,指数级的差别。

”buffer cache”有五个flush的触发点

  • pdflush(内核线程)定期flush;
  • 系统和其他进程需要内存的时候触发它flush;
  • 用户手工sync,外部命令触发它flush;
  • proc内核接口触发flush,”echo 3 >/proc/sys/vm/drop_caches;
  • 应用程序内部控制flush。

应用层buffer

既然write()能把需要写文件的数据推送到一个内核buffer来偷工减料欺骗应用层(为了加速I/O),说“我已经写完文件并返回了”。

那应用层的标准C库的fwrite()按道理也可以为了加速,在真正调用write()之前,把数据放到(FILE*)stream->buffer中,

等到多次调用fwrite(),直至(FILE*)stream->buffer中积攒的数据量达到(FILE*)stream->bufferlen这么多的时候,

一次性的把这些数据全部送入write()接口,写入内核
实际上,标准C库就是这么做的!

fwrite()在windows平台的实现也基本上是这样的,也有buffer。

值得一说的是,fread()也有一个读cache来完成预读。

setvbuf()和setbuf()都是控制这个标准C库的buffer的。

还有fflush()是C库用于flush数据的函数。

以上三个函数,如果大家有兴趣,可以去看看linux上对应的man文档。

raid卡和磁盘缓存

建立阵列的时候(raid5),关于RAID卡缓存的默认选项是:

读取策略:自适应

写策略:回写

磁盘高速缓存策略:禁用

属性解释:

读取策略:一般要启用,采用预读取策略,可提高“随机读取”性能。第二次读取相同数据时可以命中缓存。

写策略:一般要启用“回写”,操作的是RAID卡上的缓存。写入数据时先写入到缓存就算写入成功了,然后RAID卡控制器再把多个写IO合并为一个写IO一次性写入磁盘,提高“随机写入”的性能。因为RAID卡带电池,机房停电时,电池可给缓存供电72小时。缓存中的数据不会丢失。另外,如果没有给缓存接电池,默认“写缓存”是不被启用的(除非强行设定为“没有电池也启用写缓存”)

磁盘高速缓存策略: 操作的是磁盘自带的高速缓存。 做RAID时,一般要禁用,防止机房停电时磁盘自带缓存中的数据丢失。磁盘可不带电池。

喜欢这篇文章吗,不妨分享给朋友们吧!

科学是第一生产力