操作系统学习笔记——页面置换算法,Belady现象,抖动

功能:当缺页中断发生,需要调入新的页面而内存已满时,选择内存当中哪个物理页面被置换。

目标:尽可能地减少页面的换进换出次数(即缺页中断的次数)。把未来不再使用的或短期内较少使用的页面换出。通常只能在局部性原理指导下依据过去的统计数据来进行预测

 

局部页面置换算法:

最优页面置换算法(OPT),先进先出算法(FIFO),最近最久未使用算法(LRU),时钟页面置换算法(Clock),最不常用算法(LFU),二次机会法

全局页面置换算法:

工作集模型,工作集页置换算法,缺页率置换算法

 

最优页面置换算法

基本思路:当一个缺页中断发生时,对于保存在内存当中的每一个逻辑页面,计算在它的下一次访问之前,还需等待多长时间,从中选择等待时间最长的那个,作为被置换的页面。

只是理想情况,在实际系统中无法实现。可用作其他算法性能的评价依据

 

先进先出算法

基本思路:选择在内存中驻留时间最长的页面并淘汰之。具体来说,系统维护一个链表,记录了所有位于内存当中的逻辑页面。从链表的排列顺序来看,链首页面的驻留时间最长。链尾页面的驻留时间最短。当发生一个缺页中断时,把链首页面淘汰出局,并把新的页面添加到链表的末尾。

性能较差,调出的页面可能是经常要访问的页面,并且有Belady现象。很少单独使用这个算法

 

最近最久未使用算法算法

基本思路:当一个缺页中断发生时,选择最久未使用的那个页面,并淘汰之。

他是对最优页面置换算法的一个近似,其依据是程序的局部性原理,即在最近一小段时间(最近几条指令)内,如果某些页面被频繁的访问,那么在将来的一小段时间内,他们还可能会再一次被频繁的访问。反过来说,如果在过去某些页面长时间未被访问,那么在将来他们还可能会长时间得不到访问。

LRU算法需要记录各个页面使用时间的先后顺序,开销比较大。两种可能的实现方法:

1.系统维护一个页面链表,最近刚刚使用过的页面作为首结点,最久未使用的页面作为尾结点。每一次访问内存时,找到相应的页面,把它从链表中摘下来,再移动到链表的首。每次缺页中断时,淘汰链表末尾的页面。

2.设置一个页面活动栈,当访问某页时,将此页号压入栈顶,然后,考察栈内是否有与此页面相同的页号,若有则抽出。当需要淘汰一个页面时,总是选择栈底的页面,他就是最久未使用的。

 

时钟页面置换算法

LRU的近似,对FIFO的一种改进;

基本思路:需要用到页表项当中的访问位,当一个页面被装入内存时,把该位初始化为0.然后如果这个页面被访问(读/写),则把该位置置为1;把各个页面组织成环形链表(类似钟表面),把指针指向最老的页面(最先进来);当发生一个缺页中断时,考察指针所指向的最老页面。若他的访问位为0,立即淘汰;若访问位为1,则把该位置为0,然后指针往下移动一个。如果此下去,直到找到被淘汰的页面,然后把指针移动到它的下一格

func Clock_Replacement
begin
while(victim page not found) do
    if(used bit for current page = 0) then
        replace current page (&set used bit to 1)
    else
        reset used bit(to 0)
    end if
    advance clock pointer
end while
end Clock_Replacement

 

二次机会法

修改Clock算法,使他允许脏页总是在一次时钟头扫描中保留下来。同时使用脏页(dirty bit)和使用位(used bit)来指导置换

used   dirtyused   dirty
0         0替换
0        10        0
1        00        0
1       10        1

当变成 0/0  时才替换

 

最不常用法

不是说这个方法不常用,而是指替换的是不常用的页面。

基本思路:当一个缺页中断发生时,选择访问次数最少的那个页面,并淘汰之。

实现方法:对每个页面设置一个访问计数器,每当一个页面被访问时,该页面的访问计数器加1.在发生缺页中断时,淘汰计数值最小的那个页面。

LRU和LFU的区别:LRU考察的是多久没访问,时间越短越好。而LFU考察的是访问的次数或频度,访问次数越多越好。

问题:一个页面在进程开始时使用的很多,但以后就不使用了。解决方法:定期把次数寄存器右移一位

 

Belady现象:在采用FIFO算法时,有时会出现分配的物理页面数增加,缺页率反而提高的异常现象。

原因:FIFO算法的置换特征与进程访问内存的动态特征是矛盾的,与置换算法的目标是不一致的(即替换较少使用的页面),因此,被它置换出去的页面并不一定是进程不会访问的。

 

LRU,FIFO,CLOCK的比较:

