文章目录
操作系统——存储管理实验
一、实验目的
- 存储管理的主要功能之一是合理地分配空间。请求页式管理是一种常用的虚拟存储管理技术。
- 本实验的目的是通过请求页式存储管理中页面置换算法模拟设计,了解虚拟存储技术的技术特点,掌握请求页式存储管理的页面置换算法。
二、实验环境
实验环境如下表所示
名称 | 配置信息 |
---|---|
操作系统 | Xubuntu(实验楼) |
编程语言 | C语言 |
编译器 | GCC-12 |
fork.c所属内核 | Linux-6.8.7 |
三、实验问题
- 操作系统是如何管理虚拟地址与物理地址之间的关系?
- 什么是虚拟存储技术,为什么要使用虚拟内存技术?
- 什么是页地址流?
- 请求页式管理是一种什么技术?
四、实验内容
- 通过随机数产生一个指令序列,共320条指令。指令的地址按下述原则生成:
①50%的指令是顺序执行的;
②50%的指令是均匀分布在前地址部分;
③50%的指令是均匀分布在后地址部分。
具体的实施方法是:
①在 [0,319] 的指令之间随即选取一起点m;
②顺序执行一条指令,即执行地址为m+1的指令;
③在前地址[0,m+1]中随机选取一条指令并执行,该指令的地址为m′;
④顺序执行一条指令,其地址为 m′+ 1;
⑤在后地址[m′+ 2,319]中随机选取一条指令并执行;
⑥重复上述步骤①-⑤,直到执行320次指令。
- 将指令序列变换为页地址流
设:①页面大小为1k;
②用户内存容量为4页到32页;
③用户虚存容量为32k。
在用户虚存中,按每k存放10条指令排在虚存地址,即320条指令在虚存中的存放方式为:
第0条-第9条指令为第0页(对应虚存地址为[0,9]);
第10条-第19条指令为第一页(对应虚存地址为[10,19]);
… …
第310条~第319条指令为第31页(对应虚地址为[310,319])。
按以上方式,用户指令可组成32页。
- 计算并输出下述各种算法在不同内存容量下的命中率。
①先进先出的算法(FIFO);
②最近最少使用算法(LRU算法);
③最佳淘汰算法(OPT)先淘汰最不常用的页地址;
④第二次机会页面替换算法(SCR算法);
⑤CLCOK算法又称为最近未使用算法(NRU)。
其中③、④、⑤为选择内容。
命中率=1-页面失效次数/页地址流长度
在本实验中,页地址流长度为320,页面失效次数为每次访问相应指令时,该指令所对应的页不在内存的次数。
五、实验步骤
5.1方案设计
5.1.1算法分析
(1)FIFO算法:这个算法的基本思想是总是先淘汰一些驻留在内存时间最长的页面,即先进入内存的页面先被置换掉。作业只要把进入内存的各个页面按进入的时间次序用链表链接起来,设定一个链表首指针指向进入内存最早的一个页面,新进入的页面放在链表的尾部,需要淘汰某一个页面时,总是淘汰替换指针所指向的页面即可。先进先出算法在程序按线性顺序访问逻辑地址空间时比较理想,否则效率不高。特别是在遇到循环执行的程序段时,往往会把频繁访问的页面,因其在内存驻留时间过长,而周期性地淘汰掉。
(2)OPT算法:这是最理想的页面置换算法,从内存中移出以后不再使用的页面;如无这样的页面,则选择以后最长时间内不需要访问的页面。本算法因为页面访问的顺序是很难预知的,所以不是一种实际的方法。
(3)LRU算法:本算法的基本思想是,如果某一页被访问了,那么它很可能马上又被访问;反之,如果某一页很长时间没有被访问,那么最近也不太可能会被访问。这种算法考虑了程序设计的局部性原理。其实质是:当需要置换一页时,选择最近一段时间内最久未使用的页面予以淘汰。
(4)SCR算法:此算法的本质就是设置一个访问位当淘汰一个页面时,要检查其访问位:若访问位是1,给它第二次机会,选择下一个FIFO页面,并将其访问位置为0;若访问位是0,则淘汰它。另外,访问到访问位为0的页面,将其访问位重新置为1
(5)Clock算法:又称最近未用算法(NRU)。该算法将内存中所有页面都通过链接指针链接成一个循环队列,设置访问位,1表示被访问过,0表示未被访问过;如果访问位是0,则选择该页换出,如果访问位是1,则将访问位置0,暂不换出,再按照FIFO算法检查下一个页面,重复上述步骤。
5.1.2算法设计
在本实验中,我们通过对五种算法进行了解,并通过C语言编程调用实现,并打印结果,了解操作系统在内存管理是如何实现的。
(1) FIFO算法
-
每次选择淘汰的页面是最早进入内存的页面
-
实现方法:把调入内存的页面根据调入的先后顺序排成一个队列,需要换出页面时选择队头页面即可。
-
队列的最大长度取决于系统为进程分配了多少个内存块。
(2) OPT算法
- 每次选择淘汰的页面将是以后永不使用,或者在最长时间内不再被访问的页面,这样可以保证最低的缺页率。
- 实现方法: 输入页面号引用串,如果页框中的某个页面P以后永不使用,则该页面为淘汰页面Pt。如果每个P都会再次被访问,那么其中最长未来时间内不再被访问的页面为淘汰页面Pt。
(3) LRU算法
- 每次淘汰的页面是最近最久未使用的页面
- 实现方法:赋予每个页面对应的页表项中,用访问字段记录该页面自上次被访问以来所经历的时间t。
- 当需要淘汰一个页面时,选择现有页面中 t 值最大的,即最近最久未使用的页面。
(4) SCR算法
- 第二次机会算法的基本思想是与FIFO相同的,但是有所改进,避免把经常使用的页面置换出去。
- 实现方式:设置一个访问位。当淘汰一个页面时,要检查其访问位:若访问位是1,给它第二次机会,选择下一个FIFO页面,并将其访问位置为0;若访问位是0,则淘汰它。另外,访问到访问位为0的页面,将其访问位重新置为1。
(5) Clock算法
- 时钟置换算法是一种性能和开销较均衡的算法,又称CLOCK算法,或最近未用算法(NRU)
- 简单的CLOCK 算法实现方法:为每个页面设置一个访问位,再将内存中的页面都通过链接指针链接成一个循环队列。
①、当某页被访问时,其访问位置为1。当需要淘汰一个页面时,只需检查页的访问位。
②、如果是0,就选择该页换出;如果是1,则将它置为0,暂不换出,继续检查下一个页面,若第一轮扫描中所有页面都是1,则将这些页面的访问位依次置为0后,再进行第二轮扫描(第二轮扫描中一定会有访问位为0的页面,因此简单的CLOCK 算法选择一个淘汰页面最多会经过两轮扫描)
5.2 所用函数说明
-
函数typedef struct:定义一个队列的顺序存储结构
-
函数void Init():初始化数据
-
函数void OPT(int n):实现OPT算法
-
函数void FIFO(int n):实现FIFO算法
-
函数void LRU(int n):实现LRU算法
-
函数void SCR(int n):实现SCR算法
-
函数void Clock(int n):实现Clock算法
-
函数int main():主函数,实现初始化和函数调用
5.3 模块代码及其相关注释
调用头函数,进行宏定义
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 320 //指令数
#define MAX 500
int instruction[N]; //指令序列
int page[N]; //记录指令所在页面
定义队列的顺序存储结构
typedef struct
{
int* base; //存储空间的基地址
int front;
int rear;
}SqQueue;
初始化数据
void Init()
{
srand((unsigned int)time(NULL));//生成随机种子
int index = 0;
int x = 0;
int y = 0;
int z = 0;
int i;
while (index < N)
{
x = rand() % N; //在[0,319]的指令地址之间随机选取一起点m
while (x > N - 2)
{
x = rand() % N;
}
instruction[index++] = x + 1;//顺序执行一条指令,即执行地址为m+1的指令(记录顺序)
y = rand() % (x + 1); //在前地址[0,m+1]中随机选取一条指令并执行,该指令的地址为m’
while (y > N - 3)
{
y = rand() % (x + 1);
}
instruction[index++] = y;
instruction[index++] = y + 1;// 顺序执行一条指令,其地址为m’ + 1
z = rand() % (N - (y + 2)) + y + 2; //在后地址[m’+2,319]中随机选取一条指令
instruction[index++] = z;
}
for (i = 0; i < N; i++)
{
page[i] = instruction[i] / 10; //每条指令对应的页面
}
printf("随机生成的每条指令所在页面为:\n");
for (i = 0; i < N; i++)
{
if ((i % 10 == 0) && (i != 0))
printf("\n");
printf("%d\t", page[i]);
}
}
OPT算法
void OPT(int n)
{
int i, j, k;
int test, test2; //test标志该页是否在内存中,test2标志内存是否还有空间
int* next_time = NULL; //页面下次出现时间
next_time = (int*)malloc(sizeof(int) * n);
int* memory = NULL;//内存空间
memory = (int*)malloc(sizeof(int) * n);
int index = 0; //距离下次访问最远的页面的下标
double count = 0; //定义命中次数
for (i = 0; i < n; i++)
{
next_time[i] = -1; //初始化数组
memory[i] = -1;
}
for (i = 0; i < N; i++)
{
test = 0;
for (j = 0; j < n; j++)
{
if (memory[j] == page[i]) //查找该页是否在内存中
{
test = 1;
break;
}
}
if (test == 1) //test=1,该页在内存中
{
count += 1; //命中数+1
}
else//当发送缺页中断时
{
test2 = 0;
for (j = 0; j < n; j++) //缺页中断
{
if (memory[j] == -1) //检查是否还有内存空间
{
test2 = 1; //说明有多余空间,test2=1;
next_time[j] = MAX;
memory[j] = page[i]; //新的页面进入内存空间
break;
}
}
if (test2 == 0)//当内存空间已满时
{
for (int j = 0; j < n; j++)
{
next_time[j] = MAX;
for (k = i + 1; k < N; k++)
{
if (memory[j] == page[k])
{
next_time[j] = k - i; //求出内存中的页面下一次出现的位置
break;
}
}
}
for (j = 0; j < n; j++)
{
if (next_time[j] > next_time[index])
index = j; //找出距离下次访问最远的页面,求出下标index
}
memory[index] = page[i];//找到页面,替换该页面
}
}
}
printf("内存容量为%d页时,命中率为%lf", n, count / N);
printf("\n");
free(next_time);
free(memory);
}
FIFO算法
void FIFO(int n)
{
SqQueue Q;//生成队列
//初始化队列
Q.base = (int*)malloc(sizeof(int) * n);
if (!Q.base)
exit(1);
Q.front = Q.rear = 0;
double count = 0; //定义命中次数
int test;//test标志该页是否在内存中
for (int i = 0; i < N; i++)
{
test = 0;
int temp = Q.front;//标记队列的第一个
while (temp != Q.rear)
{
if (Q.base[temp] == page[i]) //查找该页是否在内存中
{
test = 1;//
break;
}
temp = (temp + 1) % n;
}
if (test == 1)//该页在内存中
{
count += 1; //命中数量+1
}
else //发生缺页中断
{
if ((Q.rear + 1) % n == Q.front) //如果内存已满
{
Q.front = (Q.front + 1) % n; //最先进去的页面淘汰,即出队操作
Q.base[Q.rear] = page[i]; //最新的页面进入内存,即入队操作
Q.rear = (Q.rear + 1) % n;
}
else //当内存没满
{
Q.base[Q.rear] = page[i]; //新的页面进入内存,即入队操作
Q.rear = (Q.rear + 1) % n;
}
}
}
printf("内存容量为%d页时命中率为%lf", n, count / N);
printf("\n");
free(Q.base);//释放资源
}
LRU算法
void LRU(int n)
{
int i, j, k;
int* memory = NULL;
memory = (int*)malloc(sizeof(int) * n); //内存空间
int* sign = NULL;
sign = (int*)malloc(sizeof(int) * n); //标志内存中的页上次访问到现在的时间
double count = 0; //定义命中次数
int test, test2; //test标志该页是否在内存中,test2标志内存是否还有空间
int index; //设置为最久未访问页面的下标
for (i = 0; i < n; i++)
{
memory[i] = -1; //初始化数组
sign[i] = -1;
}
for (i = 0; i < N; i++)
{
//更新标记时间的值
for (j = 0; j < n; j++)
{
if (sign[j] != -1)
{
sign[j] += 1; //页面上次访问到现在时间+1
}
}
test = 0;
for (j = 0; j < n; j++)
{
if (memory[j] == page[i]) //查找该页是否在内存中
{
test = 1;
sign[j] = 0; //访问时间置0
break;
}
}
if (test == 1) //test=1,该页在内存中
{
count += 1; //命中+1
}
else //引发缺页中断
{
test2 = 0;
for (k = 0; k < n; k++)
{
if (memory[k] == -1) //检查是否还有内存空间
{
test2 = 1; //有多余空间,test2=1;
memory[k] = page[i]; //新的页面进入内存
sign[k] = 0; //访问时间置0
break;
}
}
if (test2 == 0) //如果内存已满
{
index = 0;
for (k = 0; k < n; k++)
{
if (sign[index] < sign[k])
index = k; //找出上次访问到现在最久的页面,找到下标index
}
memory[index] = page[i]; //替换该页面
}
}
}
printf("内存容量为%d页时命中率为%lf", n, count / N);
printf("\n");
free(memory);
free(sign);
}
SCR算法
void SCR(int n) {
int* memory = (int*)malloc(sizeof(int) * n); // 内存空间
int* refBit = (int*)malloc(sizeof(int) * n);
int* counter = (int*)malloc(sizeof(int) * n);
int hitCount = 0; // 命中次数
int initialHand = 0; // 初始hand位置
int found = 0; // 用于标记页面是否在内存中
int i, j;
int hand = 0;
// 初始化内存、引用位和访问时间
for (i = 0; i < n; i++) {
memory[i] = -1; // 假设初始时没有页面在内存中
refBit[i] = 0; // 引用位初始化为0
counter[i] = 0; // 访问时间初始化为0
}
// 遍历页面序列
for (i = 0; i < N; i++) {
int pageFault = page[i]; // 当前页面
// 查找页面是否在内存中
for (j = 0; j < n; j++) {
if (memory[j] == pageFault) {
found = 1; // 页面在内存中
refBit[j] = 0; // 访问了该页面,将引用位设置为0
counter[j] = 0; // 重置访问时间
break;
}
}
// 页面在内存中,增加命中次数
if (found) {
hitCount++;
found = 0; // 重置found标志,为下一个页面做准备
}
else {
// 页面不在内存中,发生缺页中断
found = 0; // 重置found标志,用于下面的循环
initialHand = hand = 0; // 初始化hand并设置initialHand
do {
// 如果R位为0,则替换当前页面
if (refBit[hand] == 0) {
memory[hand] = pageFault;
refBit[hand] = 1; // 设置新页面的R位为1
counter[hand] = 0; // 重置新页面的访问时间
break;
}
// 如果R位为1,则增加访问时间并继续查找
counter[hand]++;
// 继续查找下一个页面
hand = (hand + 1) % n;
// 检查是否已经遍历了所有页面
if (hand == initialHand) {
// 如果所有页面的R位都为1,则替换hand指向的页面(即最旧的页面)
int oldest = hand;
for (j = (hand + 1) % n; j != hand; j = (j + 1) % n) {
if (counter[j] > counter[oldest]) {
oldest = j;
}
}
memory[oldest] = pageFault;
refBit[oldest] = 1; // 设置新页面的R位为1
counter[oldest] = 0; // 重置新页面的访问时间
break;
}
} while (1); // 循环直到找到可替换的页面
}
}
// 计算并输出命中率
printf("内存容量为%d页时命中率为%lf\n", n, (double)hitCount / N);
// 释放内存
free(memory);
free(refBit);
free(counter);
}
Clock算法
void Clock(int n) {
int hitCount = 0; // 命中次数
int hand = 0; // 模拟循环队列的指针(hand in hand)
int i;
int* memory = NULL;
memory = (int*)malloc(sizeof(int) * n); //内存空间
int* refBit = NULL;
refBit = (int*)malloc(sizeof(int) * n);
// 初始化内存和引用位
for (i = 0; i < n; i++) {
memory[i] = -1; // 假设初始时没有页面在内存中
refBit[i] = 0; // 引用位初始化为0
}
// 遍历页面序列
for (i = 0; i < N; i++) {
int pageFault = page[i]; // 当前页面
int found = 0; // 标记页面是否在内存中
// 查找页面是否在内存中
for (int j = 0; j < n; j++) {
if (memory[j] == pageFault) {
found = 1; // 页面在内存中
refBit[j] = 0; // 访问了该页面,将引用位设置为0
break;
}
}
// 页面在内存中,增加命中次数
if (found) {
hitCount++;
}
else {
// 页面不在内存中,发生缺页中断
// 查找第一个R位为0的页面或者循环回到开始并重置所有R位
do {
if (refBit[hand] == 0) {
// 找到R位为0的页面,替换它
memory[hand] = pageFault;
refBit[hand] = 1; // 设置新页面的R位为1
break;
}
else {
// 如果没有找到,则继续查找下一个页面,或者重置所有R位
hand = (hand + 1) % n; // 循环队列
if (hand == 0) { // 如果回到开始,重置所有R位
for (int k = 0; k < n; k++) {
refBit[k] = 0;
}
}
}
} while (hand != 0 || refBit[hand] == 1); // 循环直到找到可替换的页面或回到开始
}
}
// 计算并输出命中率
printf("内存容量为%d页时命中率为%lf\n", n, (double)hitCount / N);
free(memory);
free(refBit);
}
主函数,调用函数
void main()
{
printf("----------------------------------初始化操作----------------------------------\n");
Init(); //初始化
printf("\n");
printf("---------------------------------最佳淘汰算法(OPT)---------------------------------\n");
for (int i = 4; i <= 32; i++)
{
OPT(i); //最佳淘汰算法,用户的内存容量4页到32页
}
printf("\n");
printf("--------------------------------先进先出算法(FIFO)---------------------------------\n");
for (int j = 4; j <= 32; j++)
{
FIFO(j); //先进先出算法,用户的内存容量4页到32页
}
printf("\n");
printf("------------------------------最近最久未使用算法(LRU)------------------------------\n");
for (int t = 4; t <= 32; t++)
{
LRU(t); //最近最久未使用算法,用户的内存容量4页到32页
}
printf("\n");
printf("------------------------------第二次机会页面替换算法(SCR)------------------------------\n");
for (int b = 4; b <= 32; b++)
{
SCR(b); //第二次机会页面替换算法,用户的内存容量4页到32页
}
printf("\n");
printf("------------------------------时钟页面替换算法(Clock)------------------------------\n");
for (int t = 4; t <= 32; t++)
{
Clock(t); //时钟页面替换算法,用户的内存容量4页到32页
}
system("pause");
}
运行结果展示
初始化随机数生成:
OPT算法运行结果展示:
FIFO算法运行结果展示:
LRU算法运行结果展示:
SCR算法运行结果展示:
Clock算法运行结果展示:
六、实验结论
通过切实的实验与分析,我对于操作系统课程的分页存储管理了解大大的加深了,所有的东西都不再是纸上谈兵,从基础的五种算法的实现:OPT,FIFO,LUR,SCR,Clock,可以深入的了解操作系统中一些原理性的东西,他的运行,他的工作方式,不只是靠自己对着课本的文字去想想了。
实验五通过算法来对主存实现分配的过程中,更是深刻的体会到其中的微妙。五种算法分页管理中的运行结果出现之后一目了然,链表方式也是方便自如。是让书上不动的文字活了起来。
整个实验下来,无论是翻课本查原理,还是上网查阅资料,都是促进我们学习实践的一大助力,让课堂上一些死板的知识流转在手指之间,跃现在荧屏上面。
本实验全部代码及注释
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 320 //指令数
#define MAX 500
int instruction[N]; //指令序列
int page[N]; //记录指令所在页面
//队列的顺序存储结构
typedef struct
{
int* base; //存储空间的基地址
int front;
int rear;
}SqQueue;
//初始化数据
void Init()
{
srand((unsigned int)time(NULL));//生成随机种子
int index = 0;
int x = 0;
int y = 0;
int z = 0;
int i;
while (index < N)
{
x = rand() % N; //在[0,319]的指令地址之间随机选取一起点m
while (x > N - 2)
{
x = rand() % N;
}
instruction[index++] = x + 1;//顺序执行一条指令,即执行地址为m+1的指令(记录顺序)
y = rand() % (x + 1); //在前地址[0,m+1]中随机选取一条指令并执行,该指令的地址为m’
while (y > N - 3)
{
y = rand() % (x + 1);
}
instruction[index++] = y;
instruction[index++] = y + 1;// 顺序执行一条指令,其地址为m’ + 1
z = rand() % (N - (y + 2)) + y + 2; //在后地址[m’+2,319]中随机选取一条指令
instruction[index++] = z;
}
for (i = 0; i < N; i++)
{
page[i] = instruction[i] / 10; //每条指令对应的页面
}
printf("随机生成的每条指令所在页面为:\n");
for (i = 0; i < N; i++)
{
if ((i % 10 == 0) && (i != 0))
printf("\n");
printf("%d\t", page[i]);
}
}
//最佳淘汰算法(最佳页面替换算法)P235
void OPT(int n)
{
int i, j, k;
int test, test2; //test标志该页是否在内存中,test2标志内存是否还有空间
int* next_time = NULL; //页面下次出现时间
next_time = (int*)malloc(sizeof(int) * n);
int* memory = NULL;//内存空间
memory = (int*)malloc(sizeof(int) * n);
int index = 0; //距离下次访问最远的页面的下标
double count = 0; //定义命中次数
for (i = 0; i < n; i++)
{
next_time[i] = -1; //初始化数组
memory[i] = -1;
}
for (i = 0; i < N; i++)
{
test = 0;
for (j = 0; j < n; j++)
{
if (memory[j] == page[i]) //查找该页是否在内存中
{
test = 1;
break;
}
}
if (test == 1) //test=1,该页在内存中
{
count += 1; //命中数+1
}
else//当发送缺页中断时
{
test2 = 0;
for (j = 0; j < n; j++) //缺页中断
{
if (memory[j] == -1) //检查是否还有内存空间
{
test2 = 1; //说明有多余空间,test2=1;
next_time[j] = MAX;
memory[j] = page[i]; //新的页面进入内存空间
break;
}
}
if (test2 == 0)//当内存空间已满时
{
for (int j = 0; j < n; j++)
{
next_time[j] = MAX;
for (k = i + 1; k < N; k++)
{
if (memory[j] == page[k])
{
next_time[j] = k - i; //求出内存中的页面下一次出现的位置
break;
}
}
}
for (j = 0; j < n; j++)
{
if (next_time[j] > next_time[index])
index = j; //找出距离下次访问最远的页面,求出下标index
}
memory[index] = page[i];//找到页面,替换该页面
}
}
}
printf("内存容量为%d页时,命中率为%lf", n, count / N);
printf("\n");
free(next_time);
free(memory);
}
//先进先出法(先进先出页面替换算法)
void FIFO(int n)
{
SqQueue Q;//生成队列
//初始化队列
Q.base = (int*)malloc(sizeof(int) * n);
if (!Q.base)
exit(1);
Q.front = Q.rear = 0;
double count = 0; //定义命中次数
int test;//test标志该页是否在内存中
for (int i = 0; i < N; i++)
{
test = 0;
int temp = Q.front;//标记队列的第一个
while (temp != Q.rear)
{
if (Q.base[temp] == page[i]) //查找该页是否在内存中
{
test = 1;//
break;
}
temp = (temp + 1) % n;
}
if (test == 1)//该页在内存中
{
count += 1; //命中数量+1
}
else //发生缺页中断
{
if ((Q.rear + 1) % n == Q.front) //如果内存已满
{
Q.front = (Q.front + 1) % n; //最先进去的页面淘汰,即出队操作
Q.base[Q.rear] = page[i]; //最新的页面进入内存,即入队操作
Q.rear = (Q.rear + 1) % n;
}
else //当内存没满
{
Q.base[Q.rear] = page[i]; //新的页面进入内存,即入队操作
Q.rear = (Q.rear + 1) % n;
}
}
}
printf("内存容量为%d页时命中率为%lf", n, count / N);
printf("\n");
free(Q.base);//释放队列
}
//最近最久未使用算法(最近最少使用页面替换算法)
void LRU(int n)
{
int i, j, k;
int* memory = NULL;
memory = (int*)malloc(sizeof(int) * n); //内存空间
int* sign = NULL;
sign = (int*)malloc(sizeof(int) * n); //标志内存中的页上次访问到现在的时间
double count = 0; //定义命中次数
int test, test2; //test标志该页是否在内存中,test2标志内存是否还有空间
int index; //设置为最久未访问页面的下标
for (i = 0; i < n; i++)
{
memory[i] = -1; //初始化数组
sign[i] = -1;
}
for (i = 0; i < N; i++)
{
//更新标记时间的值
for (j = 0; j < n; j++)
{
if (sign[j] != -1)
{
sign[j] += 1; //页面上次访问到现在时间+1
}
}
test = 0;
for (j = 0; j < n; j++)
{
if (memory[j] == page[i]) //查找该页是否在内存中
{
test = 1;
sign[j] = 0; //访问时间置0
break;
}
}
if (test == 1) //test=1,该页在内存中
{
count += 1; //命中+1
}
else //引发缺页中断
{
test2 = 0;
for (k = 0; k < n; k++)
{
if (memory[k] == -1) //检查是否还有内存空间
{
test2 = 1; //有多余空间,test2=1;
memory[k] = page[i]; //新的页面进入内存
sign[k] = 0; //访问时间置0
break;
}
}
if (test2 == 0) //如果内存已满
{
index = 0;
for (k = 0; k < n; k++)
{
if (sign[index] < sign[k])
index = k; //找出上次访问到现在最久的页面,找到下标index
}
memory[index] = page[i]; //替换该页面
}
}
}
printf("内存容量为%d页时命中率为%lf", n, count / N);
printf("\n");
free(memory);
free(sign);
}
//第二次机会页面替换算法(SCR)
void SCR(int n) {
int* memory = (int*)malloc(sizeof(int) * n); // 内存空间
int* refBit = (int*)malloc(sizeof(int) * n);
int* counter = (int*)malloc(sizeof(int) * n);
int hitCount = 0; // 命中次数
int initialHand = 0; // 初始hand位置
int found = 0; // 用于标记页面是否在内存中
int i, j;
int hand = 0;
// 初始化内存、引用位和访问时间
for (i = 0; i < n; i++) {
memory[i] = -1; // 假设初始时没有页面在内存中
refBit[i] = 0; // 引用位初始化为0
counter[i] = 0; // 访问时间初始化为0
}
// 遍历页面序列
for (i = 0; i < N; i++) {
int pageFault = page[i]; // 当前页面
// 查找页面是否在内存中
for (j = 0; j < n; j++) {
if (memory[j] == pageFault) {
found = 1; // 页面在内存中
refBit[j] = 0; // 访问了该页面,将引用位设置为0
counter[j] = 0; // 重置访问时间
break;
}
}
// 页面在内存中,增加命中次数
if (found) {
hitCount++;
found = 0; // 重置found标志,为下一个页面做准备
}
else {
// 页面不在内存中,发生缺页中断
found = 0; // 重置found标志,用于下面的循环
initialHand = hand = 0; // 初始化hand并设置initialHand
do {
// 如果R位为0,则替换当前页面
if (refBit[hand] == 0) {
memory[hand] = pageFault;
refBit[hand] = 1; // 设置新页面的R位为1
counter[hand] = 0; // 重置新页面的访问时间
break;
}
// 如果R位为1,则增加访问时间并继续查找
counter[hand]++;
// 继续查找下一个页面
hand = (hand + 1) % n;
// 检查是否已经遍历了所有页面
if (hand == initialHand) {
// 如果所有页面的R位都为1,则替换hand指向的页面(即最旧的页面)
int oldest = hand;
for (j = (hand + 1) % n; j != hand; j = (j + 1) % n) {
if (counter[j] > counter[oldest]) {
oldest = j;
}
}
memory[oldest] = pageFault;
refBit[oldest] = 1; // 设置新页面的R位为1
counter[oldest] = 0; // 重置新页面的访问时间
break;
}
} while (1); // 循环直到找到可替换的页面
}
}
// 计算并输出命中率
printf("内存容量为%d页时命中率为%lf\n", n, (double)hitCount / N);
// 释放内存
free(memory);
free(refBit);
free(counter);
}
//时钟页面替换算法(Clock)
void Clock(int n) {
int hitCount = 0; // 命中次数
int hand = 0; // 模拟循环队列的指针(hand in hand)
int i;
int* memory = NULL;
memory = (int*)malloc(sizeof(int) * n); //内存空间
int* refBit = NULL;
refBit = (int*)malloc(sizeof(int) * n);
// 初始化内存和引用位
for (i = 0; i < n; i++) {
memory[i] = -1; // 假设初始时没有页面在内存中
refBit[i] = 0; // 引用位初始化为0
}
// 遍历页面序列
for (i = 0; i < N; i++) {
int pageFault = page[i]; // 当前页面
int found = 0; // 标记页面是否在内存中
// 查找页面是否在内存中
for (int j = 0; j < n; j++) {
if (memory[j] == pageFault) {
found = 1; // 页面在内存中
refBit[j] = 0; // 访问了该页面,将引用位设置为0
break;
}
}
// 页面在内存中,增加命中次数
if (found) {
hitCount++;
}
else {
// 页面不在内存中,发生缺页中断
// 查找第一个R位为0的页面或者循环回到开始并重置所有R位
do {
if (refBit[hand] == 0) {
// 找到R位为0的页面,替换它
memory[hand] = pageFault;
refBit[hand] = 1; // 设置新页面的R位为1
break;
}
else {
// 如果没有找到,则继续查找下一个页面,或者重置所有R位
hand = (hand + 1) % n; // 循环队列
if (hand == 0) { // 如果回到开始,重置所有R位
for (int k = 0; k < n; k++) {
refBit[k] = 0;
}
}
}
} while (hand != 0 || refBit[hand] == 1); // 循环直到找到可替换的页面或回到开始
}
}
// 计算并输出命中率
printf("内存容量为%d页时命中率为%lf\n", n, (double)hitCount / N);
free(memory);
free(refBit);
}
//主函数
void main()
{
printf("----------------------------------初始化操作----------------------------------\n");
Init(); //初始化
printf("\n");
printf("---------------------------------最佳淘汰算法(OPT)---------------------------------\n");
for (int i = 4; i <= 32; i++)
{
OPT(i); //最佳淘汰算法,用户的内存容量4页到32页
}
printf("\n");
printf("--------------------------------先进先出算法(FIFO)---------------------------------\n");
for (int j = 4; j <= 32; j++)
{
FIFO(j); //先进先出算法,用户的内存容量4页到32页
}
printf("\n");
printf("------------------------------最近最久未使用算法(LRU)------------------------------\n");
for (int t = 4; t <= 32; t++)
{
LRU(t); //最近最久未使用算法,用户的内存容量4页到32页
}
printf("\n");
printf("------------------------------第二次机会页面替换算法(SCR)------------------------------\n");
for (int b = 4; b <= 32; b++)
{
SCR(b); //第二次机会页面替换算法,用户的内存容量4页到32页
}
printf("\n");
printf("------------------------------时钟页面替换算法(Clock)------------------------------\n");
for (int t = 4; t <= 32; t++)
{
Clock(t); //时钟页面替换算法,用户的内存容量4页到32页
}
system("pause");
}