日期:2014-05-16  浏览次数:20632 次

每天进步一点点——Linux下的磁盘缓存
转载请说明出处:http://blog.csdn.net/cywosp/article/details/21126161

    前段时间在开发一个使用SSD做缓存的系统,在高速写入数据时会出现大量的磁盘缓存。太多的磁盘缓存如果没有及时的写入磁盘中,在机器出现问题时是非常危险的,这样会导致很多的数据丢失,但是如果实时的将数据刷入磁盘中,这样写入效率有太低了。为了弄明白Linux系统的这种磁盘写入特性,最近深入的学习了一下。
    VFS(Virtual File System)的存在使得Linux可以兼容不同的文件系统,例如ext3、ext4、xfs、ntfs等等,其不仅具有为所有的文件系统实现一个通用的外接口的作用,还具有另一个与系统性能相关的重要作用——缓存。VFS中引入了高速磁盘缓存的机制,这属于一种软件机制,允许内核将原本存在磁盘上的某些信息保存在RAM中,以便对这些数据的进一步访问能快速进行,而不必慢速访问磁盘本身。高速磁盘缓存可大致分为以下三种:
  • 目录项高速缓存——主要存放的是描述文件系统路径名的目录项对象
  • 索引节点高速缓存——主要存放的是描述磁盘索引节点的索引节点对象
  • 页高速缓存——主要存放的是完整的数据页对象,每个页所包含的数据一定属于某个文件,同时,所有的文件读写操作都依赖于页高速缓存。其是Linux内核所使用的主要磁盘高速缓存。
正是由于缓存的引入,所以VFS文件系统采用了文件数据延迟写的技术,因此,如果在调用系统接口写入数据时没有使用同步写模式,那么大多数据将会先保存在缓存中,待等到满足某些条件时才将数据刷入磁盘里。

内核是如何将数据刷入磁盘的呢?在看完以下两点后就能得到答案。

1. 把脏页写入磁盘
    正如我们所了解的,内核不断用包含块设备数据的页填充页高速缓存。只要进程修改了数据,相应的页就被标记为脏页,即把它的PG_dirty标志位置。
    Unix系统允许把脏缓冲区写入块设备的操作延迟执行,因为这种策略可以显著地提高系统的性能。对高速缓存中的页的几次写操作可能只需对相应的磁盘块进行一次缓慢的物理更新就可以满足。此外,写操作没有读操作那么紧迫,因为进程通常是不会因为延迟写而挂起,而大部分情况都因为延迟读而挂起。正是由于延迟写,使得任一物理块设备平均为读请求提供服务将多于写请求。
一个脏页可能直到最后一刻(即直到系统关闭时)都一直逗留在主存中。然而,从延迟写策略的局限性来看,它有两个主要的缺点:
  一、如果发生了硬件错误或者电源掉电的情况,那么就无法再获得RAM的内容,因此,从系统启动以来对文件进行的很多修改就丢失了。
  二、页高速缓存的大小(由此存放它所需的RAM的大小)就可要很大——至少要与所访问块设备的大小不同。
因此,在下列条件下把脏页刷新(写入)到磁盘:
  • 页高速缓存变得太满,但还需要更多的页,或者脏页的数量已经太多。
  • 自从页变成脏页以来已过去太长时间。
  • 进程请求对块设备或者特定文件任何待定的变化都进行刷新。通过调用sync()、fsync()或者fdatasync()系统调用来实现。
缓冲区页的引入是问题更加复杂。与每个缓冲区页相关的缓冲区首部使内核能够了解每个独立块缓冲区的状态。如果至少有一个缓冲区首部的PG_Dirty标志被置位,就应该设置相应缓冲区页的PG_dirty标志。当内核选择要刷新的缓冲区时,它扫描相应的缓冲区首部,并只把脏块的内容有效的写到磁盘。一旦内核把缓冲区的所有脏页刷新到磁盘,就把页的PG_dirty标志清0。

2. pdflush内核线程