《基于Linux的C编程与内核导读》连载(17)
时间:2007-06-13 来源:gaowp
4.1.4 linux的页面交换机制
linux使用LRU算法作为其页面交换的核心算法,出于安全性、稳定性、执行效率等多方面的考虑,linux所使用的LRU交换算法已经交织在其进程管理、文件系统管理等其他机制当中,他们有机的结合为一个整体。
在操作系统原理中介绍缺页处理是这样的:当进程在执行的过程中,发现某个页面不在内存中,产生缺页中断,并在系统中淘汰一个页面,然后把磁盘上的此页面调入内存。这种完全消极的页面交换策略有个缺点:换入换出页面总是在处理器忙碌的时候发生,这将使系统效率降低。Linux的交换策略是定期的,特别是在系统相对空闲的时候,挑选一些页面预先交换出来而腾出一些内存空间,从而使系统始终维持在一定的空闲页面供应量。至于挑选的原则,一般都是LRU,即“最久未用到”的页面。
1、页面汰策略的改进
以上的交换策略依然有可能发生系统“抖动”。为了防止“抖动”的发生,linux将页面的换出和内存页面的释放分成两步来做。当系统挑选若干内存页面准备换出时,将这些页面的内容写到相应当磁盘中,并且将相应的页面表项内容设置为指向磁盘页面。
页面表项的数据结构pte_t是一个32位整型变量,其中第一位P用于表示该页面是否在内存中:P位为1表示在内存中;为0,则表示不在内存中。这里需要将换出页面的P位置0,但是所占据的内存页面并不立即释放,而是将其page结构留在一个cache队列当中,并使其从“活跃状态”转为“不活跃状态”,至于最后释放内存页面的操作就推迟到以后有条件时进行。
这样如果在一个页面被换出后立即又被访问而发生缺页中断时,就可以从物理页面的cache队列中找到相应的页面,并为之建立映射。反之,如果经过一段时间后,一个不活跃的内存页面还是没有被访问,那就到了最后释放的时候了。
2、修改过和没有修改过的页面
如前面所述,将“换出”和“释放”分成两步走显然可以减少系统在页面交换上的开销,然而作为产品,Linux的交换策略还可以进一步提升。
首先,在准备换出一个页面时并不一定要把它的内容写入磁盘。如果自从最近一次换入该页面以后就没有写过这个页面,那么这个页面的内容与磁盘上相应的内容是一样的,可以把这个内存页面看成“干净”的。这样的页面显然不需要写出去了。
其次,就是“脏”页面。和“干净”页面相对应,“脏”页面就是内存页面发生改变的页面。这样的页面不需要立刻就写出去,而可以先将其从页面映射表中断开。经过一段时间,对应页面不再使用后,再写出去。这样“脏”页面就成了“干净”页面了。“干净”页面可以在cache队列中待到真的有必要回收的时候再释放,回收一个“干净”页面的花费是很小的。
3、综述
综上所述,Linux的页面交换机制如图4-5所示。
(1)空闲页面。页面的page数据结构通过其队列头结构list链入某个页面管理区(ZONE)的空闲队列free_area中。页面的使用计数count为0。
(2)分配。通过函数__alloc_pages( )或__get_free_page( )从某个空闲队列中分配内存页面,并将所分配页面的计数count置为1,其page数据结构的队列头list结构则变为空闲。
(3)活跃状态。页面的page数据结构通过其队列头结构lru链入活跃页面队列active_list,并且至少有一个用户空间进程的表项指向该页面。每当页面建立或恢复映射时,都使用计数count加1。
(4)不活跃且修改过的页面。页面的page数据结构通过其队列头结构lru链入不活跃且修改页面队列inactive_dirty_list,但是原则上不再有任何进程的页面表项指向该页面。此外,每当断开页面的映射时,都使count减1。
(5)释放和回收。将不活跃且修改过的页面的内容写入交换设备,并将页面的page数据结构从不活跃且修改页面队列inactive_dirty_list转移到某个不活跃且干净的页面队列中。
(6)不活跃且干净页面。页面的page数据结构通过其队列头结构lru链入某个不活跃且干净的页面队列,每个页面管理区ZONE都有一个这种队列inactive_clean_list。
(7)换入。如果在转入不活跃状态之后的某个时间受到访问,则由不活跃状态转入活跃状态。
(8)回收。有页面请求时,就从没有修改的页面队列中回收页面。