页高速缓存(cache)是Linux内核实现磁盘缓存的机制。磁盘高速缓存之所以重要的原因有二:1.访问磁盘的速度要远远低于访问内存的速度;2.数据一旦被访问,根据临时局部原理,它将很有可能在短时间内再次被访问到。
1.缓存手段
页高速缓存是由内存中的物理页面组成的,其内容对应磁盘上的物理块。页高速缓存大小能动态调整--它可以通过占用空闲内存以扩张大小,也可以自我收缩以缓解内存压力。注意,系统不一定要将整个文件都缓存,而只是缓存部分。
1.1写缓存
包括以下三种:
- 不缓存。直接写磁盘。
- 写透缓存。写操作自动更新内存缓存,同时也更新磁盘文件。
- 回写。程序执行写操作直接写到缓存中,后端存储不会立刻更新,而是将页高速缓存中被写入的页面标记为『脏』页,并被加入到脏页链表。然后由一个进程周期性将脏页链表中的页写回到磁盘。
1.2缓存回收
缓存算法最后涉及的重要内容是缓存中的数据如何清除;或者是为更重要的缓存项腾出位置;或者是收缩缓存大小,腾出内存给其他地方使用。这个工作被称为缓存回收策略。Linux的缓存回收是通过选择干净页进行简单替换。如果缓存中没有足够干净页面,内核将强制进行回写操作,以腾出更多地干净可用页。理想的回收策略是回收那些以后最不可能使用的页面,但这个谁也不知道,所以理想的回收策略被称为预测算法。
目前有的算法如:1.最近最少使用算法(LRU)。2.双链策略。这个是Linux实现的一个修改过的LRU算法。它维护两个链表:活动链表和非活动链表,处于活动链表上的页面被认为是『热』的且不会被换出,而非活动链表页面可以被换出。具体算法参考详细资料。
2.Linux页高速缓存
页高速缓存缓存的是内存页面。在页高速缓存中的页可能包含了多个不连续的物理磁盘块。比如x86体系下一个屋里页大小是4KB。而大多数文件系统的块大小仅仅是512B。所以8个块才填满一个页面。另外因为文件本身可能分布在磁盘的各个位置,所以页面中映射的快也不需要连续。
虽然Linux页高速缓存可以通过扩展inode结构体支持页I/O操作,但这样会将页高速缓存局限于文件,Linux页高速缓存使用了一个新对象管理缓存项也页I/O操作,这个对象是address_space
结构体。该结构体是vm_area_struct
的物理地址对等体。当一个文件可以被10个vm_area_struct
结构体标识,那么这个文件只能有一个address_space
数据结构--也就是文件可以有多个虚拟地址,但是只能在物理内存有一份。
其中i_mmap
字段是一个优先搜索树,它的搜索范围包含了在address_space
中所有共享的与私有的映射页面。优先搜索树是一种巧妙地将堆与radix树结合的快速检索树。
如果搜索的页并没在高速缓存中,find_get_page()
会返回一个NULL,并且内核将分配一个新页面,然后将之前搜索的页加入到页高速缓存中。
通常写操作包括:在页高速缓存中搜索需要的页,如果需要的页不在其中,那么内核在高速缓存中新分配一空闲项;下一步,内核创建一个写请求;接着数据被从用户控件拷贝到了内核缓冲;最后将数据写入磁盘。
3.缓冲区高速缓存
独立的磁盘块通过块I/O缓冲也要被存入页高速缓存。一个缓冲室一个物理磁盘块在内存里地表示。缓冲的作用就是映射内存中的页面到磁盘块,这样一来页高速缓存在块I/O操作时也减少了磁盘访问,因为它缓存磁盘块和减少I/O操作。这个缓存通常被称为缓冲区高速缓存,虽然实现上它没有座位独立缓存,而是作为页高速缓存的一部分。块I/O操作又一次操作一个单独的磁盘块(扇区)。普通的块I/O操作是读写i节点。通过缓存,磁盘块映射到它们相关的内存页,并缓存到页高速缓存中。在2.4以及之后的版本,将他们统一了。
在以下3种情况发生时,脏页需要被回写到磁盘:
- 当空闲内存低于一个特定的阈值。
- 当脏页在内存中驻留时间超过一个特定的阈值。
- 当用户进程调用
sync()
和fsync()
。
一组flusher线程干的就是这三个工作,它将会被周期性唤醒(和空闲内存是否过低无关)进行回写。
参考
《Linux内核设计与实现》
说明
装载请注明出处:http://vinllen.com/linuxye-gao-su-huan-cun-he-ye-hui-xie/