AI 文章摘要

本文介绍了一个页面置换系统的实现,采用固定分配和局部置换策略。用户可以选择分配给进程的物理块数(3或4),系统会随机生成20个页面访问序列,并输出页面装入情况、缺页次数及缺页率。文中详细介绍了三种页面置换算法:FIFO(先进先出)、LRU(最近最少使用)和OPT(最佳置换),并比较了它们的优缺点。FIFO算法简单易实现但可能导致较高的缺页率;LRU算法能有效减少缺页率但实现较复杂;OPT理论上最佳但实际难以实现。最后,程序展示了各算法在随机页面访问下的表现和缺页情况。 阅读此文章大概需要的时间:3分钟。
摘要更新时间:2025-02-08 18:39

一、实现要求

(1)系统采用固定分配、局部置换的策略
(2) 分配给进程的物理块数在程序中可选:3或者4
(3)系统随机产生页面访问序列地址,序列长度为20,
(4)运行时,实现了输入分配给进程的物理块数,系统输出随机页访问序列、物理块装入页面情况,并最终打印出缺页次数与缺页率

二、算法的介绍

当涉及到页面置换算法时,三种主要的算法是FIFO(先进先出)、LRU(最近最少使用)和OPT(最佳置换)。

1、FIFO(先进先出)

  • 基本思想是按照页面最早进入内存的顺序进行置换。
  • 每次新的页面来到内存时,将最早进入内存的页面替换出去。
  • 缺点是它并不考虑页面的访问频率或模式,可能会因为长时间驻留内存的页面导致性能下降。

2、LRU(最近最少使用)

  • 基于“最近最少使用”的原则进行置换。
  • 每次替换时选择最近最少被使用的页面,当访问到的序列不在物理块中时,加到物理块(字典)末尾,把最早使用的头(物理块的第一位)pop掉,当访问的序列在物理块中时,直接替换掉该页面,把新的一样的页面加到物理块(字典)末尾。
  • 通过维护页面使用的顺序,可以有效地保留经常被使用的页面,提高缓存命中率。
  • 实现上可能需要维护一个访问记录或者记录页面最近使用的时间戳。

3、OPT(最佳置换)

  • OPT是理论上的最优算法,它利用未来页面访问的信息进行置换。
  • 在每次置换时,选择未来最长时间不被访问的页面进行替换。(即不在未来字典中的页面)
  • 为了实现这个算法,需要预知未来页面访问的情况,这在实际中通常是不可能的,因此OPT通常被用作评估其他算法的性能上限。

其次,FIFO简单易实现但性能一般,LRU较为复杂但在一定程度上提高了性能,而OPT则是理论上的最佳方案,但实际应用受限于未来页面访问情况的不可知性。在实际系统中,往往会根据具体的场景和需求选择适合的算法。

三、程序所使用数据结构及其说明

1. 列表(List):

   – `frames` 在 FIFO 和 OPT 算法中用于模拟页面帧。

   – `page_sequence` 用于存储生成的随机页面访问序列。

2. 字典(Dictionary):

   – `future_pages` 在 OPT 算法中用于存储预测未来页面访问的信息。

   – `self.frames` 在 LRU 类中使用 `OrderedDict` 模拟页面帧。

3. OrderedDict:

   – `self.frames` 在 LRU 类中使用 `OrderedDict` 来实现LRU页面置换算法,保持页面访问的顺序。

四、程序运行截图以及说明

物理块数与页面访问序列:

第一张

可以看到输入的分配给进程的物理块数为3

同时随机页面访问序列: [6, 7, 3, 4, 9, 0, 7, 4, 7, 7, 5, 9, 1, 2, 9, 0, 9, 7, 6, 2]长度为20

1、FIFO算法执行

FIFO算法执行图

算法说明:
(1)按照页面最先进入页面帧的顺序进行页面置换。
(2)页面访问序列以及物理块装入情况的演变过程展示了每次页面访问时物理块的变化情况。
(3)当页面帧已满时,FIFO算法会将最先进入页面帧的页面替换出去,即使该页面在未来还会被访问。
(4)缺页次数表示需要从磁盘中获取的页面数量,缺页率则表示页面置换带来的性能损失。
(5)在这个情况下,随机页面访问序列和FIFO算法的组合导致了较高的缺页率。这是因为FIFO算法仅考虑了页面进入页面帧的先后顺序,而没有考虑到页面的访问频率或模式,导致了较高的页面缺失率。

