目录
1 项目描述
1.项目整体描述
(1)项目目的:本项目旨在通过实现一个虚拟存储器管理系统,展示请求页式存储管理方法的工作机制。该系统允许大的作业或多个作业的地址空间超过物理内存的限制,通过将部分地址空间存储在辅存中,实现作业的正常运行。项目的目标是让学生理解虚拟存储器的概念及其在操作系统中的实现,特别是请求分页管理和页面置换算法。
(2)项目内容:本项目采用请求页式存储管理方法,以进程为单位进行内存空间分配。项目包含进程的创建、执行和撤销三个主要部分,并使用FIFO先进先出页面置换算法进行页面管理。进程创建:包括创建进程控制块(PCB)、分配基本物理块和将进程插入就绪队列。进程执行:从就绪队列中选择进程执行,执行过程中产生逻辑地址并进行地址重定位,处理请求调入和页面置换,考虑进程阻塞。进程撤销:回收存储空间并注销PCB。
2.负责模块描述
我负责实现页面置换功能,使用FIFO算法管理物理内存块的分配和释放。
FIFO先进先出页面置换算法:当需要将新的页面加载到内存中且内存已满时,选择最早被加载到内存中的页面进行置换。
2 系统结构分析
1.程序结构
程序主要分为三个模块:进程管理、内存管理和调度管理。每个模块负责不同的功能,协同工作以实现虚拟存储器的管理。
进程管理:负责创建、执行和撤销进程。
内存管理:实现页面置换和内存分配。
调度管理:负责进程调度和执行。
2.整体仿真流程
(1)初始化系统,创建调度器和内存管理器。
(2)用户输入进程数量和进程信息。
(3)创建进程,分配基本物理块,将进程插入就绪队列。
(4)调度器从就绪队列中选择进程执行。
(5)执行过程中产生逻辑地址,进行地址重定位,如需页面调入则执行页面置换算法。
(6)进程执行完成后,回收存储空间,注销PCB。
(7)重复步骤4-6,直至所有进程执行完毕。
3.负责模块的子结构分析
FIFO页面置换算法模块负责管理物理内存中的页面。其子结构包括:
(1)页面队列:FIFO队列,用于记录页面的加载顺序。
(2)页面表:映射页号到物理块号。
(3)物理块状态:记录物理块是否被占用。
3 系统详细设计
1.数据结构和存储结构
进程控制块(PCB):包含进程ID、优先级、页数、分配的物理块数量等信息。
页面队列(frameQueue):用于FIFO页面置换。
页面表(pageTable):记录页面与物理块的映射关系。
物理块数组(frames):存储物理块中的页面号。
物理块状态数组(frameOccupied):记录物理块是否被占用。
2.系统总体设计
系统分为进程管理、内存管理和调度管理三个主要部分。每个部分相互协作,实现整个虚拟存储器管理系统的功能。
3.功能流程
首先输入进程数量,然后输入进程ID,优先级,要访问的页数和分配的物理块。然后系统开始执行优先级高的进程,此时输入要访问的页号,然后系统会输出页面置换的过程。
4.负责模块的详细设计(FIFO页面置换算法)
(1)初始化物理块和页面队列。
(2)当访问页面时,检查页面是否在内存中。
(3)如果不在内存且有空闲块,则将页面加载到空闲块中。
(4)如果不在内存且没有空闲块,则使用FIFO算法进行页面置换,将最早加载的页面替换为新页面。
(5)更新页面表和页面队列。
4 系统主要算法
1.算法名称
FIFO先进先出页面置换算法
2.算法作用
管理物理内存中的页面,当需要将新的页面加载到内存中且内存已满时,选择最早被加载到内存中的页面进行置换。
3.算法流程
(1)初始化:系统启动时,所有的物理块都是空闲的。一个FIFO队列用于跟踪页面加载的顺序。
(2)页面访问:
检查页面是否在内存中:如果页面在内存中(即页面表中存在该页面的映射),则直接访问页面,如果页面不在内存中,执行下一步。
检查是否有空闲物理块:如果有空闲物理块,选择一个空闲块,将页面加载到该块中,并在页面表中记录映射关系,如果没有空闲物理块,执行页面置换。
(3)页面置换:
从FIFO队列中取出最早加载的页面(即队列头部的页面),将该页面从内存中移除,并在页面表中删除其映射,将新页面加载到刚释放的物理块中,并在页面表中更新映射关系将新页面添加到FIFO队列的尾部。
(4)更新内存状态:
每次页面加载或置换后,更新内存状态以反映当前的物理块使用情况。
4.算法用到的数据结构及存储结构
使用队列来记录页面加载顺序,页面表和物理块数组来管理页面与物理内存的映射关系:
页面队列(frameQueue)
页面表(pageTable)
物理块数组(frames)
物理块状态数组(frameOccupied)
5 系统程序实现
1.实现环境:
Windows环境。
2.核心代码:
queue<int> frameQueue; // FIFO队列
unordered_map<int, int> pageTable; // 页表,映射页号到块号
vector<int> frames; // 物理块
vector<bool> frameOccupied; // 物理块是否被占用
// 没有空闲帧,进行页面置换
int pageToReplace = frameQueue.front(); // 获取最早加载的页面
frameQueue.pop(); // 从队列中移除该页面
int frameIndex = pageTable[pageToReplace]; // 获取该页面所在的物理块号
cout << "页 " << pageToReplace << " 被替换为页 " << pageNumber << "。" << endl;
frames[frameIndex] = pageNumber; // 将新页面加载到该物理块中
pageTable[pageNumber] = frameIndex; // 更新页面表
pageTable[pageToReplace] = -1; // 更新被替换页面的页表映射
frameQueue.push(pageNumber); // 将新页面加入队列
6 程序实现结果
【实验设计、实验结果截图、实验结果截图说明、实验结果分析与讨论】
首先输入进程数量,然后分别输入每个进程的进程ID、优先级、要访问的页数和分配的物理块,如图表一所示。
图表 1
然后进程调度的优先级算法会对算法的优先级进行排序,先执行优先级高的进程,如图表二所示。
图表 2
可以看到,当访问的页面不在物理块中时会触发缺页中断,然后会把要访问的页面调入物理块中。当访问的页面已经在物理块中时则会正常访问,如图表3所示。
图表 3
进程所要访问的页面都访问完成后,程序会输出进程所有页访问完成并结束,然后运行下一个次优先级的进程,如图表4,图表5所示。
图表 4
图表 5
7 课程设计总结
本次课程设计使我们对虚拟存储器管理有了深入的理解。通过实现FIFO页面置换算法,我们掌握了内存管理的基本方法和操作系统如何在内存不足的情况下管理进程。虽然FIFO算法简单直观,但实际应用中可以结合其他页面置换算法(如LRU)以提高性能。在今后的学习中,可以进一步探讨和实现更复杂的页面置换算法,以提升系统的整体性能和效率。
附录:源程序
#include <iostream>
#include <queue>
#include <unordered_map>
#include <vector>
#include <algorithm>
using namespace std;
class Process
{
public:
int processID; //进程ID
int priority; //优先级
int numPages; //访问页数
int numFrames; //分配的物理块数量
int pagesAccessed; //已访问的页数
queue<int> frameQueue; // FIFO队列
unordered_map<int, int> pageTable; // 页表,映射页号到块号
vector<int> frames; // 物理块
vector<bool> frameOccupied; // 物理块是否被占用
Process(int pid, int prio, int pages, int frames)//PCB
: processID(pid), priority(prio), numPages(pages), numFrames(frames), pagesAccessed(0)
{
this->frames.resize(numFrames, -1); // 初始化物理块
this->frameOccupied.resize(numFrames, false); // 初始化物理块占用情况
}
void accessPage(int pageNumber)
{
// 检查页号是否合法
if (pageNumber < 0)
{
cout << "非法页号: " << pageNumber << endl;
return;
}
// 页表中找到该页
if (pageTable.find(pageNumber) != pageTable.end() && pageTable[pageNumber] != -1)
{
cout << "页 " << pageNumber << " 已在物理块 " << pageTable[pageNumber] << " 中。" << endl;
}
else
{
// 页不在物理内存中,需要置换
if (frameQueue.size() < numFrames)
{
// 还有空闲块
for (int i = 0; i < numFrames; ++i)
{
if (!frameOccupied[i])
{
frames[i] = pageNumber;
pageTable[pageNumber] = i;
frameQueue.push(pageNumber);
frameOccupied[i] = true;
cout << "页 " << pageNumber << " 被加载到物理块 " << i << "。" << endl;
break;
}
}
}
else
{
// 没有空闲帧,进行页面置换
int pageToReplace = frameQueue.front(); //从 frameQueue 队列中获取最早加载的页面(FIFO队列的前端)
frameQueue.pop(); //从 frameQueue 队列中移除最早加载的页面
int frameIndex = pageTable[pageToReplace]; //页号映射到物理块号,frameIndex 保存被替换页面所在的物理块号
cout << "页 " << pageToReplace << " 被替换为页 " << pageNumber << "。" << endl;
frames[frameIndex] = pageNumber;//将页号存储到块号的位置
pageTable[pageNumber] = frameIndex;//将页号映射到物理块号
pageTable[pageToReplace] = -1;//更新页表,将被替换的页面 pageToReplace 对应的物理块号设为 -1,表示该页面不再在物理内存中
frameQueue.push(pageNumber);
}
}
// 增加已访问的页数
pagesAccessed++;
displayFrames();
// 检查是否所有页都已访问完
if (pagesAccessed == numPages)
{
cout << "进程 " << processID << " 所有页访问完成。" << endl;
cout<<endl;
}
}
void displayFrames()
{
cout << "进程 " << processID << " 当前物理块状态:" << endl;
for (int i = 0; i < numFrames; ++i)
{
if (!frameOccupied[i])
{
cout << "物理块 " << i << ": 空闲" << endl;
}
else
{
cout << "物理块 " << i << ": 页 " << frames[i] << endl;
}
}
cout<<endl;
}
};
class Scheduler
{
private:
vector<Process> readyQueue;
public:
void addProcess(Process p)//进程调度算法:高优先级
{
readyQueue.push_back(p);
sort(readyQueue.begin(), readyQueue.end(), [](const Process &a, const Process &b)
{
return a.priority > b.priority; // 优先级从高到低排序
});
}
void run()
{
while (!readyQueue.empty())
{
Process &p = readyQueue.front();
cout << "当前正在运行的进程ID: " << p.processID << ",优先级: " << p.priority << endl;
cout << endl;
int page;
int cnt = 0;
cout << "输入进程 " << p.processID << " 要访问的页号: ";
while (cin >> page && cnt <= p.numPages -2)
{
p.accessPage(page);
cout << "输入进程 " << p.processID << " 要访问的页号: ";
cnt++;
}
p.accessPage(page);
cout << "进程 " << p.processID << " 结束。" << endl;
cout<<endl;
readyQueue.erase(readyQueue.begin());
}
}
};
int main()
{
Scheduler scheduler;
int numProcesses;
cout << "输入进程数量: ";
cin >> numProcesses;
for (int i = 0; i < numProcesses; ++i)
{
int pid, priority, pages, frames;
cout << "输入进程ID: ";
cin >> pid;
cout << "输入优先级: ";
cin >> priority;
cout << "输入要访问的页数: ";
cin >> pages;
cout << "输入分配的物理块数: ";
cin >> frames;
cout<<endl;
Process p(pid, priority, pages, frames);
scheduler.addProcess(p);
}
scheduler.run();
return 0;
}