操作系统实验四 页面置换算法
16281049 王晗炜 计科1601
实验目的
- 设计和实现最佳置换算法、先进先出置换算法、最近最久未使用置换算法、页面缓冲置换算法
- 通过页面访问序列随机发生器实现对上述算法的测试及性能比较
基础知识
进程运行时,若其访问的页面不在内存而需将其调入,但内存已无空闲空间时,就需要从内存中调出一页程序或数据,送入磁盘的对换区。
选择调出页面的算法就称为页面置换算法。好的页面置换算法应有较低的页面更换频率,也就是说,应将以后不会再访问或者以后较长时间内不会再访问的页面先调出。
工作集:多数程序都显示出高度的局部性,也就是说,在一个时间段内,一组页面被反复引用。这组被反复引用的页面随着时间的推移,其成员也会发生变化。有时这种变化是剧烈的,有时这种变化则是渐进的。我们把这组页面的集合称为工作集。
缺页率 = 缺页中断次数/页面访问次数。
概要设计
-
数据结构
在页面置换程序中需要用到的主要数据为页面调用序列和当前访问页面项,前者我们可以简单地使用整数数组来表示,因此不用进行设计,我们只需定义一个当前访问页面项的结构体,其设计如下:
成员名 类型 作用 id int 表示该项对应的页号 exist int 表示该项对应的存在位 modify int 表示该项对应的修改位 这里的id成员在之后的四个页面置换算法中都会被运用到(对应生成的页面调用序列),而成员exist虽然在每个算法中均被使用,但其对应的含义却各不相同,以下给出详细解释:
算法 对应意义 OPT(最佳置换算法) 该页号在页面调用序列中的下一次调用对应的序号 FIFO(先进先出置换算法) 该页号第一次进入当前访问页面对应的序号 LRU(最近最久未使用置换算法) 该页号最后一次进入当前访问页面对应的序号 Clock(改进型Clock置换算法) 该页号在Clock改进算法中的存在位 最后一个成员modify只在Clock算法中出现,表示该项对应的修改位。
对于页面缓冲算法(PBA),我们还需准备一个空闲链表来进行页面的缓冲,这里直接使用STL中的
list
结构,将类型设置为之前自行定义的访问页面项Item
。以上就是额外定义的全部结构,下面给出其他定义的一些数据:
数据名 意义 sequence 页面调用序列号 N 虚拟内存的尺寸,页面号不大于其 p 工作集的起始位置 t p赋新值的阈值,随机数大于其则赋新值 e 工作集中包含的页数 m 工作集移动率 replace_num 算法发生置换的次数 len_sequence 页面调用序列的长度 len_memory 当前使用页面的长度 完成了数据结构的定义,我们可以继续进行程序模块的设计。
-
程序模块和接口设计
-
序列数据产生模块
良好的数据系列是性能测试的有效性的基础,在完成序列较高可靠性和随机性的基础上还要有一定限制条件,因此在此模块中我们不事先传入参数,在模块开始之初自行输入相关参数值(N,p,t等),再根据实验要求中的算法产生序列。
-
算法接入模块
在本次实验中我们一共需要实现5个不同的页面置换算法,虽然这五个算法的内核不尽相同,但需要传入的参数大体一致,我们为每个算法实现函数传入三个参数:sequence、len_sequence、len_memory。其具体含义可见上表。每个函数的返回值均为double类型的数值,代表对应的缺页率。
-
性能测试模块
性能测试模块功能为模拟整个页面调用置换的过程,不需要额外的参数传入,只需在其中调用序列数据产生模块之后再逐个调用算法接入模块进行模拟,与之同时打印出每个算法的缺页率进行比较。
-
-
算法原理
-
序列产生算法
-
基本思想
- 确定虚拟内存的尺寸 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置换算法
-
基本思想
改进型的Clock算法需要综合考虑某一内存页面的访问位和修改位来判断是否置换该页面。在实际编写算法过程中,同样可以用一个等长的整型数组来标识每个内存块的修改状态。访问位A和修改位M可以组成一下四种类型的页面。
- (A =0, M = 0):表示该页面最近既未被访问,又未被修改,是最佳淘汰页。
- (A =0, M = 1):表示该页面最近未被访问,但已被修改,并不是很好的淘汰页。
- (A =1, M = 0):表示该页面最近已被访问,但未被修改,该页有可能再被访问。
- (A =1, M = 1):表示该页最近已被访问且被修改,该页可能再被访问。
处理步骤如下:
- 从查寻指针当前位置起扫描内存分页循环队列,选择A=0且M=0的第一个页面淘汰;若未找到,转2;
- 开始第二轮扫描,选择A=0且M=1的第一个页面淘汰,同时将经过的所有页面访问位置0;若不能找到,转1。
-
流程图
-
-
页面缓冲算法(PBA)
-
基本思想
当需要置换页面时,采用FIFO从所有以分配页面中选择最先进入的页面淘汰。该淘汰页进入空闲页面队列。从空闲页面队列选择一个分配给进程用于缺页的读入。如果一段时间后没有再次使用,则依次淘汰;否则,则回到已装入页面表中。如果一段时间后该页没有再次使用,则将会被淘汰;如果在淘汰前,一个进程再次需要访问的该页面,则把它放回到进程已装入页面表中。
页面缓冲算法不同于之前的几个置换算法,其缺页率不受本身的影响,而是通过如此一种缓冲方式来减少页面置换的时间,而这在此程序中难以体现,因此之后不会将此算法同以上几个算法一起测试比较。
-
流程图
-
-
详细设计
根据以上概要设计,我们逐个对函数具体实现。
-
随机页面访问序列产生算法函数
-
实现源码及注释
// 随机页面访问序列生成函数 int *RandomSequenceGenerator() { // 按照顺序输入各初始化参数 cout << "Please input the necessary parameter!" << endl; cout << "Virtual Memory Size = "; cin >> N; cout << "Pages Working Cluster Contained = "; cin >> e; cout << "Move Ratio = "; cin >> m; cout << "Random Coefficient = [0,1]"; cin >> t; cout << "Initial Point = "; cin >> p; cout << "Sequence Length:"; cin >> len_sequence; // 为初始化序列动态分配内存空间 int *sequence; sequence = (int*)malloc(len_sequence * sizeof(int)); // 按照给出算法赋予序列值 for (int i = 0; i < len_sequence / m; i++) { for (int j = 0; j < m; j++) { // 在p至p+e中生成随机序列值 sequence[i*m + j] = ((rand() % e) + p) % N; } // 每生成m个序列更新p的值 double r = (rand() % 10) / 10.; if (r < t) p = rand() % N; else p = (p + 1) % N; } // 返回生成的页面序列 return sequence; }
-
测试结果
这里我们对每个实现的函数先进行模块化的单元测试,逐个验证其正确性后再继续进行综合测试。针对此函数,我们的测试内容为输入完相关参数之后打印产生的序列,观察产生的序列是否满足相关需求:
有图可见序列可按照算法的流程随机生成一段页号序列,该函数逻辑正确,可在之后的系统测试中使用。
-
-
最佳置换算法函数(OPT)
-
实现源码及注释
// 最佳置换算法函数 double OPT(int sequence[],int len_sequence,int len_memory) { // 当前访问页面块中存在的项数 int full_num = 0; // 页面置换数 int replace_num = 0
-