一、实验目的
编写一个程序,实现第九章所述的 FIFO、OPT 和 LRU 页面置换算法。
二、实验要求
(1)首先,生成一个随机的页面引用串,其中页码的范围为 0~9。
(2)将这个随机页面引用串应用到每个算法,记录每个算法引起的缺页次数,列出每次页面置换 的换出页序号,计算缺页率。
(3)系统分配给用户的页面帧的数量可选:3~7,具体分配策略可自行定义。
(4)假设采用请求分页(按需调页)。
(5)若实现可视化效果(图形、动画展示等)有加分
三、代码说明
全局变量说明
PN表示页面访问序列的长度,程序根据PN值随机生成长度为PN的0~9的页面访问系列。
FN表示分配给进程的内存块数
pageSeq、frames是两个指针,代表页面访问序列数组和内存块数组。
fault表示缺页数、change表示页面置换数、ratio表示缺页率、swap_out表示被淘汰的序列号。
功能函数说明
用于初始化内存,将frames数组的每一项初始化为-1。
用于输出单步结果
用于打印缺页次数、置换次数、缺页率
用于给页面访问序列数组和内存数组初始化空间,生成长度为PN的随机页面访问序列并告诉用户所生成的页面访问序列值。
用于告诉用户本程序提供了哪些功能接口,引导用户操作本程序。
搜索函数
检查是否缺页:当发出请求分页时,需要检查当前访问序列号是否在内存块中,如果已经存在、即命中,没有缺页;反之,则没有命中,缺页。
寻找置换序列号:如果发生缺页,需要遍历内存块中的每一个序列号,如果在算法要求的序列中不存在,那么认为当前序列号就是最久没有被访问的,如果所有序列号在算法要求的序列中都存在,则需要找一个最久没有被访问的序列号作为淘汰序列号。特别注意的是,FIFO算法无需使用该函数,因为FIFO算法如果发生缺页,其使用的是轮流淘汰的原则。
FIFO算法原理:
1、初始化内存块、随机生成页面访问序列;
2、循环取出页面访问序列中的一个值;
3、检查是否缺页、是则使用轮流淘汰算法进行页面置换、否则直接跳过;
4、循环上述步骤、直至没有页面访问序列需要访问。
OPT算法原理:
1、初始化内存块、随机生成页面访问序列;
2、循环取出页面访问序列中的一个值;
3、检查是否缺页、是则使用未来最久不被访问算法进行页面置换、否则直接跳过;
4、循环上述步骤、直至没有页面访问序列需要访问。
LRU算法原理:
1、初始化内存块、随机生成页面访问序列;
2、循环取出页面访问序列中的一个值;
3、检查是否缺页、是则使用过去最久不被访问算法进行页面置换、否则直接跳过;
4、循环上述步骤、直至没有页面访问序列需要访问。
未来最久不被访问算法原理:
1、定义一个变量a,并初始化为一个比较小的值MINSIZE;
2、取出内存块中的一个值b,将其与未来的访问序列比对,找出其第一次出现的位置pos,将pos与a进行比较,并将最大值赋予a,并记录b在内存块中的位置j;若匹配失败,则认为b就是最久不被访问的页面,将其淘汰,终止循环;
3、遍历内存块中所有值,最终内存块中j对应的值就是最久不被访问的页面,将其淘汰。
过去最久不被访问算法原理:
1、定义一个变量a,并初始化为一个比较大的值MAXSIZE;
2、取出内存块中的一个值b,将其与过去的访问序列比对,找出其第一次出现的位置pos,将pos与a进行比较,并将最小值赋予a,并记录b在内存块中的位置j;
3、遍历内存块中所有值,最终内存块中j对应的值就是最久不被访问的页面,将其淘汰。
四、实验结果
1、人机交互
初始化页面访问序列并告诉客户本程序的操作说明。
1、FIFO算法:
fault后面如果有数字则表明其实淘汰的页面编号,最终打印总的缺页次数、置换次数、缺页率。
置换次数=缺页次数-内存块个数
缺页率=缺页次数/页面访问序列的长度
2、OPT算法:
fault后面如果有数字则表明其实淘汰的页面编号,最终打印总的缺页次数、置换次数、缺页率。
置换次数=缺页次数-内存块个数
缺页率=缺页次数/页面访问序列的长度
3、LRU算法:
fault后面如果有数字则表明其实淘汰的页面编号,最终打印总的缺页次数、置换次数、缺页率。
置换次数=缺页次数-内存块个数
缺页率=缺页次数/页面访问序列的长度
五、实验代码
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
using namespace std;
#define PN 12//页面访问序列长度
#define FN 3//分配给进程的内存块数
int* pageSeq;//页面访问序列
int* frames;//内存块数组
int fault, change;//缺页次数和置换次数
float ratio;//缺页率
int swap_out;
//初始化内存块,没有被占用就初始化为-1
void clear()
{
fault = 0;//缺页次数
change = 0;//置换次数
for (int i = 0; i < FN; i++) {
frames[i] = -1;
}
}
//输出每一步结果
void display1(int flag, int swap_out)//是否缺页,被淘汰的序列号
{
for (int i = 0; i < FN; i++) {
printf("-----");
}
printf("\n");
//输出内存中存储序列号的情况
for (int i = 0; i < FN; i++) {
printf("|%3d|", frames[i]);
}
//是否缺页,缺页则输出fault以及被淘汰的序列号,如果为-1则不输出
if (flag) {
printf(" fault");
if (swap_out != -1) {
printf(" %d", swap_out);
}
}
printf("\n");
for (int i = 0; i < FN; i++) {
printf("-----");
}
printf("\n");
Sleep(1000);
}
//输出缺页次数、置换次数、缺页率
void display()
{
change = fault - FN;//除了前FN个,剩下的只要缺页都需要置换
ratio = (float)fault / PN * 100;
printf("------------------------------\n");
printf("缺页次数:%d\n", fault);
printf("置换次数:%d\n", change);
printf("缺 页 率:%4.1f%%\n", ratio);
printf("==============================\n");
}
//搜索内存中的序列号在访问序列中的位置,找到返回下标,否则返回-1
//p:需要找的序列号,ar:页面访问序列或者内存块数组,start:起点,end:终点
//内存块中的某个序列号在访问序列中的位置
//当前访问序列号是否在内存块中,即是否缺页
int search(int p, int* ar, int start, int end)
{
int i, to;//to作为方向标志,to=1时,循环变量递增;to=-1时,循环变量递减
//在OPT算法中,往后搜索,start<end,循环变量递增,to=1
//在LRU算法中,往前搜索,start>end,循环变量递减,to=-1
if (start > end) {
to = -1;
}
else {
to = 1;
}
i = start;
while (i != end + to)
{
if (p == ar[i]) {//找到p则返回下标
return i;
}
i = i + to;
}
return -1;//找不到,则返回-1
}
//OPT算法,往后搜索,淘汰最晚出现的
//sequence:页面访问序列数组,page:内存块数组
void OPT(int* sequence, int sequence_length, int* page, int page_length)
{
int i = 0, j, flag;
int page_num = 0;//page_num为进入内存页面数,当page_num>=page_length时,内存块满,此时缺页产生置换,且page_num的值不再增加
int start;//搜索起点,即页面访问序列中当前页的下一位置
int posi, pos_s_max, pos_p_max;//未来最久不使用页面在sequence和page中的位置
clear();//内存块数据清零
printf("页面访问过程:\n");
printf("------------------------------\n");
for (i = 0; i < sequence_length; i++)//扫描页面访问序列
{
flag = 0;//缺页标志,初值置0,不缺页
if (search(sequence[i], page, 0, page_length - 1) == -1)//页不在内存
{
flag = 1;//缺页,flag置1
fault++;//缺页+1
if (page_num < page_length)//有空闲内存块,不需要置换
{
swap_out = page[page_num];
page[page_num] = sequence[i];
page_num++;
}
else//没有空闲内存块,需要置换
{
start = i + 1; //当前访问序列的下一个位置
pos_s_max = -1;//被淘汰页面在访问序列sequence中的位置,初值置-1
//对内存page中的每个页面依次查找其在访问序列sequence中(未来)第一次出现的位置,并存放在posi中
for (j = 0; j < page_length; j++)
{
posi = search(page[j], sequence, start, sequence_length - 1);
if (posi == -1)//不存在
{
swap_out = page[j];
page[j] = sequence[i];//置换并终止循环
break;
}
if (posi > pos_s_max)//存在,找最晚出现的序列号
{
pos_s_max = posi;//更新最晚出现的序列号的位置
pos_p_max = j;//记录最晚出现的序列号在内存中的位置
}
}
//说明page中所有序列号都出现过
if (j >= page_length) {
swap_out = page[pos_p_max];
page[pos_p_max] = sequence[i];//将当前要访问的序列号放在内存中pos_p_max记录的位置,即最晚出现的序列号的位置
}
}
}
display1(flag, swap_out);//输出每一次内存页面情况
}
}
//FIFO算法
//sequence:页面访问序列数组,page:内存块数组
void FIFO(int* sequence, int sequence_length, int* page, int page_length)
{
int i, j = 0, flag;
clear();//内存块数据清零
printf("页面访问过程:\n");
printf("------------------------------\n");
for (i = 0; i < sequence_length; i++)
{
flag = 0;//缺页标志
if (search(sequence[i], page, 0, page_length - 1) == -1)//是否缺页
{
fault++;//缺页+1
flag = 1;
swap_out = page[j];
page[j] = sequence[i];
j = (j + 1) % page_length;//j+1,循环
}
display1(flag, swap_out);//输出每一次内存页面情况
}
}
//LRU算法,往前搜索,淘汰最早出现的,即最久未被访问的
//sequence:页面访问序列数组,page:内存块数组
void LRU(int* sequence, int sequence_length, int* page, int page_length)
{
int i, j;
int page_num = 0;//page_num>=page_length时,内存块满,此时缺页产生置换
int flag;//缺页标志
int posi, pos_s_max, pos_p_max;//最久未访问页面在sequence中过去的位置和在page中的位置
clear();//清理内存块数据等
for (i = 0; i < sequence_length; i++)
{
flag = 0;
if (search(sequence[i], page, 0, page_length - 1) == -1)//是否缺页
{
flag = 1;
fault++;//缺页
if (page_num < page_length)//有空闲块,不需要置换
{
swap_out = page[page_num];
page[page_num] = sequence[i];
page_num++;//空闲块减少
}
else//无空闲块,需要置换
{//在sequence中向前搜索,寻找最久未被访问的页面位置
pos_s_max = sequence_length;//pos_s_max初值(最大值或者当前值i)
for (j = 0; j < page_length; j++)//遍历内存块中的每一个值
{
posi = search(page[j], sequence, i - 1, 0);//这里与OPT不同,不会出现页面不存在的情况
if (posi < pos_s_max)//page[j]中的值一定在访问序列中出现过,所以posi一定不等于-1
{
pos_s_max = posi;//更新最久未被访问的序列号的位置
pos_p_max = j;//记录最久未被访问的序列号在内存中的位置
}
}
swap_out = page[pos_p_max];//记录被淘汰的序列号
page[pos_p_max] = sequence[i];//置换
}
}
display1(flag, swap_out);//输出每一次内存页面情况
}
}
//随机生成页面访问序列
void init()
{
//给页面访问序列数组和内存数组初始化空间
pageSeq = (int*)(malloc(PN * sizeof(int)));//初始化为页面访问序列长度
frames = (int*)(malloc(FN * sizeof(int)));//初始化为内存块个数
//随机生成序列
for (int i = 0; i < PN; i++) {
pageSeq[i] = rand() % 10;
}
printf("页面访问序列:\n\n");//输出页面访问序列
for (int i = 0; i < PN; i++) {
printf("%3d", pageSeq[i]);
}
printf("\n\n");
printf("===============================================================\n");
}
//人机交互信息
void solve() {
printf("【页面置换算法】\n");
printf("序列长度:%d\n", PN);//输出访问序列长度
printf("内存块数:%d\n", FN);//输出内存块数
printf("======================\n\n");
init();//随机生成页面访问序列
//给用户介绍该程序提供了哪些操作
printf("操作说明:\n");
printf(" op=0 程序结束\n");
printf(" op=1 FIFO算法\n");
printf(" op=2 OPT算法\n");
printf(" op=3 LRU算法\n");
printf("==============================\n");
printf("\n");
printf("输入操作序号op:");
}
int main() {
int op;//操作选择
solve();//人机交互信息
scanf_s("%d", &op);
while (1)
{
system("cls");
switch (op)
{
case 0:printf("\n=====程序结束!=====\n"); return 0;
case 1:printf("\n【FIFO算法】\n"); FIFO(pageSeq, PN, frames, FN); break;//PN和FN分别代表各自数组的长度
case 2:printf("\n【OPT算法】\n"); OPT(pageSeq, PN, frames, FN); break;//pageSeq是页面访问序列,frame是内存数组
case 3:printf("\n【LRU算法】\n"); LRU(pageSeq, PN, frames, FN); break;
default:printf("\n=====重新输入=====\n"); goto L1;
}
display();//输出缺页次数、置换次数、缺页率
L1: printf("\n");//若用户输入非法值,需要跳过display(),则拒绝处理,要求用户重新输入
printf("输入操作序号num:");
scanf_s("%d", &op);
}
}