2、LRU算法

LRU算法执行图

算法说明:

LRU算法根据页面最近的访问情况来决定页面置换,最近最少使用的页面会被置换出去。页面访问序列以及物理块装入情况的变化展示了每次页面访问时物理块的变化情况。当页面帧已满时,LRU算法会根据页面的最近使用情况选择最近最少使用的页面进行置换。如当前页面访问序列: 0 物理块装入情况: [4, 9, 0],再之后当前页面访问序列: 7 的情况中,7没有在物理块中,那么下一个物理块装入情况就为[9, 0, 7],把4pop出去了,因为从当前访问值7往前看,4的首次出现的位置是最早的,所以是最近最早使用过,所以pop掉,再如当前页面访问序列: 0 物理块装入情况: [2, 9, 0],下一个页面访问序列: 9 中,9已经在物理块中,证明之前访问过,那么就pop掉该页把新的页面加入,为[2, 0, 9],你也可以通过改变代码变成是替换该页而不是pop出去加尾巴。

3、OPT算法

OPT算法执行图

算法说明:

上述每个页面被访问时,OPT算法都会尝试预测未来的页面访问顺序,并据此选择要替换的页面。然而,OPT算法需要未来页面访问序列的完整信息,因此在实际应用中无法获得。在该例子中,由于没有未来页面访问序列的信息,算法只能基于当前已知的信息进行页面替换,所以缺页次数是12次,缺页率为60%,如在执行图中当前页面访问序列:9,物理块装入情况[9, 7, 4];当前页面访问序列:0,物理块装入情况[0, 7, 4];我们可以看到当访问到0时,从当前0位置往后看,9出现的第一个位置是最远的,即在很长一段时间内9不会被访问,所以替换掉,以此类推。

五、三种算法优缺点比较

FIFO(先进先出)

优点:

1. 实现简单,易于理解和部署。

2. 对于某些页面访问模式,表现良好。

缺点:

1. 可能导致Belady现象,即增加物理块数反而会增加缺页率。

2. 不考虑页面访问频率和时间顺序,不能适应某些复杂的访问模式。

LRU(最近最少使用)

优点:

1. 在某些场景下能够有效减少缺页率。

2. 通常情况下能较好地模拟出较少访问的页面进行置换。

缺点:

1. 实现稍微复杂一些,需要记录页面访问时间或者频率。

2. 可能在某些情况下无法很好地模拟出最佳的页面置换。

OPT(最佳置换)

优点:

1. 算法理论上能够达到最佳效果,即最少的缺页率。

2. 作为一种理想化算法,提供了衡量其他算法性能的标准。

缺点:

1. 需要对未来所有页面访问进行预测,这在实际中几乎是不可能做到的。

2. 算法无法应对实际场景中的动态变化和未知的页面访问模式,仅作为性能的一个理论上的上限。

系统比较:

1. 性能: OPT理论上最优,但实际无法实现。LRU在很多情况下表现良好,尤其是对于局部性较强的访问模式。FIFO简单易实现,但在某些情况下缺页率可能较高。

2. 复杂度: FIFO是最简单的,LRU稍复杂一些需要记录访问时间或频率,OPT则是理论上最复杂的,需要对未来页面访问进行预测。

3. 实用性: 在实际应用中,常用的是LRU算法,因为它在性能和实现复杂度之间有一个良好的平衡,而且在许多场景中表现不错。

六、附全部代码

import random
from collections import OrderedDict

#FIFO页面置换(先进先出)
def fifo(page_reference_string, num_frames):
    frames = []
    page_faults = 0  #缺页数量(即所需的页面不在内存中)

    print("\nFIFO页面替换算法\n")

    for page in page_reference_string:
        if page not in frames:
            if len(frames) < num_frames:
                frames.append(page)
            else:
                frames.pop(0)
                frames.append(page)
            page_faults += 1

        print("当前页面访问序列: {},物理块装入情况: {}".format(page, frames))

    return page_faults

