操作系统实验三虚拟存储管理C++

实验目的

存储管理的主要功能之一是合理地分配空间。请求分页存储管理是常用的虚拟存储管理技术。本实验的目的是请求分页存储管理中页面置换算法模拟设计,了解虚拟存储管理技术的特点,掌握请求分页存储管理的页面置换方法。

实验内容

(1). 通过随机数产生一个指令序列,共 320 条指令。指令的地址按下述原则生成:
① 50 %的指令是顺序执行的;
② 25 %的指令是均匀分布在前地址部分;
③ 25 %的指令是均匀分布在后地址部分。

具体的实施方法是:
④ 在[ 0,319 ]的指令地址之间随机选取一起点 m;
⑤ 顺序执行一条指令,即执行地址为 m + 1 的指令;
⑥ 在前地址[0,m+1]中随机选取一条指令并执行,该指令的地址为m’;
⑦ 顺序执行一条指令,其地址为 m’+1;
⑧ 在后地址[m’+ 2,319]中随机选取一条指令并执行;
⑨ 重复上述步骤,直至执行 320 次指令。

(2). 将指令序列变换成页地址流
设: ① 页面大小为1;② 内存容量为4到32KB;③ 用户虚存容量为 32KB (即32页);
在用户虚存中,按每页存放 10 条指令排列虚存地址,即 320 条指令在虚存中的存放方式为:
第 0 条到第 9 条指令存放在 0 号页(对应的虚存地址为[ 0,9]) ;
第10 条到第 19 条指令存放在1号页(对应的虚存地址为([10,19]) ;

第310条到第319条指令存放在31号页(对应的虚存地址为[310,319]) ;
按以上方式,用户指令可组成 32 页。

实验说明

完成最佳置换算法(OPT)、先进先出置换算法(FIFO)、最近最久未使用置换算法(LRU)三个置换算法即可
LFU及NUR置换算法为可选内容。
命中率:1 -(页面失效次数)/(页地址流长度)在本实验中,页地址流的长度为 320 ,页面失效次数为每次访问相应指令时,该指令所对应的页不在内存的次数。
随机数生成可以使用rand()函数,但在一些编译器上会有警告,推荐使用C++11给的随机函数生成库,感兴趣可以自行查找,本实验无相关说明。

注意:如果遇到在visual stdio编译报错,报错内容是std::至少需要c++17。请查询如何修改编译器C++编译版本

实现步骤

实现中采用了策略者设计模式,如果不清楚也不影响

以下抽象基类及其三个子类都存放在my_algorithm.h头文件

首先实现一个抽象基类
基类中包含一个page_change(…)纯虚函数,要求子类重写

// 页面置换算法 抽象基类
class algorithm {
protected:
    // 记录页面失页次数
    double page_invalid_number = 0;
public:
    algorithm() = default;
    double get_page_invalid_number() const {
        return page_invalid_number;
    }
    // 页面置换 纯虚函数 要求子类重写实现
    virtual void page_change(int frame, vector<int> &page_address) = 0;
    virtual ~algorithm() = default;
};

实现FIFO置换算法
思想:使用List模拟队列,先进先出

// 先进先出置换算法 first-in first-out
class FIFO : public algorithm {
private:
    list<int> page_queue;
public:
    void page_change(int frame, vector<int> &page_address) override {
        page_invalid_number = 0;
        page_queue.clear();

        for (int i = 0; i < 320; ++i) {
            bool flag = false;
            for (const auto &it: page_queue) {
                if (it == page_address[i]) {
                    flag = true;
                    break;
                }
            }
            if (!flag) {
                ++page_invalid_number;
                if (page_queue.size() >= frame) {
                    page_queue.pop_front();
                }
                page_queue.push_back(page_address[i]);
            }
        }
    }
};

实现LRU置换算法
思想:使用List模拟栈,栈底始终是最早访问到的页面,栈顶是最新访问到的页面,每次置换栈底的页面

// 最近最久未使用置换算法 least recently used
class LRU : public algorithm {
private:
    list<int> page_stack;
public:
    void page_change(int frame, vector<int> &page_address) override {
        page_invalid_number = 0;
        page_stack.clear();

        for (int i = 0; i < 320; ++i) {
            bool flag = false;
            for (auto it = page_stack.begin(); it != page_stack.end(); ++it) {
                if (*it == page_address[i]) {
                    flag = true;
                    page_stack.erase(it);
                    page_stack.push_back(page_address[i]);
                    break;
                }
            }

            if (!flag) {
                ++page_invalid_number;
                if (page_stack.size() >= frame) {
                    page_stack.pop_front();
                }
                page_stack.push_back(page_address[i]);
            }
        }
    }
};

实现OPT置换算法
思想:选择未来不会使用到或者最久不会使用到的页面进行置换

// 最佳置换算法 optimal
class OPT : public algorithm {
private:
    vector<int> page_queue;
    unordered_map<int, int> next_occurrence; // 存储每个页面下一次出现的位置
public:
    void page_change(int frame, vector<int> &page_address) override {
        page_invalid_number = 0;
        page_queue.clear();
        next_occurrence.clear();

        for (int i = 0; i < page_address.size(); ++i) {
            if (next_occurrence.find(page_address[i]) == next_occurrence.end()) {
                next_occurrence[page_address[i]] = INT_MAX; // 未知下一次出现位置的页面设为最大值
            }

            // 更新每个页面下一次出现的位置
            for (int j = i + 1; j < page_address.size(); ++j) {
                if (page_address[j] == page_address[i]) {
                    next_occurrence[page_address[i]] = j;
                    break;
                }
            }

            // 页面失效
            if (find(page_queue.begin(), page_queue.end(), page_address[i]) == page_queue.end()) {
                ++page_invalid_number;
                if (page_queue.size() >= frame) {
                    int replace_page = page_queue[0];
                    int max_next_occurrence = next_occurrence[page_queue[0]];
                    // 选择下一个要替换的页面
                    for (int j = 1; j < page_queue.size(); ++j) {
                        if (next_occurrence[page_queue[j]] > max_next_occurrence) {
                            replace_page = page_queue[j];
                            max_next_occurrence = next_occurrence[page_queue[j]];
                        }
                    }

                    // 替换页面
                    page_queue.erase(remove(page_queue.begin(), page_queue.end(), replace_page),
                                     page_queue.end());
                }
                page_queue.push_back(page_address[i]); // 将新的页面加入队列
            }
        }
    }
};

实现VSM类,虚拟存储管理 (Virtual Storage Management)

VSM类包含以下两个私有属性
command使用集合保存,是因为老师要求320条指令不能重复

private:
    // 存放指令
    unordered_set<int> command;
    // 存放页地址流
    vector<int> page_address;

VSM类下包含两个公共方法
其中Random(…)函数是自定义的保护方法,下面有给出

public:
	// 通过随机数生成指令
    inline void create_command() {
        srand(time(nullptr));
        int m2;
        int m = Random(0, 318);

        while (command.size() < 320) {
            command.insert(m);
            command.insert(m + 1);
            m2 = m + 1;
            m = Random(0, m2 - 1); // 在[0, m2-1]的随机数
            command.insert(m);
            command.insert(m + 1);
            m = Random(m + 2, 318); // 生成范围在[m2+1, 318]的随机数
        }
    }

    // 将指令序列变为页地址流
    inline void ToPage_address() {
        page_address.resize(command.size());
        int i = 0;
        auto it = command.begin();
        while (i < 320 && it != command.end()) {
            page_address[i++] = *it / 10;
            ++it;
            if (it == command.end()) {
                it = command.begin(); // 重新从头开始循环
            }
        }
    }

protected:
    static int Random(int begin, int end) {
        return rand() % (end - begin + 1) + begin;
    }

完整代码

请注意文件模块的划分

//请新建一个名为my_algorithm.h的头文件保存下面内容
#ifndef OS_03_MY_ALGORITHM_H
#define OS_03_MY_ALGORITHM_H

#include <vector>
#include <random>
#include <unordered_map>
#include <queue>
#include <list>
#include <stack>
#include <algorithm>

using namespace std;

// 页面置换算法 抽象基类
class algorithm {
protected:
    // 记录页面失页次数
    double page_invalid_number = 0;
public:
    algorithm() = default;
    [[nodiscard]] double get_page_invalid_number() const {
        return page_invalid_number;
    }
    // 页面置换 纯虚函数 要求子类重写实现
    virtual void page_change(int frame, vector<int> &page_address) = 0;
    virtual ~algorithm() = default;
};

// 先进先出置换算法 first-in first-out
class FIFO : public algorithm {
private:
    list<int> page_queue;
public:
    void page_change(int frame, vector<int> &page_address) override {
        page_invalid_number = 0;
        page_queue.clear();
        for (int i = 0; i < 320; ++i) {
            bool flag = false;
            for (const auto &it: page_queue) {
                if (it == page_address[i]) {
                    flag = true;
                    break;
                }
            }
            if (!flag) {
                ++page_invalid_number;
                if (page_queue.size() >= frame) {
                    page_queue.pop_front();
                }
                page_queue.push_back(page_address[i]);
            }
        }
    }
};

// 最近最久未使用置换算法 least recently used
class LRU : public algorithm {
private:
    list<int> page_stack;
public:
    void page_change(int frame, vector<int> &page_address) override {
        page_invalid_number = 0;
        page_stack.clear();
        for (int i = 0; i < 320; ++i) {
            bool flag = false;
            for (auto it = page_stack.begin(); it != page_stack.end(); ++it) {
                if (*it == page_address[i]) {
                    flag = true;
                    page_stack.erase(it);
                    page_stack.push_back(page_address[i]);
                    break;
                }
            }

            if (!flag) {
                ++page_invalid_number;
                if (page_stack.size() >= frame) {
                    page_stack.pop_front();
                }
                page_stack.push_back(page_address[i]);
            }
        }
    }
};

// 最佳置换算法 optimal
class OPT : public algorithm {
private:
    vector<int> page_queue;
    unordered_map<int, int> next_occurrence; // 存储每个页面下一次出现的位置
public:
    void page_change(int frame, vector<int> &page_address) override {
        page_invalid_number = 0;
        page_queue.clear();
        next_occurrence.clear();
        for (int i = 0; i < page_address.size(); ++i) {
            if (next_occurrence.find(page_address[i]) == next_occurrence.end()) {
                next_occurrence[page_address[i]] = INT_MAX; // 未知下一次出现位置的页面设为最大值
            }
            // 更新每个页面下一次出现的位置
            for (int j = i + 1; j < page_address.size(); ++j) {
                if (page_address[j] == page_address[i]) {
                    next_occurrence[page_address[i]] = j;
                    break;
                }
            }
            // 页面失效
            if (find(page_queue.begin(), page_queue.end(), page_address[i]) == page_queue.end()) {
                ++page_invalid_number;
                if (page_queue.size() >= frame) {
                    int replace_page = page_queue[0];
                    int max_next_occurrence = next_occurrence[page_queue[0]];
                    // 选择下一个要替换的页面
                    for (int j = 1; j < page_queue.size(); ++j) {
                        if (next_occurrence[page_queue[j]] > max_next_occurrence) {
                            replace_page = page_queue[j];
                            max_next_occurrence = next_occurrence[page_queue[j]];
                        }
                    }
                    // 替换页面
                    page_queue.erase(remove(page_queue.begin(), page_queue.end(), replace_page),
                                     page_queue.end());
                }
                page_queue.push_back(page_address[i]); // 将新的页面加入队列
            }
        }
    }
};
#endif //OS_03_MY_ALGORITHM_H

//请新建一个名为The_Client.h的头文件保存下面内容
#ifndef OS_03_THE_CLIENT_H
#define OS_03_THE_CLIENT_H

#include <unordered_set>
#include "my_algorithm.h"

//虚拟存储管理 (Virtual Storage Management)
class VSM {
private:
    // 存放指令
    unordered_set<int> command;
    // 存放页地址流
    vector<int> page_address;
    // 算法指针
    algorithm *algoPtrO, *algoPtrF, *algoPtrL;
protected:
    static int Random(int begin, int end) {
        return rand() % (end - begin + 1) + begin;
    }
public:
    //Client() = default;   不用默认构造函数 因为要传入算法指针
    ~VSM() = default;
    explicit VSM(algorithm *algorithm_ptrO, algorithm *algorithm_ptrF, algorithm *algorithm_ptrL) {
        this->algoPtrO = algorithm_ptrO;
        this->algoPtrF = algorithm_ptrF;
        this->algoPtrL = algorithm_ptrL;
    }
    // 通过随机数生成指令
    inline void create_command() {
        srand(time(nullptr));
        int m2;
        int m = Random(0, 318);
        while (command.size() < 320) {
            command.insert(m);
            command.insert(m + 1);
            m2 = m + 1;
            m = Random(0, m2 - 1); // 在[0, m2-1]的随机数
            command.insert(m);
            command.insert(m + 1);
            m = Random(m + 2, 318); // 生成范围在[m2+1, 318]的随机数
        }
    }
    // 将指令序列变为页地址流
    inline void ToPage_address() {
        page_address.resize(command.size());
        int i = 0;
        auto it = command.begin();
        while (i < 320 && it != command.end()) {
            page_address[i++] = *it / 10;
            ++it;
            if (it == command.end()) {
                it = command.begin(); // 重新从头开始循环
            }
        }
    }
    inline void Run() {
        // 物理块从4到32
        printf("page frames\t\tFIFO\t\t\tLRU\t\t\tOPT\n");
        for (int frame = 4; frame <= 32; ++frame) {
            algoPtrF->page_change(frame, page_address);
            algoPtrL->page_change(frame, page_address);
            algoPtrO->page_change(frame, page_address);
            printf("\t%d\t\t %.4f\t\t\t %.4f\t\t\t %.4f\n", frame, 1 - algoPtrF->get_page_invalid_number() / 320,
                   1 - algoPtrL->get_page_invalid_number() / 320, 1 - algoPtrO->get_page_invalid_number() / 320);
        }
    }
};

#endif //OS_03_THE_CLIENT_H

//测试函数,请新建一个任意名称的.cpp文件
#include <iostream>
#include "The_Client.h"

int main() {
    auto *client = new VSM(new OPT(), new FIFO(), new LRU());
    // 生成指令
    client->create_command();
    // 转为页地址流
    client->ToPage_address();
    client->Run();
    return 0;
}

欧耶,至此,实验三已经完成。

由于本人技术有限,代码中不免存在纰漏,还望见谅。💕💕💕

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
操作系统中,C语言的请求调页存储管理方式是指通过C语言的系统调用来实现页面调度的过程。该过程主要包含个步骤:页面置换、页面分配、页面回收。页面置换是指当内存中的页框被用完时,需要将其中的一些页帧替换出去,以便腾出空间给新的页面使用。页面分配是指在需要新的页面时,从主存中分配一个或多个空闲页面,在操作系统中建立该页面对应的页表项,并将该页面的物理地址与逻辑地址映射起来。页面回收是指当页面不再被使用时,需要将其从主存中回收,并更新对应的页表项。 在C语言中实现请求调页存储管理方式,需要使用一些系统调用函数,比如mmap、munmap等。其中,mmap函数用于请求操作系统为应用程序分配指定大小的虚拟地址空间,并将这个区域映射到实际物理内存上。而munmap函数则用于释放已经映射的虚拟地址空间,并将其对应的物理内存页帧标记为空闲状态。 对于操作系统的调页机制而言,C语言是非常重要的一种实现方式。通过使用C语言的系统调用,在应用程序层面上就可以控制操作系统页面调度的过程,并实现更加高效和灵活的页面管理方法。同时,C语言还可以与各种硬件设备进行有效的数据交换,从而进一步优化页面置换、分配和回收的速度和效率。因此,C语言请求调页存储管理方式在操作系统开发中是非常常见的一种实现方式。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

打代码要笑着打

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

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

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

打赏作者

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

抵扣说明:

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

余额充值