操作系统 实验9:页面置换算法模拟设计 (FIFO)(LRU)(OPT)(LFR)(NUR) --使用c++实现 (上)

目录

前言

实验要求

1. 通过随机数产生一个指令序列,共320条指令。指令的地址按下述原则生成:

2. 将指令序列映射为页地址流

代码部分

前置部分

指令序列生成

核心部分

思路

        首先是关于为什么要使用c++而不使用c语言:

        模块设计思路:

        基类

        子类(算法类):

        主函数:

实现

        基类

        主函数

运行时图:

下篇链接


前言

   存储管理的主要功能之一是合理地分配空间。请求页式管理是一种常用的虚拟存储管理技术。

   本实验的目的是通过请求页式存储管理中页面置换算法模拟设计,了解虚拟存储技术的技术特点,掌握请求页式存储管理的页面置换算法。

实现:先进先出的算法(FIFO)、最近最久未使用算法(LRU)、最近最少访问页面算法(LFR)、最近未用过算法(NUR)、最佳页面置换算法(OPT)

实验要求

1. 通过随机数产生一个指令序列,共320条指令。指令的地址按下述原则生成:

   ①50%的指令是顺序执行的;

   ②50%的指令是均匀分布在前地址部分;

   ③50%的指令是均匀分布在后地址部分。

   具体的实施方法是:

   ①在 [0,319] 的指令之间随即选取一起点m;

   ②顺序执行一条指令,即执行地址为m+1的指令;

   ③在前地址[0,m+1]中随机选取一条指令并执行,该指令的地址为m′;

   ④顺序执行一条指令,其地址为 m′+ 1;

   ⑤在后地址[m′+ 2,319]中同上操作;

   ⑥重复上述步骤①-⑤,直到执行 n 次指令。

2. 将指令序列映射为页地址流

   设:①页面大小为1k;

       ②用户内存容量为4页到32页;

       ③用户虚存容量为32k。

     在用户虚存中,按每k存放10条指令排在虚存地址,即320条指令在虚存中的存放方式为:

   第0条-第9条指令为第0页(对应虚存地址为[0,9]);

   第10条-第19条指令为第一页(对应虚存地址为[10,19]);

        … …

   第310条~第319条指令为第31页(对应虚地址为[310,319])。

   按以上方式,用户指令可组成32页。

代码部分

前置部分

使用到的头文件和宏定义,后续部分直接使用(因为接近底层,没有使用vector等stl容器)

#include <assert.h>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>

#define INC_NUM 320
#define RUN_NUM 320
#define _1K_ 10

指令序列生成

这部分没有难度,按照要求来就行,值得注意的是,可以设置随机数种子为进程ip或者当前时间,否则每一次随机数将相同。重载了'[]' '<<' 为后续操作提供便利。

class IncSeq
{
    IncSeq(IncSeq&) = delete;
    IncSeq& operator=(IncSeq&) = delete;

public:
    IncSeq(int num) : size(num), data(new int[num])
    {
        for (int i = 0; i < num; i++)
        {
            switch (i % 6)
            {
            case 0: // 0 ~ 319
                data[i] = rand() % 320;
                break;
            case 2: // 前地址随机取
                data[i] = rand() % data[i - 1];
                break;
            case 4: // 后地址随机取
                data[i] = data[i - 1] + rand() % (num - data[i - 1]);
                break;
            case 1: // 顺序取下一指令
            case 3:
            case 5:
                data[i] = (data[i - 1] + 1) % num;
            }
        }
    }

    ~IncSeq() { delete[] data; }

    int Size() const { return this->size; }

    int operator[](int num) const
    {
        assert(num < size);
        return data[num];
    }

    friend std::ostream& operator<<(std::ostream& os, const IncSeq& obj);

private:
    int* data;
    int size;
};

std::ostream& operator<<(std::ostream& os, const IncSeq& obj)
{
    os << obj[0];
    for (int i = 1; i < obj.Size(); i++)
    {
        os << " " << obj[i];
    }
    return os;
}

核心部分

思路

        首先是关于为什么要使用c++而不使用c语言:

        1. 除了opt,其他四种算法实现思路基本相同,使用多态指针可以更好的认知四种算法的相同之处和不同之处。

        2. 交互部分有大量的信息输出,单纯使用常规使用c语言的方式会导致代码冗余,重复部分占比较大,使用c++多态指针,虚函数,重载输出流可以一定程度缓解这个问题。

        3. 通过类的析构函数可以更好的管理内存,避免迭代开空间,迭代释放空间,在代码量较大的情况减少内存泄漏的可能。

        模块设计思路:
        基类

        只需要对外公开关于run部分接口,后面发现需要求命中率,增加了一个HitRate接口。

        因为页面置换算法的核心是置换,所以信息的迭代很重要,在基类中存储了前一次执行指令后的相关信息:是否置换,哪里置换,置换前是什么(置换后可以查询得到),还有必须的就是页面的存储空间,

        很重要的一个虚函数就是关于信息输出的,这部分关系着代码的易用性,后面再详细展开。

        后面增加了两个计数器,总次数和缺页次数用于计算命中率。

        子类(算法类):

        核心是实现父类的两个接口:日志和交互,至于具体的实现细节,辅助变量等就根据算法不同子类自行安排。

        这其中差别比较大的就是opt,因为opt是基于未来的算法,所以需要传入整个指令序列,用于算法实现。

        主函数:

        主函数便是生成指令序列,通过键盘输入确定多态指针绑定哪一个算法,利用面向对象晚绑定的特性,让代码非常灵活,增强扩展性,减少代码冗余:五个不同的算法,c语言需要五个不同的模块,但是其中很多交互,模式部分是非常相似甚至相同的,并且完全没有扩展性,后来添加算法只能重写。

        设计暂停模块让交互更加友好,windows可以通过puase实现,我选择使用cin.get实现(linux),之后便是调用即可。