#LRU页面置换(最少使用)
class LRU:
    def __init__(self, num_frames):
        self.num_frames = num_frames
        self.frames = OrderedDict() #OrderedDict用于模拟页面帧(即内存中的页面),存键值对。
        self.page_faults = 0  #缺失页页数

    def reference_page(self, page):
        if page not in self.frames:
            if len(self.frames) >= self.num_frames:
                self.frames.popitem(last=False)  #调用opitem() 方法用于移除字典中的最早的一项(last = false),如果last = true,那么移除最后一项
            self.frames[page] = None #将新引用的页面加入页面帧中,即加到字典末尾,值为None(page是键,None是值)
            self.page_faults += 1
        else:
            self.frames.pop(page)
            self.frames[page] = None
    
    def display_frames(self,page):
        print("当前页面访问序列: {} 物理块装入情况: {}".format(page,list(self.frames.keys())))

    def get_page_faults(self):
        return self.page_faults

#OPT页面置换(最佳置换)
# 获取接下来不会使用或最远会使用的页面
def get_future_pages(pages, current_index):
    future_pages = {}
    # 从当前位置往后遍历页面序列
    for i in range(current_index + 1, len(pages)):
        # 如果页面不在预测的未来访问页面中,则将它添加到未来页面字典中
        if pages[i] not in future_pages:
            future_pages[pages[i]] = i
    return future_pages

# OPT 算法
def optimal(page_reference_string, num_frames):
    frames = []  # 存储页面帧
    page_faults = 0  # 缺页次数

    print("\nOPT算法\n")

    for i, page in enumerate(page_reference_string):
        if page not in frames:
            # 如果页面不在帧中,进行页面置换
            if len(frames) < num_frames:
                frames.append(page)  # 如果帧未满,直接加入页面帧
            else:
                # 获取未来页面访问信息
                future_pages = get_future_pages(page_reference_string, i)
                index_to_replace = -1
                max_future_index = -1
                # 遍历当前页面帧进行置换选择
                for frame_index, frame in enumerate(frames):
                    # 如果页面不在未来访问序列中,则置换当前页面
                    if frame not in future_pages:
                        index_to_replace = frame_index
                        break
                    # 如果页面在未来访问序列中,并且其访问位置更远,则选择该页面进行置换
                    elif future_pages[frame] > max_future_index:
                        max_future_index = future_pages[frame]
                        index_to_replace = frame_index

                frames[index_to_replace] = page  # 进行页面置换
            page_faults += 1  # 增加缺页次数统计

        # 打印每次页面访问后的页面帧情况
        print("当前页面访问序列: {},物理块装入情况: {}".format(page, frames))

    return page_faults  # 返回缺页次数

# 生成随机页面访问序列
def generate_page_sequence(length):
    return [random.randint(0, 9) for _ in range(length)]

# 主程序
def main():
    num_frames = int(input("请输入分配给进程的物理块数:"))

    sequence_length = 20
    page_sequence = generate_page_sequence(sequence_length)
    print("\n随机页面访问序列:", page_sequence)
    
    while True:
        print('请输入需要选择的算法:1、FIFO   2、LRU   3、OPT   4、退出')
        a = input()
        if int(a)==1:
            page_faults = fifo(page_sequence, num_frames)
            page_fault_rate = page_faults / sequence_length * 100
            print("\n缺页次数:", page_faults)
            print("缺页率:{:.2f}%".format(page_fault_rate))
        elif int(a) == 2:
            lru = LRU(num_frames)
            print("\nLRU页面置换算法\n")
            for page in page_sequence:
                lru.reference_page(page)
                lru.display_frames(page)

            page_faults = lru.get_page_faults()
            page_fault_rate = page_faults / sequence_length * 100
            print("\n缺页次数:", page_faults)
            print("缺页率:{:.2f}%".format(page_fault_rate))
        elif int(a)==3:
            page_faults = optimal(page_sequence, num_frames)
            page_fault_rate = page_faults / sequence_length * 100
            print("\n缺页次数:", page_faults)
            print("缺页率:{:.2f}%".format(page_fault_rate))
        else:
            break

if __name__ == "__main__":
    main()