由于访问内存的速度比访问磁盘的速度快了几个数量级,如果能对经常用到的或者最近可能要用到的磁盘上的数据提前读出来,存放在内存当中,就能明显缩短读写完成的时间,显著提高性能。为此在文件系统中缓存机制在许多地方得到了应用。总结起来,常用到的有buffer cache、 page cache、 和DNLC(Directory name look-up cache)三种,分别用在对meta-data、data读写以及路径名解析的场景。 下面重点介绍下buffer cache。


    为了能对buffer cache有些基本的感性认识,将从unix和linux系统分别看看buffer cache。


Unix系统中buffer的概念,当然需要结合它的数据结构去理解。刚开始本人的理解是有点类似于Linux中的

BIO的原型,后来才明白BIO主要是用来记录从上层传递到底层的io请求的 ,而buffer cache是和page

cache相对的,主要是用来缓存对meta data (blkptr/inde/等) 的访问的。buffer cache中buffer具体

的数据结构如下:


wKiom1i0Fk3yNTZ-AACFj-zyOMQ095.jpgbuf怎样组织: 基于b_back/b_ford的双向链表,头结点是bfreelist。

buf如何初始化: 内核启动的时候初始化,在unix中是初始化NBUF个buffers。

Buf cache如何实现:在IO完成、buffer释放之后,被释放的buf会被放到av_forw/av_back指向的buf空

闲列表里面去,但此时它的identifier(device ID + block offset)会在被重放被别的请求申请、使用之前

一直保留,因此后面的读只需要直接从内存里读出就好了。


基于buf的两个最主要的接口函数:(对应于buffer cache的主要操作)


bread():

a. call getblk(device ID, block offset); (check whether the buffer is already in buffer caches),

return if true;

b. call iowait() if cache missed

bwrite():和bread()类似,也是调用那连个函数。


而在Linux中buffer cache又是如何实现、怎么使用的呢?


    Linux中的Buffer cache顾名思义,buffer cache也是缓冲区组成的磁盘高速缓存。

在linux中,每个缓冲区都存放一个单独的磁盘块,块IO操作依靠buffer cache来减小对磁盘的慢速访

问,比如读ext2超级块、inode节点。


    在linux中buffer cache同样提供了接口函数,包括:bread()

和unix的区别是bwrite():不存在专门的bwrite(), 这是因为只需要设置磁盘块对应buffer的dirty位置为

1, 就可以被后续的内核线程写会到磁盘。


Linux如何标识一个buffer to cache: 块设备ID/major/minor device ID / + 块内偏移

何时加入cache:在通过bread()读索引节点或者超级块的时候

a. getblk() 根据参数中的设备标示符、块号以及块大小,在buffer cache 中查找;如命中就返回该buffer; 如果不命中返回一个新的buffer;

b.调用mark_page_accessed()来标记这个新的buffer

c.如果缓冲区已有有效数据,则终止;

d.调用ll_rw_block()开始从磁盘读数据;

e.通过wait_on_buffer()等待,直到数据传送完毕。原理是这个函数会把内核current()进程、线程放到那

个新的buffer的缓冲区首部的strucut wait_queue * b_wait 字段队列中。


如何判断命中:根据上面的描述,不难看到是在buffer cache 数组中找到了指定id的buffer头部

何时从cache中调出:(替换策略) LRU  

针对磁盘上的哪些数据:inode/ 超级块