页面置换算法
功能与目标
功能:当缺页中断发生,需要调入新的页面,但是此时内存已满,需要选择内存中哪个物理页面被置换
目标:尽可能的减少页面的换进换出次数(即缺页中断的次数)。具体来说,把未来不再使用的或短期内较少使用的页面换出,通常只能在局部性原理指导下依据过去的统计数据来进行预测
页面锁定(frame locking):用于描述必须常驻内存的操作系统的关键部分或时间关键(time-critical)的应用进程。实现的方法:在页表中添加锁定标志位(lock bit)
实验设置与评价方法
记录一个进程对页访问的一个轨迹
举例:(虚拟)地址跟踪 (页号,位移)
(3,0), (1,9), (4,1), (2,1), (5,3), (2,0), (1,9), (2,4), (3,1), (4,8)
第三个页面的第0个offset
生成页面轨迹
3, 1, 4, 2, 5, 2, 1, 2, 3, 4(替换如c ,a, d, b, e, b, a, b, c, d)
模拟一个页面置换的行为并且记录产生页缺失数的数量
更少的缺失,更好的性能
局部页面置换算法
最优页面置换算法(OPT,optimal)
基本思路:当一个缺页中断产生时,对于保存在内存中的每一个逻辑页面,计算在它的下一次访问之前,还需等待多长时间,从中选择等待时间最长的那个,作为被置换的页面
这只是一种理想情况,在实际系统中是无法实现的,因为操作系统无从知道每一个页面要等待多长时间以后才会再次被访问
可用作其他算法的性能评价的依据(在一个模拟器上运行某个程序,并记录每一次的页面访问情况,在第二遍运行时即可使用最优算法)
Time:时间轴(1,2,3,4,5,6)
Requests:访问页(a,b,c,d,e)
比如上图,在1时刻访问的是c页,在2时刻访问的是a页
Page Frames(页帧):当前操作系统给该程序分配了4个物理页帧
虽然总共分配了4个物理页帧,但是需要访问的页有5个,这种情况就会产生物理页不够的情况,就会产生页替换
在0时刻的时候,物理页帧已经存了a,b,c,d四个逻辑页,在1,2,3,4这个四个时刻,访问c,a,d,b时,此时物理页中已经存放了a,b,c,d这四个逻辑页,所以在这四个时刻的访问不会产生缺页中断
在5时刻,需要访问e逻辑页,此时物理帧中没有,产生缺页中断,然后根据最优置换算法,置换的页面是在将来最长时间不需要的页面,a页面在第7个时刻需要访问,b在第6个时刻需要访问,c在第7个时刻需要访问,d在第10个时刻需要访问,所以d页是将来最长时间不需要访问的页面,把d给置换掉,把e页填入
先进先出算法(First-In First-Out, FIFO)
基础思路:选择在内存中驻留时间最长的页面并淘汰。具体来说,系统维护着一个链表,记录了所有位于内存当中的逻辑页面。从链表的排列顺序来看,链首页面的驻留时间最长,链尾页面的驻留时间最短。当发生一个缺页中断时,把链首页面淘汰出局,并把新的页面添加到链表的末尾。
性能较差,调出的页面有可能是经常要访问的页面。并且有Belady现象(分配的物理页帧越多,产生缺页中断次数越多(正常情况应该越少才对))。FIFO算法很少单独使用
最近最久未使用算法(LRU,Least recently used)
基本思路:当一个缺页中断发生时,选择最久未使用的那个页面,并淘汰
它是对最优页面置换算法的一个近似,其依据是程序的局部性原理,即在最近一小段时间(最近几条指令)内,如果某些页面被频繁的访问,那么在将来的一小段时间内,它们还可能会再一次被频繁的访问。反过来说,如果在过去某些页面长时间未被访问,那么在将来它们还可能会长时间地得不到访问
LRU算法需要记录各个页面使用时间的先后顺序,开销较大,两种可能的实现方法:
1.系统维护一个页面链表,最近刚刚使用过的页面作为首节点,最久未使用的页面作为尾节点。每一次访问内存时,找到相应的页面,把它从链表中摘下来,再移动到链表之首。每次缺页中断发生时,淘汰链表末尾的页面
2.设置一个活动页面栈,当访问某页时,将此页压入栈顶,然后考察栈内是否有与此页面相同的页号,若有则抽出。当需要淘汰一个页面时,总是选择栈底的页面,它就是最久未使用的
时钟页面置换算法(Clock)
LRU的近似,对FIFO的一种改进
基本思路:
1.需要用到页表项当中的访问位,当一个页面被装入内存时,把该位初始化为0。然后如果这个页面被访问(读/写),则把该位置为1
2.把各个页面组织成环形链表(类似钟表面),把指针指向最老的页面(最先进来)
3.当发生一个缺页中断时,考察指针所指向的最老页面,若它的访问位为0,立即淘汰;若访问位为1,则把该位置为0,然后指针下移一格。如此下去,直到找到被淘汰的页面,然后把指针移动到它的下一格
维持一个环形页面链表保存在内存中
1.用一个时钟(或者使用/引用)位来标记一个页面是否经常被访问
2.当一个页面被引用的时候,这个位被置为1
时钟头扫遍页面寻找一个带有used bit=0
1.替换在一个周转内没有被引用过的页面
如图,总共有5个物理页帧,有8个虚拟页,目前7,4,9,3,1这五个虚拟页是放到物理内存中的
最左边的一位是代表是否存在于物理内存中,如果置1表示存在于物理内存中,虚拟页和物理页映射关系是正常的
第二位是代表该页是否被访问过,如果置为1表示该页被访问过
第三位是代表该虚拟页对应的物理页号
如果发生了缺页中断,需要调度新的页面进来,但是物理页面只有5个,所以需要置换,从当前指针指向的页目录项开始寻找,当前指针指向的是逻辑页为0的页面,首先查看它的used bit,发现它为1,代表它最近被访问过,此时需要先把该页的Used bit清零,然后指针顺时针向下挪一格,然后依次往复,直到指针移到虚拟页1,它的used bit为0,该页就会被替换出去,然后指针再顺时针向下挪一格
如果没有发生缺页中断,那指针位置不动,只是把当前访问的页的used bit置为1
这里有一个巨大的代价来替换“脏”页
修改clock算法,使它允许脏页总是在一次时钟头扫描中保留下来
1.同时使用脏位和使用位来指导置换(dirty bit脏位表示写位,只有该页被写的时候才会置1,如果该页仅是被读,该页不会被置1,但是不管读写,used bit都会被置1)
二次机会法
如果某一页被从硬盘置换到内存中之后,程序只是对该页进行读操作,并没有写操作,那此时内存中的页和硬盘上的内容是相同的,那在发生置换的时候就不需要再把该页写回到硬盘中,直接把该页释放即可,因为内容是一样的
如果某一页被程序写过,那内存中的数据和硬盘中的数据不一致,如果发生置换时,必须把内存中的数据写回到硬盘中
如果可以通过dirty bit来判断该页是否被写过,来减少写硬盘的次数
置换原则:
1.如果dirty bit和used bit都为0,那么就会被立即替换出去
2.如果used bit为0,dirty bit为1,则把dirty bit置为0,同时把指针顺时针下移一格
3.如果used bit为1,dirty bit为0,则把used bit置为0,同时把指针顺时针下移一格
4.如果used bit为1,dirty bit也为1,则把used bit置为0,dirty bit不变,同时把指针顺时针下移一格
最不常用算法(LFU,Least Frequently Uesed)
基本思路:当一个缺页中断发生时,选择访问次数最少的那个页面,并淘汰
实现方法:对每个页面设置一个访问计数器,每当一个页面被访问时,该页面的访问计数器加1。在发生缺页中断时,淘汰计数值最小的那个页面
LRU和LFU的区别:LRU考察的是多久未访问,时间越短越好;而LFU考察的是访问的次数或频度,访问次数越多越好
问题:一个页面在进程开始时使用得很多,但以后就不使用了。实现也费时费力
解决方法:定期把次数寄存器右移一位
Belady现象
在采用FIFO算法时,有时会出现分配的物理页面数增加,缺页率反而提高的异常现象
Belady现象的原因:FIFO算法的置换特征与进程访问内存的动态特征是矛盾的,与置换算法的目标是不一致的(即替换较少使用的页面),因此,被它置换出去的页面并不一定是进程不会访问的
FIFO算法:
LRU算法:
LRU算法满足栈算法的特点,所以物理页越多缺页越少;FIFO算法不满足栈算法的特点,所以会产生Belady现象
LRU、FIFO和Clock的比较
LRU算法和FIFO本质上都是先进先出的思路,只不过LRU是针对页面的最近访问时间来进行排序,所以需要在每一次页面访问的时候动态的调整各个页面之间的先后顺序(有一个页面的最近访问时间变了);而FIFO是针对页面进入内存的时间来进行排序,这个时间是固定不变的,所以个页面之间的先后顺序是固定的。如果一个页面在进入内存后没有被访问,那么它的最近访问时候就是它进入内存的实际。换句话说,如果内存当中的所有页面都未曾访问过,那么LRU算法就退化为FIFO算法。例如:给进程分配3个物理页面,逻辑页面的访问顺序为1、2、3、4、5、6、1、2、3...
LRU算法性能较好,但系统开销较大;FIFO算法系统开销较小,但可能会发生Belady现象。因此,折中的方法就是Clock算法,在每一次页面访问时,它不必去动态地调整该页面在链表当中的顺序,而仅仅是一个标记,然后等到发生缺页中断的时候,再把它移动到链表末尾。对于内存当中那些未被访问的页面,clock算法的表现和LRU算法一样好;而对于那些曾经被访问过的页面,他不能像LRU算法那样,记住它们的准确位置。
全局页面置换算法
局部页面置换算法的问题
工作集模型
前面介绍的各种页面置换算法,都是基于一个前提,即程序的局部性原理。但是此原理是否成立?
1.如果局部性原理不成立,那么各种页面置换算法就没有什么分别,也没有什么意义。例如:假设进程对逻辑页面的访问顺序是1、2、3、4、5、6、7、8、9...,即单调递增,那么在物理页面数有限的前提下,不管采用何种置换算法,每次的页面访问都必然导致缺页中断。
2.如果局部性原理是成立的,那么如何来证明它的存在,如何来对它进行行定量地分析?这就是工作集模型!
工作集:一个进程当前正在使用的逻辑页面集合
可以用一个二元函数W(t, Δ)来表示:
1.t是当前的执行时刻
2.Δ称为工作集窗口(working-set window),即一个定长的页面访问的时间窗口
3.W(t, Δ)=在当前时刻t之前的Δ时间窗口当中的所有页面所组成的集合(随着t的变化,该集合也在不断的变化)
4.|W(t, Δ)| 指工作集的大小,即页面数目
工作集大小的变化:进程开始执行后,随着访问新页面逐步建立较稳定的工作集。当内存访问的局部性区域的位置大致稳定时,工作集大小也大致稳定;局部性区域的位置改变时,工作集快速扩张和收缩过渡到下一个稳定值
常驻集
常驻集是指在当前时刻,进程实际驻留在内存当中的页面集合
1.工作集是进程在运行过程中固有的性质,而常驻集取决于系统分配给进程的物理页面数目,以及所采用的的页面置换算法
2.如果一个进程的整个工作集都在内存当中,即常驻集⊇工作集,那么进程将很顺利地运行,而不会造成太多的缺页中断(直到工作集发生剧烈变动,从而过渡到另一个状态)
3.当进程常驻集的大小达到某个数目之后,再给它分配更多的物理页面,缺页率也不会明显下降
工作集页面置换算法
基本思想:随着程序的执行,如果某个逻辑页不属于该程序的工作集窗口了,就会被丢掉
追踪之前Δ个的引用
在之前Δ个内存访问的页引用是工作集,Δ被称为窗口大小
Example: Δ = 4 references
总共分配5个物理页帧,在1时刻之前,a(t=0),d(t=-1),e(t=-2)已经在物理内存中了,进入的先后顺序为e,d,a,在1时刻需要c页,此时物理内存中没有,产生缺页中断,然后把c装入物理内存中
在第二个时刻没有发生缺页中断,但是由于e页是在1时刻之前3个时刻进入物理内存的,并且时间窗口大小为4,所以e页就不在该时间窗口内,就需要被置换出去,如果该页只被读过,那就直接丢弃,如果该页被写过,那还需要写回硬盘
在第三个时刻,d页被访问,d页的时间被刷新成3,目前所有在物理内存中的逻辑页都在工作集中,不需要做什么啊哦做
在第四个时刻,b也被访问,产生缺页中断,同时由于a是t=0时刻的,不在工作集内,需要被置换出去
缺页率置换算法
可变分配策略:常驻集大小可变。例如:每个进程在刚开始运行的时候,先根据程序大小给它分配一定数目的物理页面,然后在进程运行过程中,再动态地调整常驻集的大小。
1.可采用全局页面置换的方式,当发生一个缺页中断时,被置换的页面可以是在其它进程当中,各个并发进程竞争地使用物理页面
2.优缺点:性能较好,但增加了系统开销
3.具体实现:可以使用缺页率算法(PFF,page fault frequency)来动态调整常驻集的大小
缺页率
缺页率表示“缺页次数/内存访问次数”(比率)或“缺页的平均时间间隔的倒数”。影响缺页率的因素:
1.页面置换算法
2.分配给进程的物理页面数目
3.页面本身的大小
4.程序的编写方法(是否具有局部性)
如果运行的程序的缺页率过高,则通过增加工作集来分配更多的物理页面;若运行的程序的缺页率过低,则通过减少工作集来减少它的物理页面数。力图使运行的每个程序的缺页率保持在一个合理的范围内
一个交替的工作集计算明确的试图最小化页缺失
1.当缺页率高的时候-增加工作集
2.当缺页率低的时候-减少工作集
算法:
保持追踪页缺失发生概率
1.当缺失发生时,从上次页缺失起计算这个时间间隔并记录该时间,tlast是上次的页缺失的时间
2.如果发生页缺失之间的间隔是“大”的,之后就需要减少工作集
如果tcurrent - tlast > T,之后从内存中移除所有在[tlast, tcurrent]时间内没有被引用的页
3.如果发生页缺失的时间是“小”的,之后就需要增加工作集
如果tcurrent - tlast =< T,之后增加缺失页到工作集中
举例:window size = 2
如果tcurrent - tlast > 2,从工作集中移除没有在[tlast, tcurrent]时间内被引用的页面
如果tcurrent - tlast =< 2,仅增加缺失页到工作集中
抖动问题(thrashing)
如果分配给一个进程的物理页面太少,不能包含整个的工作集,即常驻集⊂工作集,那么进程将会造成很多的缺页中断,需要频繁的在内存和外存之间替换页面,从而使进程的运行速度变得很慢,我们把这种状态称为“抖动”
产生抖动的原因:随着驻留内存的进程数目增加,分配给每个进程的物理页面数不断减小,缺页率不断上升。所以OS要选择一个适当的进程数目和进程需要的物理帧数,以便在并发水平和缺页率之间达到一个平衡
抖动问题可能会被本地的页面置换改善
更好的规则为加载控制:调整MPL,所以:Better cirteria for load control: Adjust MPL so that:
1.平均页缺失时间mean time between page faults(MTBF)= 页缺失服务时间 page fault service time(PFST)
2.∑WSi = 内存的大小