一、页面置换算法简介
操作系统将内存按照页的进行管理,在需要的时候才把进程相应的部分调入内存。当产生缺页中断时,需要选择一个页面写入。如果要换出的页面在内存中被修改过,变成了“脏”页面,那就需要先写会到磁盘。页面置换算法,就是要选出最合适的一个页面,使得置换的效率最高。页面置换算法有很多,简单介绍几个。
二、n种算法介绍
2.1 Optimal算法(最优算法)
首先介绍最优算法,它需要知道以后要被用到的页,然后将不会被用到的页换出内存;如果所有页都会被用到,就把需要使用时间离现在最长的页换出,以尽量使不好的情况晚发生。这种方法能使系统获得最佳性能,但是,它是不可能实现的…因为当前无法获知以后哪些页要被用到。不过最优算法还是能够作为其他算法优秀程度的衡量。
例:假定系统为某进程分配了三个物理块,并考虑有以下的页面号引用串:
7,0,1,2,0,3,0,4,2,3,0,3,2,1,2,0,1,7,0,1
进程运行时,先将 7,0,1 三个页面装入内存。以后,当进程要访问页面 2 时,将会产生缺页中断。此时 OS 根据最佳置换算法,将选择页面 7 予以淘汰。这是因为页面 0 将作为第 5 个被访问的页面,页面 1 是第 14 个被访问的页面,而页面 7 则要在第 18 次页面访问时才需调入。下次访问页面 0 时,因它已在内存而不必产生缺页中断。当进程访问页面 3时,又将引起页面 1 被淘汰;因为,它在现有的 1,2,0 三个页面中,将是以后最晚才被访问的。图 4-26 示出了采用最佳置换算法时的置换图。由图可看出,采用最佳置换算法发生了 6 次页面置换。
2.2 FIFO(First-In First-Out,先进先出)算法
FIFO算法的思想很简单,就是置换出当前已经待在内存里时间最长的那个页。FIFO算法的运行速度很快,不需要考虑其他的因素,需要的开销很少。但是正是由于没有考虑页面的重要性的问题,FIFO算法很容易将重要的页换出内存。
例:同上。当进程第一次访问页面 2 时,将把第 7 页换出,因为它是最先被调入内存的;在第一次访问页面 3 时,又将把第 0 页换出, 因为它在现有的 2, 0, 1 三个页面中是最老的页。 由图 4-27 可以看出,利用 FIFO 算法时进行了 12 次页面置换,比最佳置换算法正好多一倍。
2.3 Second Chance(第二次机会)算法
为了避免FIFO算法将重要的页换出内存,Second Chance算法提供了一些改进。Second Chance算法在将页面换出内存前检查其使用位(使用位前文有介绍),如果其使用位为1,证明此页最近有被使用,猜测它还可能被使用,于是不把它置换出内存,但是把其使用位置为0,随后检查下一个页面,直到发现某页的使用位为0,将此页置换出内存。
2.4 Clock算法(时钟轮转法)
为了节约Second Chance算法一个接着一个检查使用位的开销,时钟轮转法又提出了改进。时钟轮转法将所有的页组成一个圆,圆心的指针指向下一个要被置换的页面,置换前同样检查使用位,如果使用位为1,同样将其使用位置为0,随后将顺指针旋转,检查下一个页面,直到发现某页的使用位为0,将此页置换出内存。很容易理解此算法为什么叫“时钟”轮转法。
图示:
此时2号页是下一个要被置换出内存的页,置换时如果发现其使用位为1,则将使用位置0后顺时针旋转指针检查1号页。
2.5 LRU(Least Recent Used, 最近最少使用)算法
为获得对最优算法的模拟,提出了LRU算法。由于当前时间之后需要用到哪些页无法提前获知,于是记录当前时间之前页面的使用情况,认为之前使用过的页面以后还会被用到。在置换时,将最近使用最少的页面换出内存。此种方法的开销比较大。
例:同上。当进程第一次对页面 2 进行访问时,由于页面 7 是最近最久未被访问的,故将它置换出去。当进程第一次对页面 3进行访问时,第 1 页成为最近最久未使用的页,将它换出。由图可以看出,前 5 个时间的图像与最佳置换算法时的相同,但这并非是必然的结果。因为,最佳置换算法是从“向后看”的观点出发的,即它是依据以后各页的使用情况;而 LRU 算法则“向前看”的,即根据各页以前的使用情况来判断,而页面过去和未来的走向之间并无必然的联系。
2.6 Linux使用的页面置换算法
Linux区分四种不同的页面:不可回收的、可交换的、可同步的、可丢弃的。
不可回收的:保留的和锁定在内存中的页面,以及内核态栈等。
可交换的:必须在回收之前写回到交换区或者分页磁盘分区。
可同步的:若为脏页面,必须要先写回。
可丢弃的:可以被立即回收的页面。
Linux并没有像我们之前单纯讨论算法时那样,在缺页中断产生的时候才进行页面回收。Linux有一个守护进程kswapd,比较每个内存区域的高低水位来检测是否有足够的空闲页面来使用。每次运行时,仅有一个确定数量的页面被回收。这个阈值是受限的,以控制I/O压力。
每次执行回收,先回收容易的,再处理难的。回收的页面会加入到空闲链表中。
算法是一种改进地LRU算法,维护两组标记:活动/非活动和是否被引用。第一轮扫描清除引用位,如果第二轮运行确定被引用,就提升到一个不太可能回收的状态,否则将该页面移动到一个更可能被回收的状态。
处于非活动列表的页面,自从上次检查未被引用过,因而是移除的最佳选择。被引用但不活跃的页面同样会被考虑回收,是因为一些页面是守护进程访问的,可能很长时间不再使用。
另外,内存管理还有一个守护进程pdflush,会定期醒来,写回脏页面;或者可用内存下降到一定水平后被内核唤醒。
2.7 NRU(Not Recent Used, 最近未使用)算法
前面提到修改位和使用位,NRU算法利用这两个标志位将所有页帧分为4组:
第0组:修改位和使用位都为0;
第1组:修改位为0,使用位为1;
第2组:修改位为1,使用位为0;
第3组:修改位和使用位都为1。
NRU算法从组数最小的一组中随机选择一个页面将其移出内存。可能有人会发现第2组这种情况根本不会出现,如果一个页帧被修改,其修改位会被置1,同时它也被使用了,其使用位也会被置1;即不会出现被修改但是没有被使用的情况。真实情况是,页帧的使用位会被定时清零,这样第3组经过一次清零就会变成第2组。这也符合“最近”未使用,即很久以前被使用的页帧被清零了,不在统计范围内,只要“最近”没有被使用,就很有可能被移出。
NRU算法不是最好的,但是它使用起来开销很小,用较小的代价就得到了不错的效果,不失为一种不错的算法。
三、代码实现
//TODO