实验四: 页面置换
一、实验目的
设计和实现最佳置换算法、先进先出置换算法、最近最久未使用置换算法、改进型Clock置换算法、页面缓冲置换算法;通过页面访问序列随机发生器实现对上述算法的测试及性能比较。
二、实验环境
Windows 10 ,Visual Studio 2015
三、概要设计
1.数据结构
成员名 | 类型 | 作用 |
data | int | 页号 |
flag | int | 访问位 |
modify | int | 修改位 |
根据各个算法的特点,程序实现的过程中用到的数据结构主要有以下三种:
- 数组:定义的时候利用指针定义,然后根据全局变量block设定的给进程分配的物理内存的块数动态分配内存。一旦完成内存分配,不再改变数组的大小。
用到数组结构来实现的算法程序有:最佳置换算法,先进先出置换算法,最近最久未使用置换算法,改进型clock置换算法。
- 队列:为单向队列,队列长度仍然由全局变量指定。
用到队列的算法程序有:先进先出置换算法。
队列结点元素的结构体如下:
typedef struct node
{
int num;//页号
node* next;//下一个结点页面
} Node, *pNode
typedef struct queue
{
int n;//总的结点数
pNode front;//队首指针
pNode rear; //队尾指针
} Queue, *pQueue;
- 链表:主要是将装入内存的页块串联起来。
用到链表的算法程序:页面缓冲算法。
链表结点元素的结构体如下:
struct LNode
{
int data;//页号
int flag;//访问位
int modify;//修改位
LNode* next;
};
struct Link
{
int num;//当前链表上的结点数
LNode* next;
};
2.模块和接口
- 页面访问序列随机生成模块
在此模块中不事先传入参数,在模块开始时置入相关参数值(N,p,t等),再根据实验要求中的算法产生序列。
- 算法接入模块
在本次实验中需实现五个算法。需要为这五个不同的算法传入参数,每个函数的返回值均为double类型的数值,代表对应的缺页率。
- 测试模块
模拟页面置换过程,调用页面访问序列随机生成模块后再调用算法接入模块进行模拟,并打印缺页率。
3.算法实现
- 页面访问序列随机生成算法
基本原理:
①确定虚拟内存的尺寸N,工作集的起始位置p,工作集中包含的页数e,工作集移动率m(每处理m个页面访问则将起始位置p +1),以及一个范围在0和1之间的值t;
②生成m个取值范围在p和p + e间的随机数,并记录到页面访问序列串中;
③生成一个随机数r,0 ≤ r ≤ 1;
④如果r < t,则为p生成一个新值,否则p = (p + 1) mod N;
⑤如果想继续加大页面访问序列串的长度,请返回第2步,否则结束。
流程图:
- 最佳置换算法(OPT)
主要思想:
最佳置换算法的主要思想是,在发生页面替换时,被替换的对象应该满足,在以后的页面访问中,该对象不会再次被访问或者较晚被访问。是一种理想化算法,具有最好性能(对于固定分配页面方式,本法可保证获得最低的缺页率),但实际上却难于实现,故主要用于算法评价参照。
流程图:
- 先进先出置换算法(FIFO)
主要思想:
在发生页面替换时,被替换的对象应该是最早进入内存的。
流程图:
- 最近最久未使用置换算法(LRU)
主要思想:
在发生页面替换时,被替换的页面应该满足,在之前的访问队列中,该对象截止目前未被访问的时间最长。
流程图:
- 改进型Clock置换算法
主要思想:
在每次页面替换时,总是尽可能地先替换掉既未被访问又未被修改的页面。
①从查寻指针当前位置起扫描内存分页循环队列,选择A=0且M=0的第一个页面淘汰;若未找到,转②
② 开始第二轮扫描,选择A=0且M=1的第一个页面淘汰,同时将经过的所有页面访问位置0;若不能找到,转①
流程图:
- 页面缓冲算法(PBA)
主要思想:
设立空闲页面链表和已修改页面链表采用可变分配和基于先进先出的局部置换策略,并规定被淘汰页先不做物理移动,而是依据是否修改分别挂到空闲页面链表或已修改页面链表的末尾,空闲页面链表同时用于物理块分配,当已修改页面链表达到一定长度如Z个页面时,一起将所有已修改页面写回磁盘,故可显著减少磁盘I/O操作次数。
流程图:
4.主要函数功能说明
(1)全局共享函数
void initMemo();//初始化存储空间,主要是设置分配空间的大小
void generate();//生成访问序列
bool isInMemo (int n); //指定页号是否已经在内存中
(2)最佳置换算法
void optimal (int n); //访问一个页面,执行一次最佳置换算法
void testOptimal();//算法实现函数
(3)先进先出置换算法
void initQueue (pQueue q);//初始化队列
void push (pQueue q, int num);//队列中加入新的页面结点
void pop (pQueue q);//将页面移出内存
void destroy (pQueue q);//销毁队列
bool findInQueue (pQueue q, int num);//查找页面是否已经调入内存
void generate();//生成访问序列
void fifoTest();//每访问一个页面,执行一次算法
void fifo (pQueue q, int num);//先进先出置换算法实现函数
(4)最近最久未使用置换算法
void LRU (int n);//每访问一个新的页面,执行一次LRU算法
void testLRU();//LRU算法实现函数
(5)改进型clock置换算法
void updated_Clock (int n);//改进型clock算法实现函数
void test_Clock();//每访问一个新的页面,执行一次算法
(6)页面缓冲算法
bool isInNodes (int n); //页面是否已经在链表中
void addToLink (int data, int type);//页面添加到已修改页面链表和空闲链表上
void emptyIdle();//将空闲链表上的所有页面送出内存
void emptyModi();//将已修改页面链表上所有的链表送出内存
void PBA (int n);//PBA算法实现函数
四、详细设计及测试结果
最佳置换、最近最久未使用、改进clock
// test.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "stdio.h"
#include"stdlib.h"
#include"time.h"
#define R 32 //物理内存块数
#define V 64 //虚拟内存块数
struct LNode
{
int data;
int flag;//访问位
int modify;//修改位
};
void initMemo();
void generate();//生成访问序列
bool isInMemo(int n); //
void optimal(int n); //
void testOptimal();
void LRU(int n);
void testLRU();
void updated_Clock(int n);
void test_Clock();
int block = 3;
int access[32]; //访问序列
int* memo;
int lost = 0;//没找到的页面数
int index = 0;//指示当前下标
LNode* nodes;//改进型Clock置换算法用到的数据结构
int _tmain(int argc, _TCHAR* argv[])
{
generate();
testOptimal();
testLRU();
test_Clock();
int i = 0;
for (; i < 32; i++)
{
printf("%d, ", access[i]);
}
getchar();
getchar();
return 0;
}
void generate()
{
srand((unsigned)time(NULL)); //用时间做种,每次产生随机数不一样
int p = rand() % 64;
int m = 8, e = 8;
int i, j;
double t;
t = rand() % 10 / 10.0;
for (i = 0; i < 4; i++)
{
for (j = i * m; j < (i + 1) *m; j++)
{
access[j] = (p + rand() % e) % 64;
}
double r = (rand() % 10) / 10.0;
if (r < t)
{
p = rand() % 64;
}
else
{
p = (p + 1) % 64;
}
}
}
void initMemo()
{
memo = (int*)malloc(block * sizeof(int));
int i = 0;
for (; i < block; i++)
{
memo[i] = -1;
}
return;
}
void testOptimal()
{
initMemo();
int i = 0;
printf("最佳置换算法:\n");
for (; i < 32; i++)
{
optimal(i);
printf("%d %d %d\n", memo[0], memo[1], memo[2]);
}
printf("最佳置换算法缺页率: %2f %d\n", lost / 32.0, lost);
lost = 0;
free(memo);
index = 0;
}
bool isInMemo(int n)
{
int i = 0;
for (; i < block; i++)
{
if (access[n] == memo[i])
{
return true;
}
}
return false;
}
//最佳适应算法
void optimal(int n)
{
int i = 0, j = 0;
if (isInMemo(n))
{
printf("页面已被调入内存\n");
}
else
if (index == block)
{
lost++;
int max = 0, pos, tag;
for (i = 0; i < block; i++)
{
tag = -1;
for (j = n + 1; j < 32; j++)
{
if (access[j] == memo[i])
{
tag = j;
break;
}
}
if (tag == -1)
{
max = 32;
pos = i;
break;
}
else
{
if (max < tag)
{
max = tag;
pos = i;
}
}
}
memo[pos] = access[n];
}
else
{
memo[index] = access[n];
index++;
}
}
LRU算法
void LRU(int n)
{
int i, j;
if (isInMemo(n))
{
printf("页面已被调入内存\n");
}
else
if (index == block)
{
int max = n, pos = -1, tag;
for (i = 0; i < block; i++)
{
for (j = n - 1; j >= 0; j--)
{
if (access[j] == memo[i])
{
tag = j;
break;
}
}
if (tag < max)
{
max = tag;
pos = i;
if (max == 0)
{
break;
}
}
}
memo[pos] = access[n];
lost++;
}
else
{
memo[index] = access[n];
index++;
}
}
void testLRU()
{
int i;
initMemo();
printf("最近最久未使用算法\n");
for (i = 0; i < 32; i++)
{
LRU(i);
printf("%d %d %d\n", memo[0], memo[1], memo[2]);
}
printf("最近最久未使用缺页率: %2f %d \n", lost / 32.0, lost);
lost = 0;
index = 0;
free(memo);
}
bool isInNodes(int n)
{
int i;
for (i = 0; i < block; i++)
{
if (nodes[i].data == access[n])
{
return true;
}
}
return false;
}
void updated_Clock(int n)
{
if (isInNodes(n))
{
printf("页面已被调入内存\n");
}
else
if (index == block)
{
lost++;
int i = 0, tag = -1;
while (true)
{
if ((i / block) % 2 == 0)
{
if (nodes[i % block].flag == 0 && nodes[i % block].modify == 0)
{
tag = i % block;
break;
}
}
if ((i / block) % 2 == 1)
{
if (nodes[i % block].flag == 0 && nodes[i % block].modify == 1)
{
tag = i % block;
break;
}
else
{
nodes[i % block].flag = 0;
}
}
i++;
}
nodes[tag].data = access[n];
nodes[tag].flag = 1;
if (rand() % 10 < 4)
{
nodes[tag].modify = 1;
}
else
{
nodes[tag].modify = 0;
}
}
else
{
nodes[index].data = access[n];
nodes[index].flag = 1;
if (rand() % 10 < 4)
{
nodes[index].modify = 1;
}
else
{
nodes[index].modify = 0;
}
index++;
}
}
void test_Clock()
{
int i = 0, j = 0;
printf("改进型Clock置换算法\n");
nodes = (LNode*)malloc(block * sizeof(LNode));
for (i = 0; i < block; i++)
{
nodes[i].data = -1;
nodes[i].flag = -1;
nodes[i].modify = -1;
}
for (i = 0; i < 32; i++)
{
updated_Clock(i);
for (j = 0; j < block; j++)
{
printf("%d ", nodes[j].data);
}
printf("\n");
}
printf("改进型Clock置换算法缺页率: %2f %d \n", lost / 32.0, lost);
lost = 0;
index = 0;
}
先进先出
// FIFO.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"
#include "time.h"
typedef struct node
{
int num;
node* next;
} Node, *pNode;
typedef struct queue
{
int n;
pNode front;
pNode rear;
} Queue, *pQueue;
void initQueue(pQueue q);
void push(pQueue q, int num);
void pop(pQueue q);
void destroy(pQueue q);
bool findInQueue(pQueue q, int num);
void generate();
void fifoTest();
void fifo(pQueue q, int num);
int access[32];//访问序列
int size = 3;//给进程分配的内存的大小
int lost = 0;//缺页数
int _tmain(int argc, _TCHAR* argv[])
{
generate();
fifoTest();
getchar();
getchar();
return 0;
}
void initQueue(pQueue q)
{
q->rear = (pNode)malloc(sizeof(Node));
if (q->rear == NULL)
{
printf("failed\n");
}
else
{
q->front = q->rear;
q->rear->next = NULL;
q->front->next = NULL;
q->n = 0;
}
}
void push(pQueue q, int num)
{
pNode p = (pNode)malloc(sizeof(Node));
if (p == NULL)
{
printf("failed");
}
else
{
p->next = NULL;
p->num = num;
if (q->front == q->rear)
{
q->front->next = p;
q->rear = p;
}
else
{
q->rear->next = p;
q->rear = p;
}
q->n++;
}
}
void pop(pQueue q)
{
pNode p;
if (q->front != q->rear)
{
p = q->front->next;
q->front->next = p->next;
if (p == q->rear)
{
q->front = q->rear;
}
q->n--;
free(p);
}
}
void destroy(pQueue q)
{
while (q->front != q->rear)
{
pop(q);
}
}
bool findInQueue(pQueue q, int num)
{
pNode p;
if (q->front != q->rear)
{
p = q->front->next;
while (p)
{
if (p->num == num)
{
return true;
}
else
{
p = p->next;
}
}
}
return false;
}
/*
生成具有局部访问特点的访问序列
*/
void generate()
{
srand((unsigned)time(NULL)); //用时间做种,每次产生随机数不一样
int p = rand() % 64;
int m = 8, e = 8;
int i, j;
double t;
t = rand() % 10 / 10.0;
for (i = 0; i < 4; i++)
{
for (j = i * m; j < (i + 1) *m; j++)
{
access[j] = (p + rand() % e) % 64;
}
double r = (rand() % 10) / 10.0;
if (r < t)
{
p = rand() % 64;
}
else
{
p = (p + 1) % 64;
}
}
}
void fifoTest()
{
Queue q;
pNode p;
initQueue(&q);
int i = 0;
printf("先进先出置换算法\n");
for (; i < 32; i++)
{
fifo(&q, access[i]);
p = q.front->next;
while (p)
{
printf("%d ", p->num);
p = p->next;
}
printf("\n");
}
printf("先进先出算法缺页率:%f %d\n", lost / 32.0, lost);
destroy(&q);
}
void fifo(pQueue q, int num)
{
if (findInQueue(q, num))
{
printf("页面已调入内存\n");
}
else
{
if (q->n == size)
{
pop(q);
push(q, num);
lost++;
}
else
{
push(q, num);
}
}
}
页面缓冲
// PBA.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "stdio.h"
#include"stdlib.h"
#include"time.h"
#define M 32 //物理内存块数
#define N 64 //虚拟内存块数
struct LNode
{
int data;
int flag;//访问位
int modify;//修改位
LNode* next;
};
struct Link
{
int num;//当前链表上的结点数
LNode* next;
};
void generate();//生成访问序列
bool isInNodes(int n); //
void addToLink(int data, int type);
void emptyIdle();
void emptyModi();
void PBA(int n);
int size = 3;
int p;//工作集的起始位置
int table[32];//物理内存,每一个元素代表一个页面
int access[32]; //访问序列
int memo[3] = { -1, -1, -1 };
int lost = 0;//没找到的页面数
int index = 0;//指示当前下标
LNode* nodes;//改进型Clock置换算法用到的数据结构
Link idle;
Link modified;
int _tmain(int argc, _TCHAR* argv[])
{
int i = 0, j = 0;
generate();
printf("页面缓冲置换算法(PBA)\n");
idle.num = 0;
idle.next = NULL;
modified.num = 0;
modified.next = NULL;
nodes = (LNode*)malloc(size * sizeof(LNode));
for (i = 0; i < size; i++)
{
nodes[i].data = -1;
nodes[i].flag = 0;
nodes[i].modify = 0;
nodes[i].next = NULL;
}
for (i = 0; i < 32; i++)
{
PBA(i);
for (j = 0; j < size; j++)
{
printf("%d ", nodes[j].data);
}
printf("\n");
}
printf("页面缓冲置换算法(PBA)缺页率:%f %d\n", lost / 32.0, lost);
getchar();
getchar();
return 0;
}
void generate()
{
srand((unsigned)time(NULL)); //用时间做种,每次产生随机数不一样
p = rand() % 64;
int m = 8, e = 8;
int i, j;
double t;
t = rand() % 10 / 10.0;
for (i = 0; i < 4; i++)
{
for (j = i * m; j < (i + 1) *m; j++)
{
access[j] = (p + rand() % e) % 64;
}
double r = (rand() % 10) / 10.0;
if (r < t)
{
p = rand() % 64;
}
else
{
p = (p + 1) % 64;
}
}
}
bool isInNodes(int n)
{
int i;
for (i = 0; i < 3; i++)
{
if (nodes[i].data == access[n])
{
return true;
}
}
return false;
}
LNode* isinLinks(int n)
{
LNode*p, *q;
p = idle.next;
q = NULL;
while (p)
{
if (p->data == access[n])
{
if (q != NULL)
{
q->next = p->next;
p->next = NULL;
idle.num--;
break;
}
else
{
idle.next = NULL;
}
}
q = p;
p = p->next;
}
if (p == NULL)
{
p = modified.next;
while (p != NULL)
{
if (p->data == access[n])
{
if (p == modified.next)
{
modified.next = p->next;
}
else
{
q->next = p->next;
p->next = NULL;
modified.num--;
}
if (modified.num == 0)
{
modified.next = NULL;
}
break;
}
q = p;
p = p->next;
}
}
return p;
}
void PBA(int n)
{
if (isInNodes(n))
{
printf("已装入内存\n");
}
else
if (index == size)
{
LNode *p;
if ((p = isinLinks(n)) != NULL)
{
nodes = (LNode*)realloc(nodes, (size + 1) * sizeof(LNode));
nodes[size].data = p->data;
nodes[size].flag = p->flag;
nodes[size].modify = p->modify;
nodes[size].next = p->next;
free(p);
size++;
index++;
}
else
{
lost++;//缺页
if (nodes[n % 3].modify == 1)
{
addToLink(nodes[n % 3].data, 1);
}
else
{
addToLink(nodes[n % 3].data, 0);
}
nodes[n % 3].data = access[n];
nodes[n % 3].flag = 1;
nodes[n % 3].next = NULL;
if (rand() % 10 < 4)
{
nodes[n % 3].modify = 0;
}
else
{
nodes[n % 3].modify = 1;
}
}
}
else
{
nodes[index].data = access[n];
nodes[index].flag = 1;
nodes[index].next = NULL;
if (rand() % 10 < 4)
{
nodes[index].modify = 1;
}
else
{
nodes[index].modify = 0;
}
index++;
}
}
void addToLink(int data, int type)
{
LNode* p;
LNode* q;
q = (LNode*)malloc(sizeof(LNode));
q->data = data;
q->flag = 1;
if (type == 1)
{
q->modify = 1;
p = modified.next;
}
else
{
q->modify = 0;
p = idle.next;
}
q->next = NULL;
if (p == NULL)
{
if (type == 0)
{
idle.next = q;
}
else
{
modified.next = q;
}
}
else
{
while (p)
{
if (p->next == NULL)
{
p->next = q;
break;
}
else
{
p = p->next;
}
}
}
if (type == 0)
{
idle.num += 1;
if (idle.num == 10)
{
emptyIdle();
}
}
else
{
modified.num += 1;
if (modified.num == 10)
{
emptyModi();
}
}
}
void emptyIdle()
{
LNode* p;
p = idle.next;
while (p)
{
idle.next = p->next;
free(p);
p = idle.next;
}
idle.num = 0;
}
void emptyModi()
{
LNode* p;
p = modified.next;
while (p)
{
modified.next = p->next;
free(p);
p = modified.next;
}
modified.num = 0;
}
随机生成的页面序列为17, 23, 17, 18, 23, 20, 19, 18, 21, 22, 17, 21, 23, 18, 21, 20, 22, 23, 19, 21, 20, 25, 25, 25, 26, 22, 26, 22, 23, 20, 24, 21
最佳置换:
先进先出:
最近最久未使用:
改进型clock:
页面缓冲:
内存空间块数为3
置换算法 | 最佳置换算法 | 先进先出置换算法 | 最近最久未使用算法 | 改进型clock置换算法 | 页面缓冲置换算法 | |
测试序列1 | 缺页数 | 15 | 21 | 21 | 21 | 13 |
缺页率 | 0.468750 | 0.656250 | 0.656250 | 0.656250 | 0.406250 | |
测试序列2 | 缺页数 | 20 | 23 | 22 | 21 | 20 |
缺页率 | 0.625000 | 0.718750 | 0.687500 | 0.656250 | 0.625000 | |
测试序列3 | 缺页数 | 16 | 23 | 24 | 22 | 13 |
缺页率 | 0.500000 | 0.718750 | 0.750000 | 0.687500 | 0.406250 |
内存空间块数为5
置换算法 | 最佳置换算法 | 先进先出置换算法 | 最近最久未使用算法 | 改进型clock置换算法 | 页面缓冲置换算法 | |
测试序列1 | 缺页数 | 13 | 15 | 15 | 15 | 14 |
缺页率 | 0.406250 | 0.468750 | 0.468750 | 0.468750 | 0.437500 | |
测试序列2 | 缺页数 | 11 | 15 | 14 | 14 | 14 |
缺页率 | 0.343750 | 0.468750 | 0.437500 | 0.437500 | 0.437500 | |
测试序列3 | 缺页数 | 9 | 11 | 10 | 12 | 12 |
缺页率 | 0.281250 | 0.343750 | 0.312500 | 0.375000 | 0.375000 |
根据上述结果,可以得出结论:
①同一种算法,对于不同的访问序列,其缺页率是不同,会有所变化。
②总的来看,最佳置换算法的缺页率是最低的。剩下的集中算法中,页面缓冲算法的缺页率要低于其他置换算法。改进型clock算法稍微好于先进先出算法和最近最久未使用算法。先进先出算法和最近最久未使用算法性能相近。总的来看,性能(缺页率)如下。
最佳置换算法>页面缓冲置换算法>改进型clock置换算法>最近最久未使用算法>=先进先出置换算法。
③对比内存块数和内存块数为5两种情况下的同一序列下的同一算法,可以发现,算法的缺页率还跟分配的内存块数有关系,分配的内存块数越多,缺页率越低。即导入内存的块数越多,发生缺页的可能性就越小。
GitHub源码:https://github.com/wwyw/lab4