页缓存和块缓存
内核为块设备提供了两种通用的缓存方案:
- 页缓存:针对以页为单位的所有操作,例如内存映射技术,负责了块设备的大部分工作
- 块缓存:以块为操作单位,存取的单位是设备的各个块,由于块长度取决于特定的文件系统,块缓存能处理不通长度的块
缓冲区曾经是块设备进行I/O操作的传统方法,目前只用于支持很小的读取操作,对于块传输的标准数据结构变为struct bio,这种方式可以合并统一请求中后续的块,加速处理,但是对于单个块的操作,缓冲区仍然是首选,例如经常按块读取元数据的系统。
很多场合下,页缓存和块缓存结合使用(一个缓存的页在写操作期间划分为不同的缓冲区,在更细的粒度识别出被修改的部分,在数据写回时,只需要回写被修改的部分,不需要整页传输)
页缓存的结构
管理和查找缓存的页
Linux采用基数树的数据结构管理页缓存中的页,如下图
树的根有一个简单的数据结构表示,包含了树的高度(所包含节点的最大层次数目)和一个指针,指向组成树的第一个节点的数据结构
树的节点具备两种搜索标记,二者用于指定给定页当前是否为脏(即页的内容和后备存储器的数据不同)或该页是否正在向底层块设备回写。标记会一直向上设置到根节点,如果某个层次 n+1 的节点设置了某个标记,那么 n 层次的父节点也会设置该标记。好处是内核可以判断在某个范围内是否有一页或多页设置了某个标记位
回写修改的数据
由于页缓存的存在,写操作不是直接对块设备进行,而是在内存中进行,修改的数据首先被收集起来,然后被传输到更低的内核层,在那里对写操作进一步优化,具体的优化过程详见 通用块设备层。这里从页缓存的视角来看,需要确定何时回写?
内核提供如下几种同步方案:
- 内核守护进程pdflush周期性地同步,他们扫描缓存中的页,将超出一定时间没有与底层块设备同步的页写回
- 如果缓存中修改的数据项数目在短期内内明显增加,内核会主动激活pdflush进程
- 提供了系统调用给用户调用来写回未同步的数据,常用的有sync调用
为管理可以按整页处理和缓存各种不同对象,内核使用了**“地址空间”**抽象,将内存中的页和特定的块设备关联起来,每个地址空间都有一个“宿主”,所谓其数据来源,一般用inode表示
一般修改文件或者按页缓存的对象时,只会修改页的一部分,为节约时间,在写操作期间,会将缓存中的每一页划分为较小的单位&#