实验要求
现有若干进程,每个进程的页面访问顺序已经给出,并且这些进程交替地访问页面
设定一个工作集窗口Δ和内存页面数M
用一个数据结构维护每个进程的工作集,这个数据结构可以是数组或链表
根据进程访问页面的顺序,动态更新每个进程的工作集合和内存的空闲页面数
内存页面不足时,暂停某些进程。并在内存足够时,再将其唤醒
对给出的几个进程,利用工作集模型,进行内存的管理。
内存页面总数设为1000
工作集窗口初始可设为500左右,然后改变工作集窗口的大小,观察其对实验结果的影响
跟踪每个进程访问页面过程中页错误率的变化趋势,并将其记录到相应的文件中。
利用记录的数据生成折线图,然后做出分析。(生成折线图,可使用Excel工具)
分析
用到的数据结构
虽然是C语言的程序,不过这里先采用面向对象的思路进行分析。因为根据要求,我们会需要一些数据结构,最明显的:集合(Set),因为Set的特点是元素唯一性,所以可以使用Set保存当前工作集窗口下用到的页面。
既然是面向对象,那么把工作重心放到名词上,先抽出类。
每一个进程都有一个工作集合,在每一时刻工作集合下存着当前该进程访问的页面。另外,由于内存不足或充裕的变动,进程可能被暂停,所以保留一个标志位表明进程的运行状态。进程的访问页面顺序由题目给出,因此用一个数组保留所有访问顺序。
每一个工作集窗口对应一个进程,由于颠簸,在进程访问页面时会有访问错误,因此要记录访问错误次数。
考虑到方便起见,我先用高级语言实现,省去了自己封装Set还有处理指针的麻烦,由于我使用的是Xcode环境,所以用的是oc,代码在后面的附录中,仅供参考。接下来依旧会用C语言进行实现和讲解。
程序的实现思路
几个进程依次访问页面,对于每一个进程,每次将要访问的页存入工作集,同时将不再访问的页从工作集中移除,这样工作集中的页就是要调入到内存中的页。
下一时刻,当进程准备访问新的页时,先查看工作集中有没有该页,如果有,那么可以直接访问而不会发生错误,否则会发生页访问错误,需要将新的页装入工作集。
为了方便起见,我们假定页不动,将工作集看成窗口(也就是所谓的工作集窗口),每次访问时工作集窗口后移。起初窗口从起点开始,这时访问的页还不足以填充满这个窗口,直到窗口移到最后一个元素,说明进程执行完毕。
这样,我们就能基本上确定每个类需要的操作了。主要是窗口类,要有一个将窗口后移的函数
/**
* 工作窗口后移至某一值,将移出窗口并且不用的页从内存删除
*/
void window_moveTo(ModelSetWindow *window, int end);
内存不足时将进程暂停
/**
* 内存空间不够时将该进程暂停
*/
void window_stop(ModelSetWindow *window);
对进程而言,需要每次将进程用到的页调入内存
/**将该进程用到的新的页调入内存*/
void process_addPage(Process *process, int pageNumber);
实现
常量
本次实验最麻烦的就是这些难以理解、容易混淆的概念。这里理顺一下。
内存页面总数:整个内存能容纳的最大页面数,所有进程的所有工作集窗口内页面总数不允许超过该值。
工作集窗口大小:每个进程的工作集大小,例如上图中大小为10。(虽然某一时刻下由于相同页面的出现,工作集内元素不一定等于10,上图中分别是5和2)
将它们和其它常量定义一下
//CONST.h
//bool 类型
typedef int BOOL;
#define YES 1
#define NO 0
//数组最大长度
#define MAX_LENGTH 40000
//本次实验中以int表示一个页
typedef int ELEM_TYPE;
//获取较大值
#define MAX(a,b) (((a)>(b))?(a):(b))
/**
* 内存页面总数
*/
#define kMemoryPageCount 1000
/**
* 工作集窗口大小
*/
#define kModelSetWindowSize 400
第一个数据结构:集合(Set)
Set在数据结构上的定义是,无序的,不重复的collection,这里因为是用C语言写,所以用数组表示,在方法中限制。
/**集合*/
typedef struct Set {
ELEM_TYPE data[MAX_LENGTH];
int length;
} Set;
void set_init(Set *