16281004-洪华兴-操作系统实验四
实验目的及基本要求
设计和实现最佳置换算法、先进先出置换算法、最近最久未使用置换算法、页面缓冲置换算法;通过页面访问序列随机发生器实现对上述算法的测试及性能比较。
符合局部访问特性的随机生成算法
确定虚拟内存的尺寸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步,否则结束。
性能测评及问题说明
测试不同的页面访问序列及不同的虚拟内存尺寸,并从缺页率、算法开销等方面对各个算法进行比较。
(同时请给出在给定页面访问序列的情况下,发生页面置换次数的平均值)
解答:
(1)最佳置换算法(OPT)
最佳置换算法的主要思想是,在发生页面替换时,被替换的对象应该满足,在以后的页面访问中,该对象不会再次被访问或者较晚被访问。是一种理想化算法,具有最好性能(对于固定分配页面方式,本法可保证获得最低的缺页率),但实际上却难于实现,故主要用于算法评价参照
数据结构设计
数组:定义的时候利用指针定义,然后根据全局变量block设定的给进程分配的物理内存的块数动态分配内存。一旦完成内存分配,不再改变数组的大小。
具体函数实现
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++;
}
}
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;
}
(2)先进先出置换算法(FIFO)
先进先出置换算法的主要思想是,在发生页面替换时,被替换的对象应该是最早进入内存的。
数据结构
队列:为单向队列,队列长度仍然由全局变量指定。
用到队列的算法程序有:先进先出置换算法。队列结点元素的结构体如下
typedef struct node
{
int num;//页号
node* next;//下一个结点页面
} Node, *pNode;
typedef struct queue
{
int n;//总的结点数
pNode front;//队首指针
pNode rear; //队尾指针
} Queue, *pQueue;
具体函数实现
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);
}
}
}
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);
}
(3)最近最久未使用置换算法(LRU)
最近最久未使用置换算法的主要思想是,在发生页面替换时,被替换的页面应该满足,在之前的访问队列中,该对象截止目前未被访问的时间最长
数据结构设计
数组:定义的时候利用指针定义,然后根据全局变量block设定的给进程分配的物理内存的块数动态分配内存。一旦完成内存分配,不再改变数组的大小。
具体函数实现
void LRU(int n)//LRU算法实现函数
{
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()//每访问一个新的页面,执行一次LRU算法
{
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);
}
(4)改进型Clock置换算法
改进型Clock置换算法的主要思想是,在每次页面替换时,总是尽可能地先替换掉既未被访问又未被修改的页面。
数据结构设计
数组:定义的时候利用指针定义,然后根据全局变量block设定的给进程分配的物理内存的块数动态分配内存。一旦完成内存分配,不再改变数组的大小。
具体函数实现
void updated_Clock(int n)//改进型clock算法实现函数
{
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;
}
(5)页面缓冲算法
设立空闲页面链表和已修改页面链表采用可变分配和基于先进先出的局部置换策略,并规定被淘汰页先不做物理移动,而是依据是否修改分别挂到空闲页面链表或已修改页面链表的末尾,空闲页面链表同时用于物理块分配,当已修改页面链表达到一定长度如Z个页面时,一起将所有已修改页面写回磁盘,故可显著减少磁盘I/O操作次数
数据结构设计
链表:主要是将装入内存的页块串联起来。
用到链表的算法程序:页面缓冲算法。链表结点元素的结构体如下
struct LNode
{
int data;//页号
int flag;//访问位
int modify;//修改位
LNode* next;
};
struct Link
{
int num;//当前链表上的结点数
LNode* next;
};
具体函数实现
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++;
}
}
运行结果和分析
访问序列一为{14,16,14,16,15,13,13,13,18,15,15,17,17,19,16,15,15,15,13,14,18,13,17,17,3,6,4,5,2,1,6,0}
访问序列二为{49,49,49,54,49,56,49,54,60,53,58,54,56,60,53,60,58,59,56,57,54,58,55,58,8,11,13,15,9,8,12,9}
访问序列三为{16,16,22,16,20,22,19,17,23,23,18,20,22,23,20,19,25,21,21,23,22,23,20,25,20,19,21,26,25,21,23,21}
访问序列一结果截图如下:
最佳置换算法(OPT)
先进先出算法(FIFO)
最近最久未使用置换算法(LRU)
改进型clock置换算法
页面缓算法(PBA)
序列一
{14,16,14,16,15,13,13,13,18,15,15,17,17,19,16,15,15,15,13,14,18,13,17,17,3,6,4,5,2,1,6,0}
算法 | 缺页数 | 缺页率 |
---|---|---|
最佳置换算法 (OPT) | 15 | 0.469 |
先进先出置换算法 (FIFO) | 18 | 0.563 |
最近最久未使用算法(LRU) | 18 | 0.563 |
改进型clock置换算法 | 19 | 0.594 |
页面缓冲置换算法(PBA) | 15 | 0.469 |
序列二
{16,16,22,16,20,22,19,17,23,23,18,20,22,23,20,19,25,21,21,23,22,23,20,25,20,19,21,26,25,21,23,21}
算法 | 缺页数 | 缺页率 |
---|---|---|
最佳置换算法 (OPT) | 16 | 0.500 |
先进先出置换算法(FIFO) | 21 | 0.656 |
最近最久未使用算法(LRU) | 21 | 0.656 |
改进型clock置换算法 | 21 | 0.656 |
页面缓冲置换算法(PBA) | 15 | 0.469 |
序列三
{49,49,49,54,49,56,49,54,60,53,58,54,56,60,53,60,58,59,56,57,54,58,55,58,8,11,13,15,9,8,12,9}
算法 | 缺页数 | 缺页率 |
---|---|---|
最佳置换算法(OPT) | 13 | 0.406 |
先进先出置换算法(FIFO) | 20 | 0.625 |
最近最久未使用算法(LRU) | 19 | 0.594 |
改进型clock置换算法 | 19 | 0.594 |
页面缓冲置换算法(PBA) | 14 | 0.436 |
平均缺页率
算法缺页率 | 缺页率 |
---|---|
最佳置换算法(OPT) | 0.458 |
先进先出置换算法(FIFO) | 0.615 |
最近最久未使用算法(LRU) | 0.604 |
改进型clock置换算法 | 0.615 |
页面缓冲置换算法(PBA) | 0.458 |
从表中可以发现最佳置换算法和PBA的平均缺页率最低,
GitHub源码:https://github.com/hhxhongchen/lab/tree/master/lab4