第16章:页高速缓存和页回写
磁盘访问ms级别,内存访问ns级别,使用数据务必将数据拷贝到内存中,这里是将一些磁盘数据映射到内存中提高命中率。
对应于磁盘的物理块(映射关系),可以占用空闲内存扩张,也可以收缩(换出)。read操作(读取一段数据,首先在缓存中查找,没有的话才调用I/O去安排,读取就直接读取到缓存中,一段时间没有人用就可能被换出)。
写操作:linux中是回写操作,写操作直接安排缓存数据,并将该页标志为脏。按照一定规则由回写进程周期性地将脏页回写于硬盘(统一I/O比断断续续调用I/O效率高)。
缓存回收:只能回收干净页面,如果没有足够的,就会强制调用回写页面操作来产生消除脏页。问题在于回收什么页(未来最不可能用到的页),这种回收策略很重要。
LRU:最近最少使用,建立按照访问时间为序的LRU页链表,回收从该链表的头部开始。不适用于许多文件被访问一次便不再访问的情况(因为这些文件的页面将会有很长时间才能够移动到页头部位置,导致占用空间)。
双链策略:维护活跃链表和非活跃链表,系统需要保证两个链表的大小平衡,非活跃链表将会作为移出的集合,这样只能访问一次的页面将会更快地移到非活跃链表中,所以这种方法能够解决LRU的缺点。当然也可以安排n个链表,作为活跃优先级分,进一步地提高效率。
意义:系统载入以及编译过程,需要打开相当大的文件,尽可能地减少I/O 操作可以提高效率。
Linux页高速缓存:来自对正规文件,块设备文件和内存映射文件的读写。
address_space对象表示实际的物理地址,对等于vm_area_struct结构体(虚拟地址),一个(一些)被缓存的文件只和一个address_space结构体关联,但它可以映射多个vm_area_struct,也就是说address_space和vm也是一对多的关系。
struct address_space{ //可以看出这是以个文件集,包含了这个文件集所有的页的情况
i_mmap; //优先搜索树,所有共享的与私有的映射页面,用以高效地找到关联的被缓存文件。
nrpages; //页总数
struct inode host; //拥有节点(文件个数)
struct radix_tree_root page_tree; //每个address_space维护一个基树,亦包含本域中全部页面的radix树,这个树的学习暂时保留,特征:给1文件偏移值可以快速地查找到页。
...
}
操作:address_space_operations操作函数指针集,每个后备存储(即硬盘等存储实体),需要自己写适配自己的该操作表。
读写操作readpage()和writepage()最为重要;
读页面步骤:find_get_page(address_space对象,偏移量 )检查页缓冲是否有->没有找到就申请一个新页面(再通过I/O将数据读到这个页面里面)。
写页面步骤:与读大致相同,只是直接在内存中写,按照一定策略回写到硬盘。
写的线程:(触发写操作情况:1.空闲内存低于某一个阈值 2.脏页驻留时间过长,需要周期性同步 3.显式地调用回写(sync()和fsync()系统调用));
flusher线程:拥有一群线程,或于低于阈值时按紧张程度唤醒一批,或周期性(需设置周期性参数)唤醒写回。
膝上型(低功耗要求):在电池供电模式时,flusher线程采取机制(找准时机,比如在当前执行写盘操作的时候,就顺手把脏页安排了),设计思想就是尽可能地减少调用磁盘以节约电。