1 缓存
linux为了提高对磁盘的访问速度,通过把磁盘中的数据缓存到物理内存中,把对磁盘的访问变为对物理内存的访问。
两个重要因素:
1)访问磁盘的速度要远低于访问内存的速度,若从处理器L1和L2高速缓存访问则速度更快。
2)数据一旦被访问,就很有可能短时间内再次访问。
1.1 缓存策略
3种策略:
1)不缓存:不缓存写操作,当对缓存中的数据进行写操作时,直接写入磁盘,同时使此数据的缓存失效
2)写透缓存:写数据时同时更新磁盘和缓存
3)回写:先写到缓存,并将页面标记为“脏”,加入脏页链表,由回写进程周期性将脏页链表中的页写回到磁盘。linux采用此方法。
1.2 缓存回收
(1)最近最少使用LRU:将最近最少使用的页面进行回收
(2)双链策略:内核基于LRU,维护两个链表:活跃链表和非活跃链表,页面会在这两个双向链表中移动,操作系统会根据页面的活跃程度来判断应该把页面放到哪个链表上。
2 页高速缓存
一个物理页可能由多个不连续的物理磁盘块组成。也正是由于页面中映射的磁盘块不一定连续,所以在页高速缓存中检测特定数据是否已被缓存就变得不那么容易了。
(1)address_space
用于管理缓存项和页IO操作。文件可以有多个虚拟地址,但是这个文件只能有一个address_space对象。
struct address_space {
struct inode *host; /* 拥有此 address_space 的inode对象 */
struct radix_tree_root page_tree; /* 包含全部页面的 radix 树 */
spinlock_t tree_lock; /* 保护 radix 树的自旋锁 */
unsigned int i_mmap_writable;/* VM_SHARED 计数 */
struct prio_tree_root i_mmap; /* 私有映射链表的树 */
struct list_head i_mmap_nonlinear;/* VM_NONLINEAR 链表 */
spinlock_t i_mmap_lock; /* 保护 i_map 的自旋锁 */
unsigned int truncate_count; /* 截断计数 */
unsigned long nrpages; /* 总页数 */
pgoff_t writeback_index;/* 回写的起始偏移 */
const struct address_space_operations *a_ops; /* address_space 的操作表 */
unsigned long flags; /* gfp_mask 掩码与错误标识 */
struct backing_dev_info *backing_dev_info; /* 预读信息 */
spinlock_t private_lock; /* 私有 address_space 自旋锁 */
struct list_head private_list; /* 私有 address_space 链表 */
struct address_space *assoc_mapping; /* 缓冲 */
struct mutex unmap_mutex; /* 保护未映射页的 mutux 锁 */
} __attribute__((aligned(sizeof(long))));
一个具体的文件在打开后, 内核会在内存中为之建立一个struct inode结构,其中的i_mapping域指向一个address_space结构。这样,一个文件就对应一个address_space结构。
(2)操作
找数据
page = find_get_page(mapping, index);
(2)基树
需要高效地检查页是否在页高速缓存中。
每个address_space对象都有唯一的基树,它保存在page_tree结构体中。
(3)关系
1)一个inode节点对象对应一个address_space对象。其中inode节点对象的i_mapping和i_data字段指向相应的 address_space对象,而address_space对象的host字段指向对应的inode节点对象。
2)每个address_space对象对应一颗基树。他们之间的联系是通过address_space对象中的page_tree字段指向该address_space对象对应的基树。
3)一般情况下一个inode节点对象对应的文件或者是块设备都会包含多个页面的内容,所以一个inode对象对应多个page描述符。同一个文件拥有的所有page描述符都可以再该文件对应的基树中找到。
3 缓冲区高速缓存
缓冲——buffer——对应磁盘块
缓存——cache——按页面管理
4 flusher线程
在以下3种情况发生时,一群flusher进行回写:
1)当空闲内存低于一个阀值时
2)当脏页在内存中驻留时间超过一个阀值时
3.)当用户进程调用 sync() 和 fsync() 系统调用时
(1)2.6以前
bdflush:一个bdflush线程,在内存过低或缓冲数较大时,将脏缓冲写回磁盘。
kupdated:周期性地写回脏页。
(2)pdflush
线程数目是动态的
(3)flusher
线程的数目不唯一
基于页面,将脏页写回磁盘
每个 flusher 线程对应一个磁盘
参考:
Linux 2.6 中的页面回收与反向映射
linux文件系统的页高速缓存page cache中的核心数据结构address_space