16281053_杨瑷彤_操作系统第四次实验
16281053_杨瑷彤_操作系统第四次实验
源代码链接:https://github.com/rdjyat/operating-system/tree/master/操作系统实验四
1. 实验目的
设计和实现最佳置换算法、先进先出置换算法、最近最久未使用置换算法、页面缓冲置换算法;
通过页面访问序列随机发生器实现对上述算法的测试及性能比较。
2. 实验说明
模拟的虚拟内存的地址为16位,页面大小为1K
模拟的物理内存有32K
页表用整数数组或结构数组来表示
页面访问序列串是一个整数序列,整数的取值范围为0到N - 1。页面访问序列串中的每个元素p表示对页面p的一次访问
页面访问序列随机生成:
符合局部访问特性的随机生成算法
确定虚拟内存的尺寸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步,否则结束。
性能测评:
测试不同的页面访问序列及不同的虚拟内存尺寸,并从缺页率、算法开销等方面对各个算法进行比较。(同时请给出在给定页面访问序列的情况下,发生页面置换次数的平均值)
3.实验设计
3.1 页面访问序列串随机生成
3.1.1 算法设计
1) 确定虚拟内存的尺寸N,工作集的起始位置p,工作集中包含的页数e,工作集移 动率m(每处理m个页面访问则将起始位置p +1),以及一个范围在0和1之间 的值t;
2) 生成m个取值范围在p和p + e间的随机数,并记录到页面访问序列串中;
3) 生成一个随机数r,0 ≤ r ≤ 1;
4) 如果r < t,则为p生成一个新值,否则p = (p + 1) mod N;
5) 如果想继续加大页面访问序列串的长度,请返回第2步,否则结束。
3.1.2 算法流程图
代码:
void getPageAccessSequence() {
double t = 0.6;
int sum = 0;
int i;
while (sum + m <= lengthOfSequence) {
for (i = 0; i<m; i++) {
workingSet[sum + i] = (rand() % e + p) % N;
}
double r = rand()*1.0 / RAND_MAX;
if (r<t) {
p = rand() % N;
}
else {
p = (p + 1) % N;
}
sum += m;
}
}
3.2 最佳置换算法
3.2.1 基本思想
选择永不使用或是在最长时间内不再被访问(即距现在最长时间才会被访问)的页面淘汰出内存。
1、当物理块未满时,按物理块号大小遍历,寻找空闲物理块。
2、当物理块满,需进行替换时,初始化各物理内存块最近访问排名(为语言所支持的最大值)。通过遍历剩余访问序列,确定每个物理块中所存放的逻辑块最近会被访问的排名。找出排名最大的物理块作为替换。
3.2.2 算法流程图
代码:
int optimal(int index) {
int number = workingSet[index];
int i;
for (i = 0; i<(sizeof(mm) / sizeof(mm[0])); i++) {
if (mm[i] == -1) {
mm[i] = number;
return 1;
}
}
for (i = 0; i<(sizeof(mm) / sizeof(mm[0])); i++) {
if (mm[i] == number)
return 1;
}
for (i = 0; i<(sizeof(mm) / sizeof(mm[0])); i++) {
visit[i] = 0;
}
int count = 0;
for (i = index + 1; i<lengthOfSequence; i++) {
int x = workingSet[i];
int j;
for (j = 0; j<(sizeof(mm) / sizeof(mm[0])); j++) {
if (mm[j] == x) {
if (visit[j] == 0) {
visit[j] = 1;
count++;
if (count == (sizeof(mm) / sizeof(mm[0])) - 1) {
break;
}
}
break;
}
}
if (count == (sizeof(mm) / sizeof(mm[0])) - 1) {
break;
}
}
for (i = 0; i<(sizeof(mm) / sizeof(mm[0])); i++) {
if (visit[i] == 0) {
mm[i] = number;
return 0;
}
}
return 0;
}
3.3 先进先出置换算法
3.3.1 基本思想
(1)选择最先进入内存即在内存驻留时间最久的页面换出到外存
(2)进程已调入内存的页面按进入先后次序链接成一个队列,并设置替换指针以指向最老页面
3.3.2 算法流程图
代码:
int fifo(int index) {
int number = workingSet[index];
int i;
for (i = 0; i<(sizeof(mm) / sizeof(mm[0])); i++) {
if (mm[i] == -1) {
mm[i] = number;
return 1;
}
}
for (i = 0; i<(sizeof(mm) / sizeof(mm[0])); i++) {
if (mm[i] == number)
return 1;
}
mm[pointer] = number;
pointer = (pointer + 1) % (sizeof(mm) / sizeof(mm[0]));
return 0;
}
3.4 最近最久未使用置换算法
3.4.1 基本思想
以“最近的过去”作为“最近的将来”的近似,选择最近一段时间最长时间未被访问的页面淘汰出内存。
1、通过设置一个物理块访问的动态数组,每次访问,如果待访问的物理块在动态数组中,则把其移到数组尾部;
2、如果不在,当物理块未满时,按物理块号大小遍历,寻找空闲物理块。
3、如果不在,当物理块满时,将动态数组的头部的物理块选为替换块。
3.4.2 算法流程图
代码:
int LRU(int index) {
int number = workingSet[index];
int i;
for (i = 0; i<(sizeof(mm) / sizeof(mm[0])); i++) {
if (mm[i] == number) {
int j = i;
while (j<(sizeof(mm) / sizeof(mm[0])) && mm[j] != -1)
j++;
j--;
int k;
for (k = i; k < j; k++) {
mm[k] = mm[k + 1];
}
mm[(sizeof(mm) / sizeof(mm[0])) - 1] = number;
return 1;
}
}
for (i = 0; i<(sizeof(mm) / sizeof(mm[0])); i++) {
if (mm[i] == -1) {
mm[i] = number;
return 1;
}
}
for (i = 0; i < (sizeof(mm) / sizeof(mm[0])) - 1; i++) {
mm[i] = mm[i + 1];
}
mm[(sizeof(mm) / sizeof(mm[0])) - 1] = number;
return 0;
}
3.5 改进型clock算法
3.5.1 基本思想
1、当物理块未满时,按物理块号大小遍历,寻找空闲物理块。
2、当物理块满时,按物理块号大小,从小到大,扫描循环队列,寻找访问位为假且修改位为假的,将所遇到的第一个作为所选中的淘汰块。
3、如果(2)没找到,则寻找访问位为假且修改位为真的,将所遇到的第一个作为所选中的淘汰块。将所有扫描过的块的访问位都置为0;
4、如果(3)没找到,重复(2)和(3),必定能找到淘汰块。
代码:
int clock(int index) {
int number = workingSet[index];
int i;
for (i = 0; i<(sizeof(mm) / sizeof(mm[0])); i++) {
if (mm[i] == -1) {
mm[i] = number;
visit[i] = 1;
return 1;
}
}
for (i = 0; i<(sizeof(mm) / sizeof(mm[0])); i++) {
if (mm[i] == number) {
visit[i] = 1;
return 1;
}
}
while (1) {
if (visit[pointerForClock] == 1) {
visit[pointerForClock] = 0;
}
else {
mm[pointerForClock] = number;
visit[pointerForClock] = 1;
pointerForClock = (pointerForClock + 1) % (sizeof(mm) / sizeof(mm[0]));
break;
}
pointerForClock = (pointerForClock + 1) % (sizeof(mm) / sizeof(mm[0]));
}
return 0;
}
4.性能测试
为了测试在不同的页面访问序列及不同的虚拟内存尺寸下,不同淘汰算法发生的页面置换情况,采用控制变量法进行处理。
部分访问序列如下:
对该序列测试结果:
可见,对一个页面访问序列,发生页面置换次数的平均值为21071
4.1 不同内存尺寸各类算法的缺页率影响
变量: 内存尺寸
恒量如下:
所有算法使用页面访问序列:70120304230321201701
可见,当 memory=6 时,各类算法的缺页率开始稳定,因此,我们选择 memory=6 来继续进行下面的实验。
4.2 不同访问序列长度对各类算法的缺页率的影响
变量: 访问序列长度
恒量如下:
(1)实际内存的大小=6
(2)虚拟内存的尺寸 N=20
(3)工作集中包含的页数 e=10
(4)工作集移动率 m=20
5.实验分析
通过实验数据可以看出,最佳替换算法的缺页率最低,且最为稳定,算法最优,但它是理想化算法,实际上却难于实现(假设后面的序列已知,但实际情况是后面序列无法预测),故主要用于算法评价参照。
先进先出置换算法简单直观,但不符合进程实际运行规律,性能较差,故实际应用少。
最久最近未使用置换算法是用序列的前部来推测后部,适用于大部分类型的程序,性能较好,现在也处于主流地位,但需要较多的硬件支持。
Clock算法需要根据页面内存是否被访问来决定是否置换该页面,性能也较好,但选择淘汰页可能要经过多次扫描。
将算法之间进行比较,最优算法比 LRU 算法要优越,原因是最优算法可以预知下次出现的访问页是哪个,而 LRU 算法只能预测最近最久未使用的那个页面是最不可能先被访问的,所以, LRU 算法有一定的误差,缺页率较高。从算法开销来说,FIFO是最容易实现的,只需有一个指向最老的物理块指针即可,时间复杂度为O(1);最佳置换需为其每个物理内存块,设置最近访问次序位,仅选出最小的最近访问次序物理块的时间复杂度为O(n),通过优化可达O(log_2N);LRU需使用一个栈来淘汰栈底的物理块,而判断栈内是否有待访问的物理块时间复杂度需O(n),移出与移入栈时间复杂度为O(1);Clock算法需为每个物理块设置访问位和修改位,在寻找待淘汰物理块的时间复杂度为O(n)。