操作系统实验三——虚拟存储管理
实验目的
存储管理的主要功能之一是合理地分配空间。请求分页存储管理是常用的虚拟存储管理技术。本实验的目的是请求分页存储管理中页面置换算法模拟设计,了解虚拟存储管理技术的特点,掌握请求分页存储管理的页面置换方法。
实验内容
(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;
}
欧耶,至此,实验三已经完成。