LRU算法和FIFO本质上都是先进先出的思路,只不过LRU是针对页面的最近访问时间来进行排序,所以需要在每一次页面访问的时候动态的调整各个页面之间的先后顺序(有一个页面的最近访问时间变了);而FIFO是针对页面进入内存的时间来进行排序,这个时间是固定不变的,所以各页面之间的先后顺序是固定的。如果一个页面在进入内存后没有被访问,那么它的最近访问时间就是它进入内存的时间。换句话说,如果内存当中的所有页面都未曾访问过,那么LRU算法就退化为FIFO算法。

LRU算法性能较好,但系统开销大;FIFO算法系统开销小,但可能会发生belady现象。因此,折中的办法是Clock算法,在每一次页面访问时,它不必去动态的调整该页面在链表当中的顺序,而仅仅是做一个标记,然后等到发生缺页中断的时候,再把它移动到链表末尾。对于内存当中那些未被访问的页面,Clock算法表现的和LRU算法一样好,而对于那些曾经被访问过的页面,他不能像LRU算法那样,记住他们的准确位置。

 

 

工作集:一个进程当前正在使用的逻辑页面集合

常驻集:指在当前时刻,进程实际驻留在内存当中的页面集合。

工作集页面置换算法:追踪之前T个的引用,在之前T个内存访问的页引用是工作集,T被称为窗口大小

缺页率页面置换算法

可变分配策略:常驻集大小可变。可采用全局页面置换的方式,当发生一个缺页中断时,被置换的页面可以是在其他进程当中,各个并发进程竞争的使用物理页面。

优缺点:性能较好,但增加了系统开销。

具体实现:可以使用缺页率算法来动态调整常驻集的大小。

缺页率:缺页次数/内存访问次数。影响缺页率的因素:页面置换算法,分配给进程的物理页面数目,页面本身的大小,程序的编写方法

一个交替的工作集计算明确的试图最小化页缺失:当缺页率高的时候——增加工作集,当缺页率低的时候——减少工作集

算法:

保持追踪缺失发生概率

       当缺失发生时,从上次页缺失起计算这个时间,tlast 是上次的页缺失的时间

       如果发生页缺失之间的时间是“大”的,之后减少工作集。

      如果tcurrent - tlast > T,之后从内存中移除所有在[tlast, tcurrent]时间内没有被引用的页。

      如果这个发生页缺失的时间是“小”的,之后增加工作集。

      如果 tcurrent - tlast <= T,之后增加缺失页到工作集中

 

 

抖动问题

如果分配给一个进程的物理页面太少,不能包含整个的工作集,即常驻集包含于工作集,那么进程将会造成很多的缺页中断,需要频繁地在内存与外存之间替换页面,从而使进程的运行速度变得很慢,把这种状态称为“抖动”。

产生抖动的原因:随着驻留内存的进程数目增加,分配给每个进程的物理页面数不断减小,缺页率不断上升。所以OS要选择一个适当的进程数目和进程需要的帧数,以便在并发水平和缺页率之间达到一个平衡。

 

抖动问题可能会被本地的页面置换改善。

  • 13
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
计算机操作系统页面置换算法是指在虚拟内存管理中,当物理内存不足时,需要将某些页面从内存中置换出去,以便为新的页面腾出空间。常见的页面置换算法有以下几种: 1. 最优页面置换算法(OPT):选择在未来最长时间内不再被访问的页面进行置换,但是由于需要预测未来的访问情况,因此实际应用较少。 2. 先进先出页面置换算法(FIFO):选择最早进入内存的页面进行置换,但是该算法可能会出现Belady异常,即增加物理内存可能会导致缺页次数增加。 3. 最近最久未使用页面置换算法(LRU):选择最近最久未使用的页面进行置换,该算法相对于FIFO算法能够更好地反映页面的使用情况,但是实现较为复杂。 4. 时钟页面置换算法(Clock):将页面组织成一个环形链表,每个页面有一个访问位,当页面被访问时,访问位被设置为1,当需要置换页面时,选择访问位为0的页面进行置换。 5. 最不经常使用页面置换算法(LFU):选择最不经常使用的页面进行置换,该算法需要记录每个页面被访问的次数,实现较为复杂。 下面是一个LRU算法的Python实现: ```python from collections import OrderedDict class LRUCache: def __init__(self, capacity: int): self.capacity = capacity self.cache = OrderedDict() def get(self, key: int) -> int: if key not in self.cache: return -1 self.cache.move_to_end(key) return self.cache[key] def put(self, key: int, value: int) -> None: if key in self.cache: self.cache.move_to_end(key) self.cache[key] = value if len(self.cache) > self.capacity: self.cache.popitem(last=False) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值