页高速缓存和页回写
从访问速度来看,内存访问速度高于磁盘访问,而缓存访问速度又高于内存。
缓存手段
页高速缓存是由内存中的物理页面组成,对应的是磁盘上的物理块,可以动态扩大缩小。当内核开始读操作时,会先检查数据是否子啊缓存中,不命中采取磁盘找。
现在的缓存一般焊接在CPU上
写缓存
Linux在调用写操作时,使用的策略是——回写。这种策略是直接写到缓存中,而存储不是立刻更新,而是将页高速缓存中被写入的页面标记成脏页,表示缓存与磁盘不同步。延迟写磁盘,能方便后续对该页面的操作。
缓存回收
Linux实现的是一种改良LRU算法,则使用双链。Linux维护的是两个链表——活跃链表和非活跃链表,非活跃链表的页面是可以被换出的,而两个链表要保持数量平衡。
Linux页高速缓存
由于页高速缓存中的页可能包含多个不连续的物理磁盘块。Linux页高速缓存使用一个新的结构来管理缓存项和页面io操作。
1 | struct address_space { |
其中i_mmap字段是一个优先搜索树,它包含了在该结构体中所有共享的与私有的映射页面。
address_space 操作
a_ops域指向地址空间对象中的操作函数表,由adress_space_operations结构体表示:
1 | struct address_space_operations { |
这里面最重要的是读写——readpage()和writepage()。
对于readpage,Linux内核试图在页高速缓存中使用find_get_page(mapping, index)找到需要的数据,将一个adress_space对象和一个偏移量传给该方法。如果找不到会返回一个NULL,并且内核新分配一个页面,并之前搜索的页面加入到页高速缓存。
对于writepage,首先在cache中搜索需要的页,如果需要的页不在,则新分配一个空闲项;下一步,内核会创建一个写请求,接着数据被从用户空间拷贝到内核缓冲,最后写入磁盘。
基树
每个address_page对象都有唯一的基树,保存在page_tree结构体中。只要指定了文件偏移量,就可以在基树中迅速检索到希望的页面。
flusher线程
在内存中累积起来的脏页最终会被写回磁盘,当下列3种情况发生时,脏页被写回磁盘:
- 当空闲内存低于阈值时;
- 当脏页在内存中驻留时间过长;
- 当用户进程调用sync()和fsync()时;
在Linux内核中由一群flusher线程执行这三种工作,flusher线程会被周期性唤醒,或者因为空闲内存过低被唤醒。