实验目的
实现分页式存储管理内存分配和地址转换过程。进一步实现请求分页式存储管理过程,包括内存和置换空间管理、地址转换以及缺页处理,能够体现 FIFO 和 LRU 算法思想。
实验提示:
1、 建立一个位示图数据结构,用来模拟内存的使用情况。位示图是利用若干位的0/1 值代表某类空间的占用状态的数据结构。在本实验中,位示图的位数与设定的物理块个数相同。程序启动时可利用一组随机 0 或 1 填充位示图,以模拟程序开始执行是内存已被占用状态。
2、随机填充后的位示图可能的值如图 2-1 所示。该位示图表示内存的 2(0 字节第 2 位)、3(0 字节第 3 位)、6(0 字节第 6 位)、8(1 字节第 0 位)、9(1字节第 1 位)、12(1 字节第 4 位)、15(1 字节第 7 位)…等块没有被占用。
位示图中空闲状态的位置编号就是所谓的物理地址
下面来说明一下数据结构的使用和具体算法流程思想
由于这次试验更偏向于内存地址转换的知识,所以用一个结构体数组来表示我们声明的页表,其中有两个元素,分别表示物理块号和状态位;
typedef struct Pagetable
{
int physicalblock_No;//物理块号
int state;//状态位
}PT;
程序开始时需要申请空间,也就是位示图,位示图的声明方式我是使用的方法是基于生成随机数的方法进行声明。首先一个位示图有八个字节,每个字节有八个位,所以用一个8*8的数组来存储位示图。
int bit_map[8][8];//位示图
初始化位示图时将前两行全初始化成1来模拟系统的存储区,程序区使用其余的6个字节
随机生成策略:每个字节正好是8位,所以八位2进制数正好能表示十进制的128.因此,我通过随机生成1-128的十进制数然后通过基于循环的二进制转换来初始化位示图。代码如下:
void Inite()//初始化位示图
{
int h = 2;
for (int i = 0; i < 2; i++)//给最开始的前2行赋值为1,系统区
{
for (int j = 0; j < 8; j++)
{
bit_map[i][j] = 1;
}
}
do {
int a[32];
int i = 0;
long num;
//srand((int )time(0));
//srand((unsigned)time(NULL));
//num = rand() % (128 - 64 + 1) + 64;
//srand(1);
num = rand() % 128;//2进制对应八位的数,正好128
while (num >= 1)//算出2进制数
{
a[i] = num % 2;
num = num / 2;
i++;
}
//不足8位在前面补0
if (i < 8)
{
for (int j = i; j <= 7; j++)
{
a[j] = 0;
}
}
//依次把位示图后面几行弄上
for (i = 7; i >= 0; i--)
{
bit_map[h][i] = a[i];
}
h++;
} while (h < 8);
//输出位示图
/*for (int h = 0; h < 8; h++)//行
{
for (int l = 0; l < 8; l++)//列
{
cout << m[h][l];
}
cout << endl;
}*/
}
1、初始化完位示图之后我们需要确定页面大小,页表长度,内存块数,这些都是通过用户输入完成的。输入完成后,将页表的状态位变成0;
2、在程序中输入逻辑地址。通过用逻辑地址/页面大小来算出来页号。
3、将页号和页表长度进行判断。观察是否越界。如果发生越界就重新输入,如果没有越界就进行下一步进程进栈的操作
do {
COUNT++;
//-----------------------------------判断是否越界----------------------------
do
{
a = 0;//a是一个标记,=1的时候就是越界
cout << "\t\t\t\t\t************************************" << endl;
cout << "\t\t\t\t\t* 输入逻辑地址 *" << endl;
cout << "\t\t\t\t\t************************************" << endl;
count = count + 1;
cin >> hex >> logic_add;//这里是以16进制的方式输入的地址
PageNo = logic_add / (1024 * page_size);//页号=逻辑地址/长度
if (PageNo >= pagetable_len)//如果页号大于页表长度
{
a = 1;//a是一个标记,=1的时候就是越界
cout << "\t\t\t\t\t************************************" << endl;
cout << "\t\t\t\t\t* 越界中断请重新输入 *" << endl;
cout << "\t\t\t\t\t************************************" << endl;
}
} while (a == 1);
//----------------------------------------------------------------------------------
//cout << "页号" << PageNo << endl;//输出计算出来的页号
//opt[h] = PageNo;//把这个页号存起来
//h++;
PageAdd = logic_add % (1024 * page_size);//页内地址,就是16位二进制数的后十位
cout << "页内地址" << hex << PageAdd << endl;
cout << "\t\t\t\t\t************************************" << endl;
cout << "\t\t\t\t\t| |" << endl;
cout << "\t\t\t\t\t| 页号:"<< PageNo<<" |" << endl;
cout << "\t\t\t\t\t| |" << endl;
cout << "\t\t\t\t\t| 页内地址:"<< hex << PageAdd<<" |" << endl;
cout << "\t\t\t\t\t| |" << endl;
cout << "\t\t\t\t\t************************************\n\n\n" << endl;
/*if(pt[PageNo].state==0)
{
cout<<"此页不在内存!正在查找是否有剩余物理块......"<<endl;
}*/
有关进程进栈:
1、首先判断内存栈是否满了,如果没有满,就正常的进行入栈
2、如果进程栈已经满了,就要考虑命中和置换操作。
3、在此期间,记录缺页次数,以便于程序最后输出缺页率。
if (stack.size() == physical_block)//栈满了(栈的大小等于内存块数)
{
//cout << "FIFO" << endl;
FIFO(PageNo);
//cout << "LRU" << endl;
LRU(PageNo);
}
else//栈没满
{
int n = 0;
for (int i = 0; i < stack.size(); i++)
{
if (stack[i] == PageNo)
{
cout << "\t\t\t<--命中-->" << endl;
cout << "\t\t\t|物理地址" << endl << hex << pt[PageNo].physicalblock_No * 1024 + PageAdd<<"|";
n = 1;//命中了的标记
break;
}
}
if (n == 0)//如果没有命中
{
cout << "\t\t\t<---内存有剩余,可以从位示图中分配内存(物理块号)--->" << endl;
int local = call_in();//调入页面
pt[PageNo].physicalblock_No = local;//记录当前位示图可以分配的位置的序号
//cout << local;
cout << "\t\t\t|物理地址" ;
cout << hex << local * page_size * 1024 + PageAdd << "|"<<endl;//物理地址=物理块号*长度+页内地址--
//------------------------把页号放进栈中----------------------
stack.push_back(PageNo);
stack2.push_back(PageNo);
//stack3.push_back(PageNo);
//-------------------------------------------------------
//-------------------------进行输出-----------------------------
//cout << "FIFO" << endl;
//ShowFIFO();
//cout << "LRU" << endl;
//ShowLRU();
//cout << "最佳置换" << endl;
//showstack3();
//----------------------------------------------------------------
//cout << "***********" << pt[PageNo].physicalblock_No;
//cout << local;
pt[PageNo].physicalblock_No = local;
pt[PageNo].state = 1;//改变状态位
//pt3[PageNo].physicalblock_No3 = local;//记录物理块号,即位示图可以分配的位置的序号
//pt3[PageNo].state3 = 1;//改变状态位
}
}
cout << "\t\t\t\n\n\n\t\t\t<---是否继续输入 1是 2否---->\n\n\n\n\n" << endl;
cin >> i;
} while (i != 2);
}
有关置换和命中
1、关于置换操作其实比较简单,对于LRU 和FIFO 两种置换算法来说,置换操作都是将栈底的进程移出栈,将新的进程压入栈。同时新入栈的进程的物理块号就是刚才删除的进程的物理块号
2、关于命中操作,对于LRU的命中操作为:如果命中,就将命中的块删除,同时将新块进入,在直观的体现就是命中的块跑到了栈的最上方。对于FIFO操作:将无需进行栈的变化。只需要转一下地址。
FIFO
void FIFO(int page_no)//传进来的参数是页号
{
int a = 0;//a是一个标记,等0代表未命中,=1代表命中
for (vector<int>::iterator it = stack.begin(); it != stack.end(); ++it)//在栈中顺序查找
{
if (page_no == *it)//如果找到了这个页号,即命中了
{
cout << "\t\t\t\t<--命中(FIFO)-->" << endl;//提示命中
cout << "\t\t\t物理地址: |---" << hex << pt[page_no].physicalblock_No * 1024 * page_size + PageAdd <<"---|"<< endl;
a = 1;
break;
}
}
if (a == 0)//如果没有命中。发生了置换
{
fifo++;//置换次数记录+1
vector<int>::iterator it = stack.begin();//返回的迭代器指向第一个元素
pt[page_no].physicalblock_No = pt[*it].physicalblock_No; //将腾出来的物理块号给新来的
//cout << pt[*it].physicalblock_No;
cout << "\t\t\t\t<--置换(FIFO)-->" << endl;
cout << "\t\t\t物理地址:";
cout <<"|---" << hex << pt[page_no].physicalblock_No * page_size * 1024 + PageAdd << "---|"<<endl;//输出物理地址
stack.erase(it);//删除栈中最下面的那个元素
stack.push_back(page_no);//在栈的尾部插入新的进程
pt[page_no].state = 0;//标记位置0
}
//ShowFIFO();
}
LRU
void LRU(int page_no)
{
int a = 0;
vector<int>::iterator it = stack2.begin();
for (it = stack2.begin(); it != stack2.end(); ++it)//遍历整个内存块
{
if (page_no == *it)//如果找到页号相同的,也就是命中的话
{
cout << "\t\t\t\t<--命中(LRU)-->" << endl;//提示命中;
cout << "\t\t\t|---物理地址:" << hex << pt[page_no].physicalblock_No * 1024 * page_size + PageAdd << "---|"<<endl;
//vector<int>::iterator it = stack2.begin(); //返回的迭代器指向第一个元素
stack2.erase(it);//删掉这个东西
stack2.push_back(page_no);//在栈的尾部插入新的进程
a = 1;//标志,代表命中
break;
}
}
if (a == 0)//如果没有命中
{
//------------------------------操作和fifo一样的--------------------------------
lru++;
vector<int>::iterator it = stack2.begin();
pt[page_no].physicalblock_No = pt[*it].physicalblock_No;
cout << "\t\t\t\t<--置换(LRU)-->" << endl;//提示命中;
cout << "\t\t\t|---物理地址:|---";
cout << hex << pt[page_no].physicalblock_No * page_size * 1024 + PageAdd <<"---|"<< endl;
stack2.erase(it);
stack2.push_back(page_no);
}
//ShowLRU();
}
最后附上完整代码
#include<iostream>
#include<vector>
#include<string>
#include<time.h>
using namespace std;
typedef struct Pagetable
{
int physicalblock_No;//物理块号
int state;//状态位
}PT;
int fifo;
int lru;
PT pt[100];
int COUNT;
vector<int> stack;//FIFO栈
vector<int> stack2;//LRU栈
int physical_block;//内存块数
int page_size;//页大小
int pagetable_len;//页表长度
int bit_map[8][8];//位示图
int PageAdd;//页内地址
void Inite();//初始化位示图
void Showbit_map();//显示位视图
void showqyl();//显示缺页率
int call_in();//调入页面
void LRU(int page_no);//
void FIFO(int page_no);//
void ShowFIFO();//显示栈
void ShowLRU();//LRU
int w;
void menu()
{
int funny = 1;
while(funny)
{
cout << "\t\t\t\t\t*******************************************************" << endl;
cout << "\t\t\t\t\t* 请输入START进行本程序的初始化以开始使用 *" << endl;
cout << "\t\t\t\t\t*******************************************************" << endl;
char a[10];
cin >> a;
if (strcmp(a, "START") == 0)
{
Inite();//初始化位示图
funny--;
cout << "\t\t\t\t\t************************************" << endl;
cout << "\t\t\t\t\t* 输入所分配的内存块数 *" << endl;
cout << "\t\t\t\t\t************************************" << endl;
cin >> physical_block;//内存块数
cout << "\t\t\t\t\t************************************" << endl;
cout << "\t\t\t\t\t* 输入页面大小(kb) *" << endl;
cout << "\t\t\t\t\t************************************" << endl;
cin >> page_size;//页面大小
cout << "\t\t\t\t\t************************************" << endl;
cout << "\t\t\t\t\t* 输入页表长度 *" << endl;
cout << "\t\t\t\t\t************************************" << endl;
cin >> pagetable_len;//页表长度
int i;
for (int i = 0; i < pagetable_len; i++)//最开始页表的状态位都是0
{
pt[i].state = 0;//状态位是0
}
}
cout << "\t\t\t位示图初始化完成!" << endl;
}
while (true)
{
cout << "\n\n\n\n" << "\t\t\t _________________________________________" << endl << "\t\t\t ***||实验二:分页式存储管理 ||***" << endl << "\t\t\t _________________________________________" << endl;
cout << "\t\t\t******************************************************" << endl;
cout << "\t\t\t* 输入序号以开始使用 *" << endl;
cout << "\t\t\t* *" << endl;
cout << "\t\t\t* (1)创建一个进程 *" << endl;
cout << "\t\t\t* *" << endl;
cout << "\t\t\t* (2)显示FIFO进程页表 *" << endl;
cout << "\t\t\t* *" << endl;
cout << "\t\t\t* (3)显示LRU进程页表 *" << endl;
cout << "\t\t\t* *" << endl;
cout << "\t\t\t* (4)查看缺页率 *" << endl;
cout << "\t\t\t* *" << endl;
cout << "\t\t\t* (5)显示位示图 *" << endl;
cout << "\t\t\t* *" << endl;
cout << "\t\t\t******************************************************" << endl;
cout <<"\n\n\n\n\n\n" << endl;
int ff;
cin >> ff;
if (ff == 1)
{
int i;
int logic_add;//逻辑地址
int PageNo;//页号
int count = 0;
int a;//a是一个标记,=1的时候就是越界
do {
COUNT++;
//-----------------------------------判断是否越界----------------------------
do
{
a = 0;//a是一个标记,=1的时候就是越界
cout << "\t\t\t\t\t************************************" << endl;
cout << "\t\t\t\t\t* 输入逻辑地址 *" << endl;
cout << "\t\t\t\t\t************************************" << endl;
count = count + 1;
cin >> hex >> logic_add;//这里是以16进制的方式输入的地址
PageNo = logic_add / (1024 * page_size);//页号=逻辑地址/长度
if (PageNo >= pagetable_len)//如果页号大于页表长度
{
a = 1;//a是一个标记,=1的时候就是越界
cout << "\t\t\t\t\t************************************" << endl;
cout << "\t\t\t\t\t* 越界中断请重新输入 *" << endl;
cout << "\t\t\t\t\t************************************" << endl;
}
} while (a == 1);
//----------------------------------------------------------------------------------
//cout << "页号" << PageNo << endl;//输出计算出来的页号
//opt[h] = PageNo;//把这个页号存起来
//h++;
PageAdd = logic_add % (1024 * page_size);//页内地址,就是16位二进制数的后十位
cout << "页内地址" << hex << PageAdd << endl;
cout << "\t\t\t\t\t************************************" << endl;
cout << "\t\t\t\t\t| |" << endl;
cout << "\t\t\t\t\t| 页号:"<< PageNo<<" |" << endl;
cout << "\t\t\t\t\t| |" << endl;
cout << "\t\t\t\t\t| 页内地址:"<< hex << PageAdd<<" |" << endl;
cout << "\t\t\t\t\t| |" << endl;
cout << "\t\t\t\t\t************************************\n\n\n" << endl;
/*if(pt[PageNo].state==0)
{
cout<<"此页不在内存!正在查找是否有剩余物理块......"<<endl;
}*/
if (stack.size() == physical_block)//栈满了(栈的大小等于内存块数)
{
//cout << "FIFO" << endl;
FIFO(PageNo);
//cout << "LRU" << endl;
LRU(PageNo);
}
else//栈没满
{
int n = 0;
for (int i = 0; i < stack.size(); i++)
{
if (stack[i] == PageNo)
{
cout << "\t\t\t<--命中-->" << endl;
cout << "\t\t\t|物理地址" << endl << hex << pt[PageNo].physicalblock_No * 1024 + PageAdd<<"|";
n = 1;//命中了的标记
break;
}
}
if (n == 0)//如果没有命中
{
cout << "\t\t\t<---内存有剩余,可以从位示图中分配内存(物理块号)--->" << endl;
int local = call_in();//调入页面
pt[PageNo].physicalblock_No = local;//记录当前位示图可以分配的位置的序号
//cout << local;
cout << "\t\t\t|物理地址" ;
cout << hex << local * page_size * 1024 + PageAdd << "|"<<endl;//物理地址=物理块号*长度+页内地址--
//------------------------把页号放进栈中----------------------
stack.push_back(PageNo);
stack2.push_back(PageNo);
//stack3.push_back(PageNo);
//-------------------------------------------------------
//-------------------------进行输出-----------------------------
//cout << "FIFO" << endl;
//ShowFIFO();
//cout << "LRU" << endl;
//ShowLRU();
//cout << "最佳置换" << endl;
//showstack3();
//----------------------------------------------------------------
//cout << "***********" << pt[PageNo].physicalblock_No;
//cout << local;
pt[PageNo].physicalblock_No = local;
pt[PageNo].state = 1;//改变状态位
//pt3[PageNo].physicalblock_No3 = local;//记录物理块号,即位示图可以分配的位置的序号
//pt3[PageNo].state3 = 1;//改变状态位
}
}
cout << "\t\t\t\n\n\n\t\t\t<---是否继续输入 1是 2否---->\n\n\n\n\n" << endl;
cin >> i;
} while (i != 2);
}
if(ff==2)
{
ShowFIFO();//显示FIFO
}
if (ff==3)
{
ShowLRU();//显示LRU
}
if (ff == 4)
{
showqyl();//显示缺页率
}
if (ff==5)
{
Showbit_map();//显示位示图
}
}
}
void main()
{
menu();
system("pause");
}
void showqyl()//计算缺页率
{
cout << "\t\t\t\t|FIFO" << "缺页率:" << dec << fifo + physical_block << "/" << dec << COUNT << endl;
cout << "\t\t\t\t\|LRU" << "缺页率:" << dec << lru + physical_block << "/" << dec << COUNT << endl;
}
int call_in()//位视图是8*8的,找到可以分配的位示图的位置的序号
{
int loc;
for (int h = 2; h < 8; h++)
for (int lie = 0; lie < 8; lie++)
{
if (bit_map[h][lie] == 0)
{
loc = (((8 * h) + lie) + 1);
cout << "\t\t\t<---"<<dec << loc << "号内存可以分配--->" << endl;
bit_map[h][lie] = 1;//把可以分配的位示图的位置改成1
h = 8;
break;
}
}
//Showbit_map();//输出位示图
return loc;
}
void Inite()//初始化位示图
{
int h = 2;
for (int i = 0; i < 2; i++)//给最开始的前2行赋值为1,系统区
{
for (int j = 0; j < 8; j++)
{
bit_map[i][j] = 1;
}
}
do {
int a[32];
int i = 0;
long num;
//srand((int )time(0));
//srand((unsigned)time(NULL));
//num = rand() % (128 - 64 + 1) + 64;
//srand(1);
num = rand() % 128;//2进制对应八位的数,正好128
while (num >= 1)//算出2进制数
{
a[i] = num % 2;
num = num / 2;
i++;
}
//不足8位在前面补0
if (i < 8)
{
for (int j = i; j <= 7; j++)
{
a[j] = 0;
}
}
//依次把位示图后面几行弄上
for (i = 7; i >= 0; i--)
{
bit_map[h][i] = a[i];
}
h++;
} while (h < 8);
//输出位示图
/*for (int h = 0; h < 8; h++)//行
{
for (int l = 0; l < 8; l++)//列
{
cout << m[h][l];
}
cout << endl;
}*/
}
void FIFO(int page_no)//传进来的参数是页号
{
int a = 0;//a是一个标记,等0代表未命中,=1代表命中
for (vector<int>::iterator it = stack.begin(); it != stack.end(); ++it)//在栈中顺序查找
{
if (page_no == *it)//如果找到了这个页号,即命中了
{
cout << "\t\t\t\t<--命中(FIFO)-->" << endl;//提示命中
cout << "\t\t\t物理地址: |---" << hex << pt[page_no].physicalblock_No * 1024 * page_size + PageAdd <<"---|"<< endl;
a = 1;
break;
}
}
if (a == 0)//如果没有命中。发生了置换
{
fifo++;//置换次数记录+1
vector<int>::iterator it = stack.begin();//返回的迭代器指向第一个元素
pt[page_no].physicalblock_No = pt[*it].physicalblock_No; //将腾出来的物理块号给新来的
//cout << pt[*it].physicalblock_No;
cout << "\t\t\t\t<--置换(FIFO)-->" << endl;
cout << "\t\t\t物理地址:";
cout <<"|---" << hex << pt[page_no].physicalblock_No * page_size * 1024 + PageAdd << "---|"<<endl;//输出物理地址
stack.erase(it);//删除栈中最下面的那个元素
stack.push_back(page_no);//在栈的尾部插入新的进程
pt[page_no].state = 0;//标记位置0
}
//ShowFIFO();
}
void ShowFIFO()//显示FIFO的状态
{
cout << "\n\n\t\t\t FIFO方式下的内存状态如下" << endl;
for (int i = stack.size() - 1; i >= 0; i--)
{
cout << "\t\t\t\t| "<<stack[i] <<" |"<< endl;
}
cout << endl;
}
void ShowLRU()//显示LRU的状态
{
cout << "\n\n\t\t\t LRU方式下的内存状态如下" << endl;
for (int i = stack2.size() - 1; i >= 0; i--)
{
cout << "\t\t\t\t| "<<stack2[i]<<" |" << endl;
}
cout << endl;
}
void LRU(int page_no)
{
int a = 0;
vector<int>::iterator it = stack2.begin();
for (it = stack2.begin(); it != stack2.end(); ++it)//遍历整个内存块
{
if (page_no == *it)//如果找到页号相同的,也就是命中的话
{
cout << "\t\t\t\t<--命中(LRU)-->" << endl;//提示命中;
cout << "\t\t\t|---物理地址:" << hex << pt[page_no].physicalblock_No * 1024 * page_size + PageAdd << "---|"<<endl;
//vector<int>::iterator it = stack2.begin(); //返回的迭代器指向第一个元素
stack2.erase(it);//删掉这个东西
stack2.push_back(page_no);//在栈的尾部插入新的进程
a = 1;//标志,代表命中
break;
}
}
if (a == 0)//如果没有命中
{
//------------------------------操作和fifo一样的--------------------------------
lru++;
vector<int>::iterator it = stack2.begin();
pt[page_no].physicalblock_No = pt[*it].physicalblock_No;
cout << "\t\t\t\t<--置换(LRU)-->" << endl;//提示命中;
cout << "\t\t\t|---物理地址:|---";
cout << hex << pt[page_no].physicalblock_No * page_size * 1024 + PageAdd <<"---|"<< endl;
stack2.erase(it);
stack2.push_back(page_no);
}
//ShowLRU();
}
void Showbit_map()//显示位示图
{
for (int h = 0; h < 8; h++)//行
{
for (int l = 0; l < 8; l++)//列
{
cout << bit_map[h][l];
}
cout << endl;
}
}
总结
本实验主要运用了栈的操作和各种地址变化方式,编程时需要注意物理地址的传递和控制好内存和页表的范围,防止越界。在位示图的生成上利用随机数,学习了新知识。了解了十六进制,十进制,二进制之间的转换。在LRU和FIFO的置换,命中等算法时要多加留意这两种算法置换策略的不同,进而修改栈中内容的指向。
希望这篇文章可以帮助到大家
如果对您有帮助,给我点个赞吧。













网友评论