实现

        基类

        可以看到,增加求命中率的方法前是没有RunOnce函数的,只有Once函数,但是因为对于所有算法计算运行次数和缺页次数都是经Once接口的,所以只有再基类中添加两个计数器即可实现对求命中率的扩展,不必挨个算法添加。

        在交互方面,通过基类的'<<'运算符来实现标准输出,通过Subjoin()虚函数来实现不同算法之间差异性结构的输出,拉至后面可以直接查看输出是什么样的。

class User 
{
    User(User&) = delete;
    User& operator=(User&) = delete;

public:
    User(int memoryCapacity)
        : memory(new int[memoryCapacity])
        , memoryCapacity(memoryCapacity)
        , haveRep(false)
        , lastRep(-1)
        , lastRepNum(0)
        , count(0)
        , repCount(0)
    {
        memset(memory, -1, sizeof(int) * memoryCapacity);
    }

    virtual ~User()
    {
        delete[] memory;
    }

    virtual bool Once(int incId) = 0; // 缺页返回true

    void RunOnce(int incId)
    {
        count++;
        if (Once(incId))
            repCount++;
    }

    float HitRate() const // 求命中率
    {
        return 1 - static_cast<float>(repCount) / count;
    }

    friend std::ostream& operator<<(std::ostream& os, const User& obj);

protected:
    bool haveRep;       // 上一步是否发生替换
    int lastRep;        // 上一步被替换的内容
    int lastRepNum;     // 上一步被替换的位置
    int memoryCapacity; // 物理块大小(内存容量)
    int* memory;

    int count;    // 计输入的指令次数
    int repCount; // 计缺页的次数

    virtual std::string Subjoin() const
    {
        return "";
    }
};

std::ostream& operator<<(std::ostream& os, const User& obj)
{
    os << "      [ " << std::setw(3) << obj.memory[0];
    for (int i = 1; i < obj.memoryCapacity; i++)
        os << " | " << std::setw(3) << obj.memory[i];
    os << " ]";
    if (obj.haveRep)
        os << "  | 缺页-第" << (obj.lastRepNum + 1) << "个: [" << obj.lastRep
           << " -> " << obj.memory[obj.lastRepNum] << "] |";
    os << obj.Subjoin();
    return os;
}
        主函数

        先放出主函数部分,后面算法可以结合运行结果分析,查看。

        分为三个部分,首先是随机生成指令序列并输出,没什么说的。然后是通过与用户交互进行多态指针的绑定,最后就是运行并输出结果,献给大家放几张运行图。

int main()
{   // 生成随机指令序列
    IncSeq incSeq(RUN_NUM);
    std::cout << "序列随机生成 " << RUN_NUM << " 个指令:\n"
              << incSeq << std::endl;
    // 输入物理块数和选择页面置换算法
    User* usr;
    void init(User * &usr, IncSeq & incSeq);
    init(usr, incSeq);

    void run(User * &usr, IncSeq & incSeq);
    run(usr, incSeq);

    std::cout << "\npress any key to exit..." << std::endl;
    std::cin.get();
    return 0;
}

void init(User*& usr, IncSeq& incSeq)
{
    char nop, y;
    int x = 0;
    std::cout << "请输入物理块数( >0 and not char ): ";
    std::cin >> x;
    if (x <= 0)
        exit(1);
    std::cout << std::endl;
    std::cout << "1.FIFO  2.LRU  3.LFU  4.NRU  5.OPT" << std::endl;
    while (true)
    {
        bool loop = false;
        std::cout << "请选择页面置换算法: ";
        std::cin >> y;
        switch (y)
        {
        case '1':
            usr = new FIFO(x);
            break;
        case '2':
            usr = new LRU(x);
            break;
        case '3':
            usr = new LFU(x);
            break;
        case '4':
            usr = new NRU(x);
            break;
        case '5':
            usr = new OPT(x, incSeq);
            break;
        default:
            loop = true;
            break;
        }
        if (!loop)
            break;
    }
    std::cout << std::endl;
}

void run(User*& usr, IncSeq& incSeq)
{
    int t = 0;
    std::cin.get();
    std::cout << "接下来按下任意键继续下一步...(按下'e'将跳至结束)"
              << std::endl;
    std::cout << "then press any key to continue...(press 'e' to end last)"
              << std::endl;
    std::cin.get();
    system("clear");
    std::cout << "\nT" << t << ":\n" << *usr << std::endl;
    char nop = std::cin.get();
    while (t < RUN_NUM)
    {
        usr->RunOnce(incSeq[t]);
        std::cout << "\nT" << std::left << std::setw(3) << t + 1
                  << ": |  Inc: " << std::right << std::setw(3) << incSeq[t]
                  << " |  命中率: " << usr->HitRate() << '\n'
                  << *usr << std::endl;
        t++;
        if (nop != 'e')
            nop = std::cin.get();
    }
    if (nop == 'e')
        std::cin.get();
}
运行时图:

运行出来大概就是这种感觉,可以根据个人习惯调整,因为篇幅比较长所以分上下文,大家可以点击链接跳转至后篇。

下篇链接

【操作系统 实验9:页面置换算法模拟设计 (FIFO)(LRU)(OPT)(LFR)(NUR) --使用c++实现 (下) - CSDN App】

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leisure_水中鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值