实验二:存储管理
一、 实验目的:
- 通过编写程序实现请求分页存储管理的Optimal、FIFO、LRU等页面置换算法中的一种,掌握虚拟存储管理中有关缺页处理方法等内容,巩固有关虚拟存储管理的教学内容。
- 理解内存分配原理,特别是以页面为单位的虚拟内存分配方法。
关于操作系统的内存管理,如何节省利用容量不大的内存为最多的进程提供资源,一直是研究的重要方向。而内存的虚拟存储管理,是现在最通用,最成功的方式—— 在内存有限的情况下,扩展一部分外存作为虚拟内存,真正的内存只存储当前运行时所用得到信息。这无疑极大地扩充了内存的功能,极大地提高了计算机的并发度。虚拟页式存储管理,则是将进程所需空间划分为多个页面,内存中只存放当前所需页面,其余页面放入外存的管理方式。
置换算法有:最佳置换算法OPT、FIFO置换算法、最少使用页面置换算法、最近未使用页面置换算法、时钟页面置换算法等
三、 实验环境
基于win10x64位操作系统
四、 实验内容
通过简单的程序模拟多种存储管理算法,通过输入页面访问序列,查页表等操作判别是否缺页,按照FIFO、LRU两种算法淘汰页面,并调入所访问的页面,打印输入、输出结果,在程序中,0代表为空,*代表缺页,并计算缺页率。
FIFO算法:FIFO( First In First Out)简单说就是指先进先出,是内存管理的一种页面置换算法。按照页面装进内存的时间进行置换,老的页面最先被换出,不管该页面是否经常使用,这样就有可能导致缺页率增加,导致页面置换次数增加。
LRU是Least Recently Used 近期最少使用算法。该算法按照上次使用时间进行排序,将离上次使用时间最长的页面换出。可以采用栈的数据结构,每次页面被访问将该页面号放在栈顶。也可使用移位寄存器实现:设置引用位R,每次调用将R=1,系统每个一段时间将R=0,当进行置换式检查哪个页面为零说明近期不会再使用,可以将其换出。
五、 设计实现
FIFO流程图:
LRU流程图:
#include <iostream>
#include<set>
#include <algorithm>
#include<cstdio>
#include <windows.h>
#include <conio.h>
#define N 200
using namespace std;
int page[N];//页面引用号
int block[N];//物理块
int dist[N][N];//表示第i次访问内存的时候,内存中的页面j 在以后被访问的最小时间
int pnum;//页面号数
int bnum;//物理块数
int page_max;//最大页面号
int pre[N];//page[i]在page中的索引
set<int>page_set;
int fifo() {//先进先出页面置换算法
int page_lack = 0;
memset(block, -1, sizeof(block));
int index = 1;
cout<<"fifo算法开始"<<endl<<endl;
for (int i = 1; i <= pnum; ++i) {
if (index > bnum) index = 1; //开始下一轮循环
set<int>::iterator it;
it = page_set.find(page[i]);
cout << "第"<<i<<"次访问,页为"<<page[i]<< endl;
if (it == page_set.end()) //没有找到page[i]项
{
cout << "页面" << page[i] << "不在表中,放入页号为" << index << "的表项中" << endl;
if (block[index] != -1) //如果物理块不为空,则先清除,再替换
page_set.erase(block[index]);
page_set.insert(page[i]);
block[index++] = page[i];
++page_lack;
}
else
{
cout << "已存在并查到第" << page[i] << "页" << endl;
}
cout << "显示当前页面分配情况" << endl;
cout<<"| ";
for (int k = 1; k <= bnum; ++k)
{
if (block[k] != -1)
{
if (k == bnum)
{
cout << block[bnum] << " |" << endl << endl;
}
else
{
cout << block[k] << " | ";
}
}
else
{
if (k == bnum)
{
cout << " | " << endl << endl;
}
else
{
cout << " | ";
}
}
}
}
printf("缺页次数:%d 缺页率:%.2f 命中率:%.2f\n", page_lack, (float)page_lack / pnum, 1.0 - (float)page_lack / pnum);
return page_lack;
}
int lru() { //最近最久未使用页面淘汰法
int page_lack = 0;
memset(pre, 0, sizeof(pre));
memset(dist, 0x3f, sizeof(dist));
memset(block, -1, sizeof(block));
for (int i = 1; i <= pnum; ++i) {
for (int j = 0; j <= page_max; ++j)
if (pre[j])
dist[i][j] = i - pre[j];//当前轮次数减去上次存在的轮次
pre[page[i]] = i;//存在i的当前轮次
}
for (int i = 1; i <= pnum; ++i) {
for (int j = 0; j <= page_max; ++j)
cout << "dist["<<i<<"]["<<j<<"]" << dist[i][j]<<endl;
}
for (int i = 1; i <= pnum; ++i)
{
cout << "pre["<<i<<"]" << pre[i]<<endl;
}
cout << "lru算法开始" << endl << endl;
for (int i = 1; i <= pnum; ++i) {//开始访问页面,初始是内存中没有分页
int j;
int max_dist = 0, p;
for (j = 1; j <= bnum; ++j) //所有物理块均为空,将前bnum个页面填入表。改块没有放入页面,则直接放入, 产生缺页
{
if (block[j] == -1) {
block[j] = page[i];
cout << "页面" << page[i] << "表中,放入页号为" << j << "的表项中" << endl;
page_lack++;
break;
}
else if (block[j] == page[i])//页面存在内存中
{
cout << endl;
cout << "已存在并查到第" << page[i] << "页在第" << j << "块" << endl;
break;
}
if (max_dist < dist[i][block[j]]) {
max_dist = dist[i][block[j]];//说明block[j] 对应的页面以后会长时间不会用到
p = j;//block[] 第j个页面会被替换掉
}
}
if (j > bnum) {//此时内存中不能在放入新的分页,而且没有找到page[i]对应的分页,接下来进行页面替换
cout << "页面" << page[i] << "表中,放入页号为" << p << "的表项中" << endl;
block[p] = page[i];
page_lack++;
}
cout << endl << "当前内存中页面的情况:" << endl;
for (int k = 1; k <= bnum; ++k)
{
if (block[k] != -1)
{
if (k == bnum)
{
cout << block[bnum] << " |" << endl << endl;
}
else
{
cout << block[k] << " | ";
}
}
else
{
if (k == bnum)
{
cout << " | " << endl;
}
else
{
cout << " | ";
}
}
}
}
printf("缺页次数:%d 缺页率:%.2f 命中率:%.2f\n", page_lack, (float)page_lack / pnum, 1.0 - (float)page_lack / pnum);
return page_lack;//返回缺页次数
}
int main() {
int c;
cout << "--------------页面置换算法-------------" << endl;
cout << "---------------页面个数:-------------" << endl;
cin >> pnum;
cout << "--------------物理块数目:------------" << endl;
cin >> bnum;
cout << "-------请输入" << pnum << "个页面号:---------" << endl;
for (int i = 1; i <= pnum; ++i) {
cin >> page[i];
page_max = max(page_max, page[i]);
}
while (true)
{
cout << "1:FIFO先进先出算法" << endl; //先进先出 算法
cout << "2:LRU最近最久未使用页面淘汰算法" << endl; //最近最久未使用页面淘汰 算法
cout << "0:结束" << endl;
cout << "请选择页面置换算法:" << endl;
cin>>c;
switch (c)
{
case 1:
cout << "缺页中断次数:" << fifo() << endl;
printf("按任意键继续\n");
system("PAUSE");
if(!_kbhit())
{
system("cls");
}
break;
case 2:
cout << "缺页中断次数:" << lru() << endl;
printf("按任意键继续\n");
system("PAUSE");
if (!_kbhit())
{
system("cls");
}
break;
case 0:
exit(0);
default:
system("cls");
cout << "没有与输入相匹配的操作,请重新输入" << endl;
}
}
return 0;
}
六、 实验结果
7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 1 7 0 1
FIFO算法开始
第1次访问,页为7
页面7不在表中,放入页号为1的表项中
显示当前页面分配情况
| 7 | | |
第2次访问,页为0
页面0不在表中,放入页号为2的表项中
显示当前页面分配情况
| 7 | 0 | |
第3次访问,页为1
页面1不在表中,放入页号为3的表项中
显示当前页面分配情况
| 7 | 0 | 1 |
第4次访问,页为2
页面2不在表中,放入页号为1的表项中
显示当前页面分配情况
| 2 | 0 | 1 |
第5次访问,页为0
已存在并查到第0页
显示当前页面分配情况
| 2 | 0 | 1 |
第6次访问,页为3
页面3不在表中,放入页号为2的表项中
显示当前页面分配情况
| 2 | 3 | 1 |
第7次访问,页为0
页面0不在表中,放入页号为3的表项中
显示当前页面分配情况
| 2 | 3 | 0 |
第8次访问,页为4
页面4不在表中,放入页号为1的表项中
显示当前页面分配情况
| 4 | 3 | 0 |
第9次访问,页为2
页面2不在表中,放入页号为2的表项中
显示当前页面分配情况
| 4 | 2 | 0 |
第10次访问,页为3
页面3不在表中,放入页号为3的表项中
显示当前页面分配情况
| 4 | 2 | 3 |
第11次访问,页为0
页面0不在表中,放入页号为1的表项中
显示当前页面分配情况
| 0 | 2 | 3 |
第12次访问,页为3
已存在并查到第3页
显示当前页面分配情况
| 0 | 2 | 3 |
第13次访问,页为2
已存在并查到第2页
显示当前页面分配情况
| 0 | 2 | 3 |
第14次访问,页为1
页面1不在表中,放入页号为2的表项中
显示当前页面分配情况
| 0 | 1 | 3 |
第15次访问,页为2
页面2不在表中,放入页号为3的表项中
显示当前页面分配情况
| 0 | 1 | 2 |
第16次访问,页为0
已存在并查到第0页
显示当前页面分配情况
| 0 | 1 | 2 |
第17次访问,页为1
已存在并查到第1页
显示当前页面分配情况
| 0 | 1 | 2 |
第18次访问,页为1
已存在并查到第1页
显示当前页面分配情况
| 0 | 1 | 2 |
第19次访问,页为7
页面7不在表中,放入页号为1的表项中
显示当前页面分配情况
| 7 | 1 | 2 |
第20次访问,页为0
页面0不在表中,放入页号为2的表项中
显示当前页面分配情况
| 7 | 0 | 2 |
第21次访问,页为1
页面1不在表中,放入页号为3的表项中
显示当前页面分配情况
| 7 | 0 | 1 |
缺页次数:15 缺页率:0.71 命中率:0.29
缺页中断次数:15
七、 实验过程中出现的问题及解决办法
页面置换算法是操作系统中与进程调用同样重要的一部分,意义在于能够将使用较频繁的项目保存在页表中,便于快速的执行应用程序等。