【Kernel】页高速缓存和页回写
一、页高速缓存
页高速缓存是Linux内核实现磁盘缓存。它主要用来减少对磁盘的I/O操作。具体而言,通过把磁盘的数据缓存到物理内存中,把对磁盘的访问变为对物理内存的访问。
二、页高速缓存的价值
磁盘高速缓存的价值在两方面:
- 访问磁盘的速度远低于访问内存的速度;
- 数据一旦被访问,就很有可能在短期内再次被访问(时间局部性原理),这些数据会被暂存在高速缓存中,实现快速命中。
三、页高速缓存的实现
页高速缓存由RAM中的物理页组成。缓存中每一页对应着磁盘中的多个块。
读缓存
- 内核开始一个读操作,首先检查页高速缓存中是否有该数据。
- 如果有,则直接读取。这一步叫做缓存命中;如果没有,则直接从磁盘读写,这叫未缓存命中。
- 内核调度块I/O操作从磁盘中读取数据,并将全部数据或者部分数据放入页高速缓存中。注意:缓存谁是取决于谁被访问到。系统不一定将文件中全部放入内存,可能只是部分内容。
写缓存
-
不缓存。不经过页高速缓存,直接将数据写入磁盘。
-
写透缓存。写操作时自动更新内存缓存,同时更新磁盘文件。
-
回写。程序执行写操作直接将数据写到缓存中,然后将页高速缓存中被写入的页面标记为“脏”,并且将这些“脏”页面加入到脏链表中。最后,回写进程会将脏链表中的脏页刷新到磁盘中,最终做到保证磁盘和内存中数据的一致性。目前主流写缓存策略就是回写策略。
缓存回收
缓存回收,也叫缓存淘汰,作用有三个:
- 缓存中的数据清除(即清除页的数据);
- 为更重要的缓存项提供空闲物理页;
- 为了收缩缓存大小,以减轻内存的压力。
缓存中的什么内容将被清除的策略或者什么样的干净页可以回收的策略,就叫做缓存策略。
Linux的缓存回收是通过选择干净页(非脏页)进行简单地替换——注意:干净页也是有数据的,只不过这个页此时没有回写操作罢了。如果干净页不够的话,内核强制进行回写操作,以释放出更多的干净页。
对于回收策略Linux内核提供了两种方法:
(1)最近最少使用
简称LRU。LRU回收策略需要跟踪每个页面的访问踪迹,以便回收最老时间戳的页面。
该算法的效果在于:缓存的数据越久未被访问,则越大可能不会再次被访问,而近期访问的,则很大可能会再次被访问。
但是,LRU有一个致命的缺点——内核无法预测一个文件只会被访问一次后不会再被访问。
(2)双链策略
这个是修改过后的LRU,内核维护的不再是一个LRU链表,而是两个:活跃链表和非活跃链表。
处于活跃链表的页面被认为是‘热’的,且不会被换出(回收);而在非活跃链表上的也是会被回收的。
四、脏数据写入磁盘时机
脏数据:页高速缓存的数据比后台存储的数据更加新的时候,这些数据就叫做脏数据。
脏数据被回写到磁盘的时机有3种:
- 当空闲内存低于一个特定阈值时。内核必须回写数据以释放内存,因为缓存回收只能回收干净页。当干净页数量足够多时,内核就会收缩缓存,释放内存。该值可以通过修改/proc/sys/vm/dirty_background_ratio设定(这个值是百分比,入文件中数字为10,则表示占全部内存的10%)
- 当脏页在内存中存储时间超过一个阈值后,内核必须将脏页回写入磁盘,确保脏页不会无限期驻留内存。
- 用户进程显示调用sync()和fsync()系统调用时,内核会按要求将数据回写进磁盘。
以上工作均有一群flusher线程执行。
为什么是一群了?想象下,如果是单个线程,当回写操作很多时,就会造成拥塞。这是因为单一线程可能堵塞在某个设备的已拥塞请求队列(正在等待将请求提交给磁盘的I/O请求队列上),而其他设备的请求队列无法得到处理。
在2.6.32内核中,flusher线程取代了pdflush线程,pdflush线程个数一般为2-8个;而pdflush又是取代最早的bdflush和kupdated两个线程。
flusher线程的数目是根据磁盘数量变化的**,一般每个磁盘对应一个线程**
四、参考资料与补充
参考资料
《Linux设计与实现》