理解并掌握主要页面淘汰算法的设计和实现要旨。
利用标准 C 语言,编程设计与实现最佳淘汰算法、先进先出淘汰算法、最近最久未使用淘汰算法、简单 Clock 淘汰算法及改进型 Clock 淘汰算法,并随机发生页面访问序列开展有关算法的测试及性能比较。
本实验课题功能设计要求如下:
关于页面访问序列随机发生机制的设计思想:
假设虚拟内存的地址是16位,页面大小为1K,则进程的大小为 2 16 = 64 K B 2^{16}=64KB 216=64KB
分 页 个 数 = 进 程 的 大 小 页 面 的 大 小 = 逻 辑 地 址 表 示 的 大 小 页 面 的 大 小 = 2 16 B 1 K B = 2 6 页 分页个数=\frac{进程的大小}{页面的大小}=\frac{逻辑地址表示的大小}{页面的大小}=\frac{2^{16B}}{1KB}=2^{6}页 分页个数=页面的大小进程的大小=页面的大小逻辑地址表示的大小=1KB216B=26页
因此页号P占用前6位,页内偏移量W=16-6=10位。每一个页表项占用2B,因此页表存储空间需要 2 6 ∗ 2 B = 128 B 2^6*2B=128B 26∗2B=128B
页面结构以及页表项设计如下:
struct PageInfo //页面信息结构
{
int pages[MAX]; // 模拟的最大访问页面数
int isVisited; // 标志位,0表示无页面访问数据
int page_missing_num; // 缺页中断次数
int allocated_page_num; // 分配的页框数
int visit_list_length; // 访问页面序列长度
} pInfo;
struct MemInfo // 页表项信息
{
int time; //记录页框中数据的访问次数
int isVisit;//访问位
int isModify;//修改位
int pages;//页号
}mInfo;
MemInfo pageList[MAX]; // 分配的页框
int page_loss_num = 0; // 缺页次数
int current_page; //页面访问指针
int replace_page; //页面替换指针
int is_loss_page; //缺页标志:1->缺页,0->命中
Init
初始化void Init()
{
int i, pn;
is_loss_page = 0; //缺页标志,0为不缺页,1为缺页
pInfo.page_missing_num = 0; // 缺页次数
pInfo.isVisited = 0; // 标志位,0表示无页面访问数据
printf("请输入分配的页框数:"); // 自定义分配的页框数
scanf("%d", &pn);
pInfo.allocated_page_num = pn;
for (i = 0; i < MAX; i++) // 清空页面序列
{
pInfo.pages[i] = -1;
}
}
getRandomList
随机生成访问序列算法流程如下:
具体实现如下:
void getRandomList(int m, int e, int s)
{
int pl;
Init(); //初始化
printf("请输入访问序列的长度:");
scanf("%d", &pl); //随机生成访问序列的长度
pInfo.visit_list_length = pl;
srand((unsigned)time(NULL)); // 随机种子
int p = rand() % MAX; //在0——MAX范围内随机初始化工作集的起始位置p
int i, j=0;
double t= rand() % 10 / 10.0; // 一个范围在0和1之间的值t
for (i = 0; i < s; i++) // 重复s次
{
if (j > pInfo.visit_list_length) // 不能超过访问序列的长度
break;
for (j = i * m; j < (i + 1) * m; j++)//生成m个取值范围在p~p + e间的随机数
{
pInfo.pages[j]= (p + (rand() % e)) % MAX; //并记录到页面访问序列串中;
}
double r = (rand() % 10) / 10.0;// 生成一个范围在0-1之间的随机数r
if (r < t) // 如果r < t,则为p生成一个新值
p = rand() % MAX;
else
{
p = (p + 1) % MAX; //否则向后循环移位
}
}
}
showState
跟踪显示状态信息算法流程如下:
current_page
=0, 则说明当前页面指针处于起始位置,则首先遍历pInfo.pages[i]
数组,打印页面的访问序列。其具体格式为每行显示10个等访问的页面。is_loss_page
判断是否发生中断。如果发生中断,则用缺页数/已访问的页面数计算得到缺页率并显示给用户。具体实现如下:
void showState(void)
{
int i, n;
if (current_page == 0)
{
printf("\n~~~~~~~~~~~~~~~~~页面访问序列~~~~~~~~~~~~~~~~~\n");
for (i = 0; i < pInfo.visit_list_length; i++)
{
printf("%4d", pInfo.pages[i]);
if ((i + 1) % 10 == 0) printf("\n"); //每行显示10个
}
printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
}
printf("访问%3d -->内存空间[", pInfo.pages[current_page]); // 访问当前页面信息
for (n = 0; n < pInfo.allocated_page_num; n++) // 页框信息
{
if (pageList[n].pages >= 0)
printf("%3d", pageList[n].pages);
else
printf(" ");
}
printf(" ]");
if (is_loss_page == 1) //缺页标志,0为不缺页,1为缺页
{
printf(" --> 缺页中断 --> ");
if (current_page == 0)
{
printf("缺页率 = %3.1f", (float)(pInfo.page_missing_num) * 100.00 );
}
else{
printf("缺页率 = %3.1f", (float)(pInfo.page_missing_num) * 100.00 / current_page); }
}
printf("\n");
}
isExist
查找页面是否已经在内存中算法流程如下:
is_loss_page
缺页标志=0(不缺页),同时访问次数time
也置为0,然后通过随机生成的0或者1,来设置该访问页面是否被修改。并返回1。int isExist(int page)
{
int n;
for (n = 0; n < pInfo.allocated_page_num; n++)
pageList[n].time++; // 访问次数加1
for (n = 0; n < pInfo.allocated_page_num; n++)
{
if (pageList[n].pages == page)
{
is_loss_page = 0; //is_loss_page缺页标志=0(不缺页)
pageList[n].time = 0; //置访问次数为0
pageList[n].isVisit = 1; // 访问标志
if (randBool()) {
pageList[n].isModify = 1; // 修改标志
}
return 1;
}
}
is_loss_page = 1; //页面不存在,缺页
return 0;
}
其中调用了randBool()
函数,随机的生成0或者1,其具体实现如下
bool randBool()
{
if ((rand() % 2 + 1) == 1)
return true;
else
return false;
}
算法规则:选择永不使用或是在最长时间内不再被访问(即距现在最长时间才会被访问)的页面淘汰出内存。
假定系统为某进程分配了三个物理块,并考虑有以下页面访问序列:
[7, 0, 1, 2, 0, 3, 0, 4, 2, 3, 0, 3, 2, 1, 2, 0, 1, 7, 0, 1]
进程运行时,先将7, 0, 1三个页面依次装入内存。进程要访问页面2时,产生缺页中断,根据最佳置换算法,选择第18次访问才需调入的页面7予以淘汰。然后,访问页面0时,因为已在内存中所以不必产生缺页中断。访问页面3时又会根据最佳置换算法将页面1淘汰……依此类推。
页面 | 7 | 0 | 1 | 2 | 0 | 3 | 0 | 4 | 2 | 3 | 0 | 3 | 2 | 1 | 2 | 0 | 1 | 7 | 0 | 1 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
页面1 | 7 | 7 | 7 | 2 | 2 | 2 | 2 | 2 | 7 | |||||||||||
页面2 | 0 | 0 | 0 | 0 | 4 | 0 | 0 | 0 | ||||||||||||
页面3 | 1 | 1 | 3 | 3 | 3 | 1 | 1 | |||||||||||||
缺页 | √ | √ | √ | √ | √ | √ | √ | √ |
算法流程图如下:
OPT
算法具体实现如下:
void OPT()
{
int n, full, status;
replace_page = 0; // 页面替换指针初始化为0
page_loss_num = 0; // 缺页数初始化为0
full = 0; // 已经占用的页框数
is_loss_page = 0; //缺页标志,0为不缺页,1为缺页
for (n = 0; n < pInfo.allocated_page_num; n++) // 清除页框信息
pageList[n].pages = -1;
for (current_page = 0; current_page < pInfo.visit_list_length; current_page++) // 遍历所有的访问页面
{
status = isExist(pInfo.pages[current_page]); //查找页面是否在内存
if (full < pInfo.allocated_page_num) // 当已经占用的页框数< 分配的页框数时,则不可能发生缺页
{
if (status == 0) // 页不存在则装入页面
{
pageList[replace_page].pages = pInfo.pages[current_page]; // 当前位置放入新的页面
replace_page = (replace_page + 1) % pInfo.allocated_page_num; //循环后移一位
full++; // 已经装入的页面数+1
}
}
else // 页框已经满,缺页置换
{
if (status == 0) // 页不存在则置换页面
{
int min,max = 0 ; //很大的数
for (int m = 0; m < pInfo.allocated_page_num ; m++) // 遍历页框中以占用的页号
{
min = 1000;
for (int n = current_page; n < pInfo.visit_list_length; n++)// 向后查找访问序列
{
if (pInfo.pages[n] == pageList[m].pages) // 找到在当前内存出现过的页面
{
min = n;
break;
}
}
if (max < min)
{
max = min;
replace_page = m; // 距离当前页面最远的被置换
}
}
pageList[replace_page].pages = pInfo.pages[current_page]; // 置换动作
replace_page = (replace_page + 1) % pInfo.allocated_page_num;
pInfo.page_missing_num++; // 置换次数加1
}
}
Sleep(10);
showState(); // 显示当前状态
} // 置换算法循环结束
_getch();
return;
}
算法规则:在发生页面替换时,被替换的对象应该是最早进入内存的。
仍然采用上述的例子,此时页面置换过程如下表所示
页面 | 7 | 0 | 1 | 2 | 0 | 3 | 0 | 4 | 2 | 3 | 0 | 3 | 2 | 1 | 2 | 0 | 1 | 7 | 0 | 1 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
页面1 | 7 | 7 | 7 | 2 | 2 | 2 | 4 | 4 | 4 | 0 | 0 | 0 | 7 | 7 | 7 | |||||
页面2 | 0 | 0 | 0 | 3 | 3 | 3 | 2 | 2 | 2 | 1 | 1 | 1 | 0 | 0 | ||||||
页面3 | 1 | 1 | 1 | 0 | 0 | 0 | 3 | 3 | 3 | 2 | 2 | 2 | 1 | |||||||
缺页 | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ |
void FIFO(void)
{
int n, full, status;
replace_page = 0; // 页面替换指针初始化为0
page_loss_num = 0; // 缺页数初始化为0
full = 0; // 是否装满是所有的页框
for (n = 0; n < pInfo.allocated_page_num; n++) // 清除页框信息
pageList[n].pages = -1;
is_loss_page = 0; //缺页标志,0为不缺页,1为缺页
for (current_page = 0; current_page < pInfo.visit_list_length; current_page++) // 执行算法
{
status = isExist(pInfo.pages[current_page]); //查找页面是否在内存
if (full < pInfo.allocated_page_num) // 开始时不计算缺页
{
if (status == 0) // 页不存在则装入页面
{
pageList[replace_page].pages = pInfo.pages[current_page];
replace_page = (replace_page + 1) % pInfo.allocated_page_num;
full++;
}
}
else // 正常缺页置换
{
if (status == 0) // 页不存在则置换页面
{
pageList[replace_page].pages = pInfo.pages[current_page];
replace_page = (replace_page + 1) % pInfo.allocated_page_num;
pInfo.page_missing_num++; // 缺页次数加1
}
}
Sleep(10);
showState(); // 显示当前状态
} // 置换算法循环结束
_getch();
return;
}
初始化设置
m=8,e=8,s=10; 页框数=3,访问序列长度=20;
选择算法2:先进先出淘汰算法
算法规则:在发生页面替换时,被替换的页面应该满足,在之前的访问队列中,该对象截止目前未被访问的时间最长。
仍然采用上述的例子,此时页面置换过程如下表所示
页面 | 7 | 0 | 1 | 2 | 0 | 3 | 0 | 4 | 2 | 3 | 0 | 3 | 2 | 1 | 2 | 0 | 1 | 7 | 0 | 1 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
页面1 | 7 | 7 | 7 | 2 | 2 | 4 | 4 | 4 | 0 | 1 | 1 | 1 | ||||||||
页面2 | 0 | 0 | 0 | 0 | 0 | 0 | 3 | 3 | 3 | 0 | 0 | |||||||||
页面3 | 1 | 1 | 3 | 3 | 2 | 2 | 2 | 2 | 2 | 7 | ||||||||||
缺页 | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ |
LRU
页面置换算法具体实现如下:void LRU(void)
{
int n, full, status, max;
replace_page = 0; // 页面替换指针
page_loss_num = 0; // 缺页次数初始化为0
full = 0; // 是否装满所有的页框
for (n = 0; n < pInfo.allocated_page_num; n++)
{
pageList[n].pages = -1; // 清除页框信息
pageList[n].time = 0; // 清除页框历史
}
is_loss_page = 0; //缺页标志,0为不缺页,1为缺页
for (current_page = 0; current_page < pInfo.visit_list_length; current_page++) // 执行算法
{
status = isExist(pInfo.pages[current_page]); //查找页面是否在内存
if (full < pInfo.allocated_page_num) // 开始时不计算缺页
{
if (status == 0) // 页不存在则装入页面
{
pageList[replace_page].pages = pInfo.pages[current_page]; //把要调入的页面放入一个空的页框里
replace_page = (replace_page + 1) % pInfo.allocated_page_num;
full++;
}
}
else //正常缺页置换
{
if (status == 0)//页不存在则置换页面
{
max = 0;
for (n = 1; n < pInfo.allocated_page_num; n++)
{
if (pageList[n].time > pageList[max].time)
{
max = n;
}
}
replace_page = max;
pageList[replace_page].pages = pInfo.pages[current_page];
pageList[replace_page].time = 0;
pInfo.page_missing_num++; // 缺页次数加1
}
}
Sleep(10);
showState(); // 显示当前状态
} // 置换算法循环结束
_getch();
return;
}
初始化设置
m=8,e=8,s=10; 页框数=3,访问序列长度=20;
选择算法3:先进先出淘汰算法
页面置换过程
算法规则:简单的CLOCK算法实现方法:为每个页面设置一个访问位,再将内存中的页面都通过链接指针链接成一个循环队列。当某页被访问时,其访问位置为1。当需要淘汰一个页面时,只需检查页的访问位。如果是0,就选择该页换出;如果是1,则将它置为0,暂不换出,继续检查下一个页面,若第一轮扫描中所有页面都是1,则将这些页面的访问位依次置为0后,再进行第二轮扫描(第二轮扫描中一定会有访问位为0的页面,因此简单的CLOCK算法选择一个淘汰页面最多会经过两轮扫描)
CLOCK_pro
是CLOCK算法的入口参数,其中会传入参数choose。当choose =1时采用改进的clock算法,当choose =0时采用基本的clock算法。
void CLOCK_pro(int choose)
{
int n, full, status;
int num = -1;
replace_page = 0; // 页面替换指针初始化为0
page_loss_num = 0; // 缺页数初始化为0
full = 0; // 是否装满是所有的页框
for (n = 0; n < pInfo.allocated_page_num; n++) // 清除页框信息
{
pageList[n].pages = -1;
pageList[n].isModify = 0;
pageList[n].isVisit = 0;
pageList[n].time = 0;
}
is_loss_page = 0; //缺页标志,0为不缺页,1为缺页
for (current_page = 0; current_page < pInfo.visit_list_length; current_page++) // 执行算法
{
status = isExist(pInfo.pages[current_page]); //查找页面是否在内存
if (full < pInfo.allocated_page_num) // 开始时不计算缺页
{
if (status == 0) // 页不存在则装入页面
{
pageList[replace_page].pages = pInfo.pages[current_page];
replace_page = (replace_page + 1) % pInfo.allocated_page_num;
pageList[n].isVisit = 1;
full++;
}
}
else // 正常缺页置换
{
if (status == 0) // 页不存在则置换页面
{
if(choose==1)
replace_page = replace_page_pro(++num); // 当choose =1时采用改进的clock算法
else if(choose==0)
replace_page = replace_page_clock(++num); // 当choose =0时采用基本的clock算法
pageList[replace_page].pages = pInfo.pages[current_page];
pageList[replace_page].isVisit = 1;
pInfo.page_missing_num++; // 缺页次数加1
}
}
Sleep(10);
showState(); // 显示当前状态
} // 置换算法循环结束
_getch();
return;
}
然后replace_page_clock
实现了基本CLOCK算法的置换策略,具体代码如下:
int replace_page_clock(int num) {
int j;
// 将所有可能被置换的页面排成一个循环队列,用(访问位,修改位)的形式表示各页面状态
// 1.当需要淘汰一个页面时,只需检查页的访问位。如果是0,就选择该页换出;如果是1,则将它置为0,暂不换出,继续检查下一个页面
for (j = 0; j < pInfo.allocated_page_num; j++){
if (pageList[(j + num) % pInfo.allocated_page_num].isVisit == 0 )
return (j+num) % pInfo.allocated_page_num;
pageList[(j + num) % pInfo.allocated_page_num].isVisit = 0;
}
//2. 若第一轮扫描中所有页面都是1,则将这些页面的访问位依次置为0后,再进行第二轮扫描(第二轮扫描中一定会有访问位为0的页面
for (j = 0; j < pInfo.allocated_page_num; j++){
if (pageList[(j + num) % pInfo.allocated_page_num].isVisit == 0 )
return (j + num) % pInfo.allocated_page_num;
}
}
初始化设置
m=8,e=8,s=10; 页框数=3,访问序列长度=20;
选择算法4:基本的CLOCK算法
算法规则:简单的时钟置换算法仅考虑到一个页面最近是否被访问过。事实上,如果被淘汰的页面没有被修改过,就不需要执行I/o操作写回外存。只有被淘汰的页面被修改过时,才需要写回外存。
因此,除了考虑一个页面最近有没有被访问过之外,操作系统还应考虑页面有没有被修改过。在其他条件都相同时,应优先淘汰没有修改过的页面,避免I/o操作。这就是改进型的时钟置换算法的思想。修改位=o,表示页面没有被修改过;修改位=1,表示页面被修改过。为方便讨论,用(访问位,修改位)的形式表示各页面状态。如(1,1)表示一个页面近期被访问过,且被修改过。
将所有可能被置换的页面排成一个循环队列
int replace_page_pro(int num) {
int j;
// 将所有可能被置换的页面排成一个循环队列,用(访问位,修改位)的形式表示各页面状态
// 1.从当前位置开始扫描到第一个(0,0)的帧用于替换。本轮扫描不修改任何标志位
for (j = 0; j < pInfo.allocated_page_num; j++){
if (pageList[(j + num) % pInfo.allocated_page_num].isVisit == 0 && pageList[(j + num) % pInfo.allocated_page_num].isModify == 0)
return (j+num) % pInfo.allocated_page_num;
}
//2. 若第一轮扫描失败,则重新扫描,查找第一个(0,1)的帧用于替换。本轮将所有扫描过的帧访问位设为0
for (j = 0; j < pInfo.allocated_page_num; j++){
if (pageList[(j + num) % pInfo.allocated_page_num].isVisit == 0 && pageList[(j + num) % pInfo.allocated_page_num].isModify == 1)
return (j + num) % pInfo.allocated_page_num;
pageList[(j + num) % pInfo.allocated_page_num].isVisit = 0;
}
// 3.若第二轮扫描失败,则重新扫描,查找第一个(0,0)的帧用于替换。本轮扫描不修改任何标志位
for (j = 0; j < pInfo.allocated_page_num; j++){
if (pageList[(j + num) % pInfo.allocated_page_num].isVisit == 0 && pageList[(j + num) % pInfo.allocated_page_num].isModify == 0)
return (j+num) % pInfo.allocated_page_num;
}
//4. 若第三轮扫描失败,则重新扫描,查找第一个(0,1)的帧用于替换。
for (j = 0; j < pInfo.allocated_page_num; j++){
if (pageList[(j + num) % pInfo.allocated_page_num].isVisit == 0 && pageList[(j + num) % pInfo.allocated_page_num].isModify == 1)
return (j + num) % pInfo.allocated_page_num;
// pageList[(j + num) % pInfo.allocated_page_num].isVisit = 0;
}
}
初始化设置
m=8,e=8,s=10; 页框数=3,访问序列长度=20;
选择算法5:改进的CLOCK算法
页面置换过程
由于上述的生成访问随机序列较小,且只考虑了页框大小为3的情况。因此,需要改变页框数和增大访问序列来对比算法的命中率,并对不同的实验情况的置换率和系统开销求得平均值,统计如下表所示:
算法名称 \ 性能 | 置换率 | 系统开销 |
---|---|---|
OPT | 25.4% | 431ms |
FIFO | 43.1% | 336ms |
LRU | 42.7% | 362ms |
CLOCK | 35.2% | 384ms |
CLOCK_pro | 32.6% | 398ms |
置换率比较
最佳置换算法 < 改进型clock置换算法
系统开销比较
先进先出置换算法 < 最近最久未使用算法 < clock算法<改进型clock置换算法 < 最佳置换算法
通过这次实验我更加熟悉了操作系统页面置换算法的流程,并通过编写代码实现了5种置换算法。通过对比其置换率和系统开销,分析出了不同算法的优缺点和特性。同时也提升了我的编程能力,受益匪浅。
#include "windows.h"
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define MAX 64
void FIFO(); //先进先出算法
void OPT(); //最佳置换算法
void LRU(); //最近最久未使用算法
void CLOCK_pro(int choose); //改进型Clock置换算法
void Init(); //初始化相关数据结构
void getRandomList(int m, int e, int s); //随机生成访问序列
void showState(); //显示当前状态及缺页情况
int isExist(int page); //查找页面是否在内存
bool randBool(); // 随机生成0或1
struct PageInfo //页面信息结构
{
int pages[MAX]; // 模拟的最大访问页面数
int isVisited; // 标志位,0表示无页面访问数据
int page_missing_num; // 缺页中断次数
int allocated_page_num; // 分配的页框数
int visit_list_length; // 访问页面序列长度
} pInfo;
struct MemInfo // 页表项信息
{
int time; //记录页框中数据的访问历史
int isVisit;//访问位
int isModify;//修改位
int pages;//页号,16位信息,前6位代表页号,后10位为偏移地址 0~2^16
}mInfo;
MemInfo pageList[MAX]; // 分配的页框
int page_loss_num = 0; // 缺页次数
int current_page; //页面访问指针
int replace_page; //页面替换指针
int is_loss_page; //缺页标志,0为不缺页,1为缺页
//初始化相关数据结构
void Init()
{
int i, pn;
is_loss_page = 0; //缺页标志,0为不缺页,1为缺页
pInfo.page_missing_num = 0; // 缺页次数
pInfo.isVisited = 0; // 标志位,0表示无页面访问数据
printf("请输入分配的页框数:"); // 自定义分配的页框数
scanf("%d", &pn);
pInfo.allocated_page_num = pn;
for (i = 0; i < MAX; i++) // 清空页面序列
{
pInfo.pages[i] = -1;
}
}
// 随机生成访问序列
void getRandomList(int m, int e, int s)
{
int pl;
Init(); //初始化
printf("请输入访问序列的长度:");
scanf("%d", &pl); //随机生成访问序列的长度
pInfo.visit_list_length = pl;
srand((unsigned)time(NULL)); // 随机种子
int p = rand() % MAX; //在0——MAX范围内随机初始化工作集的起始位置p
int i, j=0;
double t= rand() % 10 / 10.0; // 一个范围在0和1之间的值t
for (i = 0; i < s; i++) // 重复s次
{
if (j > pInfo.visit_list_length) // 不能超过访问序列的长度
break;
for (j = i * m; j < (i + 1) * m; j++)//生成m个取值范围在p~p + e间的随机数
{
pInfo.pages[j]= (p + (rand() % e)) % MAX; //并记录到页面访问序列串中;
}
double r = (rand() % 10) / 10.0;// 生成一个范围在0-1之间的随机数r
if (r < t) // 如果r < t,则为p生成一个新值
p = rand() % MAX;
else
{
p = (p + 1) % MAX; //否则向后循环移位
}
}
}
// 显示当前状态及缺页情况
void showState(void)
{
int i, n;
if (current_page == 0)
{
printf("\n~~~~~~~~~~~~~~~~~页面访问序列~~~~~~~~~~~~~~~~~\n");
for (i = 0; i < pInfo.visit_list_length; i++)
{
printf("%4d", pInfo.pages[i]);
if ((i + 1) % 10 == 0) printf("\n"); //每行显示10个
}
printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
}
printf("访问%3d -->内存空间[", pInfo.pages[current_page]);
for (n = 0; n < pInfo.allocated_page_num; n++) // 页框信息
{
if (pageList[n].pages >= 0)
printf("%3d", pageList[n].pages);
else
printf(" ");
}
printf(" ]");
if (is_loss_page == 1) //缺页标志,0为不缺页,1为缺页
{
printf(" --> 缺页中断 --> ");
if (current_page == 0)
{
printf("置换率 = %3.1f ", (float)(pInfo.page_missing_num) * 100.00 );
}
else{
printf("置换率 = %3.1f", (float)(pInfo.page_missing_num) * 100.00 / current_page);
}
}
printf("\n");
}
// 查找页面是否在内存,1为在内存,0为不在即缺页
int isExist(int page)
{
int n;
for (n = 0; n < pInfo.allocated_page_num; n++)
{
pageList[n].time++; // 访问历史加1
}
for (n = 0; n < pInfo.allocated_page_num; n++)
{
if (pageList[n].pages == page)
{
is_loss_page = 0; //is_loss_page缺页标志,0为不缺页,1为缺页
pageList[n].time = 0; //置访问历史为0
if (randBool()) {
pageList[n].isVisit = 1;
pageList[n].isModify = 1;
}
else {
pageList[n].isVisit = 1;
}
return 1;
}
}
is_loss_page = 1; //页面不存在,缺页
return 0;
}
// FIFO页面置换算法
void FIFO(void)
{
int n, full, status;
replace_page = 0; // 页面替换指针初始化为0
page_loss_num = 0; // 缺页数初始化为0
full = 0; // 是否装满是所有的页框
for (n = 0; n < pInfo.allocated_page_num; n++) // 清除页框信息
pageList[n].pages = -1;
is_loss_page = 0; //缺页标志,0为不缺页,1为缺页
for (current_page = 0; current_page < pInfo.visit_list_length; current_page++) // 执行算法
{
status = isExist(pInfo.pages[current_page]); //查找页面是否在内存
if (full < pInfo.allocated_page_num) // 开始时不计算缺页
{
if (status == 0) // 页不存在则装入页面
{
pageList[replace_page].pages = pInfo.pages[current_page];
replace_page = (replace_page + 1) % pInfo.allocated_page_num;
full++;
}
}
else // 正常缺页置换
{
if (status == 0) // 页不存在则置换页面
{
pageList[replace_page].pages = pInfo.pages[current_page];
replace_page = (replace_page + 1) % pInfo.allocated_page_num;
pInfo.page_missing_num++; // 缺页次数加1
}
}
Sleep(10);
showState(); // 显示当前状态
} // 置换算法循环结束
_getch();
return;
}
// OPT 页面置换算法
void OPT()
{
int n, full, status;
replace_page = 0; // 页面替换指针初始化为0
page_loss_num = 0; // 缺页数初始化为0
full = 0; // 是否装满是所有的页框
for (n = 0; n < pInfo.allocated_page_num; n++) // 清除页框信息
{
pageList[n].pages = -1;
}
is_loss_page = 0; //缺页标志,0为不缺页,1为缺页
for (current_page = 0; current_page < pInfo.visit_list_length; current_page++) // 执行算法
{
status = isExist(pInfo.pages[current_page]); //查找页面是否在内存
if (full < pInfo.allocated_page_num) // 开始时不计算缺页
{
if (status == 0) // 页不存在则装入页面
{
pageList[replace_page].pages = pInfo.pages[current_page];
replace_page = (replace_page + 1) % pInfo.allocated_page_num;
full++;
}
}
else // 正常缺页置换
{
if (status == 0) // 页不存在,则置换页面
{
int min,max = 0 ; //很大的数
for (int m = 0; m < pInfo.allocated_page_num ; m++)
{
min = 1000;
for (int n = current_page; n < pInfo.visit_list_length; n++)
{
if (pInfo.pages[n] == pageList[m].pages)
{
min = n;
break;
}
}
if (max < min)
{
max = min;
replace_page = m;
}
}
pageList[replace_page].pages = pInfo.pages[current_page];
replace_page = (replace_page + 1) % pInfo.allocated_page_num;
pInfo.page_missing_num++; // 缺页次数加1
}
}
Sleep(10);
showState(); // 显示当前状态
} // 置换算法循环结束
_getch();
return;
}
//查找替换页面算法,递归实现
int replace_page_pro(int num) {
int j;
// 将所有可能被置换的页面排成一个循环队列,用(访问位,修改位)的形式表示各页面状态
// 1.从当前位置开始扫描到第一个(0,0)的帧用于替换。本轮扫描不修改任何标志位
for (j = 0; j < pInfo.allocated_page_num; j++){
if (pageList[(j + num) % pInfo.allocated_page_num].isVisit == 0 && pageList[(j + num) % pInfo.allocated_page_num].isModify == 0)
return (j+num) % pInfo.allocated_page_num;
}
//2. 若第一轮扫描失败,则重新扫描,查找第一个(0,1)的帧用于替换。本轮将所有扫描过的帧访问位设为0
for (j = 0; j < pInfo.allocated_page_num; j++){
if (pageList[(j + num) % pInfo.allocated_page_num].isVisit == 0 && pageList[(j + num) % pInfo.allocated_page_num].isModify == 1)
return (j + num) % pInfo.allocated_page_num;
pageList[(j + num) % pInfo.allocated_page_num].isVisit = 0;
}
// 3.若第二轮扫描失败,则重新扫描,查找第一个(0,0)的帧用于替换。本轮扫描不修改任何标志位
for (j = 0; j < pInfo.allocated_page_num; j++){
if (pageList[(j + num) % pInfo.allocated_page_num].isVisit == 0 && pageList[(j + num) % pInfo.allocated_page_num].isModify == 0)
return (j+num) % pInfo.allocated_page_num;
}
//4. 若第三轮扫描失败,则重新扫描,查找第一个(0,1)的帧用于替换。
for (j = 0; j < pInfo.allocated_page_num; j++){
if (pageList[(j + num) % pInfo.allocated_page_num].isVisit == 0 && pageList[(j + num) % pInfo.allocated_page_num].isModify == 1)
return (j + num) % pInfo.allocated_page_num;
// pageList[(j + num) % pInfo.allocated_page_num].isVisit = 0;
}
}
int replace_page_clock(int num) {
int j;
// 将所有可能被置换的页面排成一个循环队列,用(访问位,修改位)的形式表示各页面状态
// 1.当需要淘汰一个页面时,只需检查页的访问位。如果是0,就选择该页换出;如果是1,则将它置为0,暂不换出,继续检查下一个页面
for (j = 0; j < pInfo.allocated_page_num; j++){
if (pageList[(j + num) % pInfo.allocated_page_num].isVisit == 0 )
return (j+num) % pInfo.allocated_page_num;
pageList[(j + num) % pInfo.allocated_page_num].isVisit = 0;
}
//2. 若第一轮扫描中所有页面都是1,则将这些页面的访问位依次置为0后,再进行第二轮扫描(第二轮扫描中一定会有访问位为0的页面
for (j = 0; j < pInfo.allocated_page_num; j++){
if (pageList[(j + num) % pInfo.allocated_page_num].isVisit == 0 )
return (j + num) % pInfo.allocated_page_num;
}
}
//随机化函数
bool randBool()
{
if ((rand() % 2 + 1) == 1)
return true;
else
return false;
}
void CLOCK_pro(int choose)
{
int n, full, status;
int num = -1;
replace_page = 0; // 页面替换指针初始化为0
page_loss_num = 0; // 缺页数初始化为0
full = 0; // 是否装满是所有的页框
for (n = 0; n < pInfo.allocated_page_num; n++) // 清除页框信息
{
pageList[n].pages = -1;
pageList[n].isModify = 0;
pageList[n].isVisit = 0;
pageList[n].time = 0;
}
is_loss_page = 0; //缺页标志,0为不缺页,1为缺页
for (current_page = 0; current_page < pInfo.visit_list_length; current_page++) // 执行算法
{
status = isExist(pInfo.pages[current_page]); //查找页面是否在内存
if (full < pInfo.allocated_page_num) // 开始时不计算缺页
{
if (status == 0) // 页不存在则装入页面
{
pageList[replace_page].pages = pInfo.pages[current_page];
replace_page = (replace_page + 1) % pInfo.allocated_page_num;
pageList[n].isVisit = 1;
full++;
}
}
else // 正常缺页置换
{
if (status == 0) // 页不存在则置换页面
{
if(choose==1)
replace_page = replace_page_pro(++num); // 当choose =1时采用改进的clock算法
else if(choose==0)
replace_page = replace_page_clock(++num); // 当choose =0时采用基本的clock算法
pageList[replace_page].pages = pInfo.pages[current_page];
pageList[replace_page].isVisit = 1;
pInfo.page_missing_num++; // 缺页次数加1
}
}
Sleep(10);
showState(); // 显示当前状态
} // 置换算法循环结束
_getch();
return;
}
// LRU页面置换算法
void LRU(void)
{
int n, full, status, max;
replace_page = 0; // 页面替换指针
page_loss_num = 0; // 缺页次数初始化为0
full = 0; // 是否装满所有的页框
for (n = 0; n < pInfo.allocated_page_num; n++)
{
pageList[n].pages = -1; // 清除页框信息
pageList[n].time = 0; // 清除页框历史
}
is_loss_page = 0; //缺页标志,0为不缺页,1为缺页
for (current_page = 0; current_page < pInfo.visit_list_length; current_page++) // 执行算法
{
status = isExist(pInfo.pages[current_page]); //查找页面是否在内存
if (full < pInfo.allocated_page_num) // 开始时不计算缺页
{
if (status == 0) // 页不存在则装入页面
{
pageList[replace_page].pages = pInfo.pages[current_page]; //把要调入的页面放入一个空的页框里
replace_page = (replace_page + 1) % pInfo.allocated_page_num;
full++;
}
}
else //正常缺页置换
{
if (status == 0)//页不存在则置换页面
{
max = 0;
for (n = 1; n < pInfo.allocated_page_num; n++)
{
if (pageList[n].time > pageList[max].time)
{
max = n;
}
}
replace_page = max;
pageList[replace_page].pages = pInfo.pages[current_page];
pageList[replace_page].time = 0;
pInfo.page_missing_num++; // 缺页次数加1
}
}
Sleep(10);
showState(); // 显示当前状态
} // 置换算法循环结束
_getch();
return;
}
// 主函数
int main()
{
char ch;
system("cls");
printf("**********张煜玮——19291255——页面置换算法********\n");
int m,e,s;
printf("请输入工作集移动率(m):");
scanf("%d",&m); //8
printf("请输入工作集包含的页数(e):");
scanf("%d",&e); //8
printf("请输入重复次数(s):");
scanf("%d",&s); // 10
getRandomList(m, e, s); // 随机生成访问序列
clock_t start, finish;
double totaltime;
while (true)
{
printf("====================MENU=========================\n");
printf("1. 最佳淘汰算法(OPT)\n");
printf("2. 先进先出淘汰算法(FIFO)\n");
printf("3. 最近最久未使用淘汰算法(LRU)\n");
printf("4. 基本的Clock 淘汰算法\n");
printf("5. 改进型的Clock 淘汰算法\n");
printf("=================================================\n");
printf("请输入选择的算法(1/2/3/4/5): ");
ch = (char)_getch();
switch (ch) {
case '1':
printf("\n《------------1. 最佳淘汰算法(OPT)--------------》\n");
start = clock();
OPT();
finish = clock();
totaltime = (double)(finish - start) / CLOCKS_PER_SEC;
cout << "\n运行总时长= " << totaltime << " 秒" << endl;
break;
case '2':
printf("\n\n《----------2. 先进先出淘汰算法(FIFO)------------》\n");
start = clock();
FIFO();
finish = clock();
totaltime = (double)(finish - start) / CLOCKS_PER_SEC;
cout << "\n运行总时长= " << totaltime << " 秒" << endl;
break;
case '3':
printf("\n\n《--------3. 最近最久未使用淘汰算法(LRU)-----------》\n");
start = clock();
LRU();
finish = clock();
totaltime = (double)(finish - start) / CLOCKS_PER_SEC;
cout << "\n运行总时长= " << totaltime << " 秒" << endl;
break;
case '4':
printf("\n\n《------------ 4. Clock 淘汰算法--------------》\n");
start = clock();
CLOCK_pro(0);
finish = clock();
totaltime = (double)(finish - start) / CLOCKS_PER_SEC;
cout << "\n运行总时长= " << totaltime << "秒" << endl;
break;
case '5':
printf("\n\n《------------ 5. 改进的Clock 淘汰算法--------------》\n");
start = clock();
CLOCK_pro(1);
finish = clock();
totaltime = (double)(finish - start) / CLOCKS_PER_SEC;
cout << "\n运行总时长= " << totaltime << "秒" << endl;
break;
default:
return 0;
}
}
return 0;
}