1、实验报告优先级作业调度系统的模拟课程名称: 数据结构大型试验 实验项目名称: 优先级作业调度系统的模拟 学院: 计算机科学与技术学院 专业: 计算机科学与技术 指导教师: 刘端阳 报告人: 学号: 班级: 目录1.实验内容分析31.1 实验目的1.2 实验要求.1.3 设计分析.2.试验验证分析2.1 输入的形式及输入值的范围2.2 程序所能达到的结果2.3 测试数据3.测试分析3.1 基础问题.3.2 技术问题.3.3 调试错误4.调试结果分析4.1 程序的运行结果.5.附录一、实验内容分析:实验目的:Windows、Linux 等操作系统都支持同时运行多个作业,但作业的执行顺序却因调度算法
2、的不同而不同。通常,操作系统都采用优先级作业调度,即操作系统根据作业的长短来设置优先级大小,优先级高的作业先执行,优先级低的作业后执行。作业调度的详细情况如下描述:一个作业 Ji 的长度为 ti =(si,ei) ,si 为作业运行的开始时间(进入时间) ,ei 为作业运行的结束时间(离开时间) ,ti 则为完成作业 Ji 所需要的执行时间(单位:秒) 。作业调度的基本任务是从作业队列中选取一个来执行,如果没有作业则执行空操作操作。而优先级作业调度,是指每次选取优先级最高的作业来调度,优先级可以用优先数(每个作业一个优先数 pi)来表示,优先数越小,优先级越高。作业 Ji 进入系统时,即 si
3、 时刻,系统给该作业指定其初始优先数 pi = ti,从而使越短的作业优先级越高。该优先数在作业等待调度执行的过程中会不断减小,调整公式为:pi = pi - wi,其中 wi 为作业 Ji 的等待时间:wi = 当前时间-si。一旦作业被调度,该作业就一直执行,不能被抢占,只有当前执行的作业完成时,才产生下一轮调度。所以需要在每次调度前动态调整各作业的优先数。在每次调度的时候,如果出现相同优先级的作业,则按照先进先出(FIFO: First In First Out)的原则进行调度。实验要求:1. 要求自己编程实现堆结构及其相关功能,从而实现优先级队列,不允许使用标准模板类的堆函数和优先级队
4、列;测试时,各种情况都需要测试,并附上测试截图;2. 要求采用类的设计思路,不允许出现类以外的函数定义,但允许友元函数。主函数中只能出现类的成员函数的调用,不允许出现对其它函数的调用。3. 要求采用多文件方式:.h 文件存储类的声明,.cpp 文件存储类的实现,主函数 main 存储在另外一个单独的 cpp 文件中。如果采用类模板,则类的声明和实现都放在.h 文件中。4. 要求源程序中有相应注释;5. 不强制要求采用类模板,也不要求采用可视化窗口;6. 要求测试例子要比较详尽,各种极限情况也要考虑到,测试的输出信息要详细易懂,表明各个功能的执行正确,包括何时作业进入,何时调度哪个作业,何时离开
5、,每个作业等待多长时间,优先数的动态变化情况等;7. 要求采用 Visual C+ 6.0 及以上版本进行调试;设计分析: 类的设计Work:自定义的作业类。MyHeap:自定义的优先级队列,帮助工程类的实现System:模拟作业调度系统定义的工程类,模拟处理作业的过程。类的关系图 基本数据结构类的设计:MyHeap(优先级队列):利用自定义的最小堆实现优先级队列,实现主要的功能,包括作业的插入、最小优先级作业的提取和删除、各个作业优先级的修改等,优先级队列采用的是模板类 MyHeap(); /队列的构造函数 void pop();/删除队首元素,并更新队列 void push(const D
6、ATA/在队列中加入新项,并更新队列 DATA top();/返回队首的元素 bool empty();/判断队列知否为空 int size();/返回队列的中元素的个数 void update();/将队列中每一项的优先级减一 void show(); / 显示队列的所有信息 作业类以及工程类的设计Work(作业类)System(工程类)MyHeap(优先级队列类)数据类型实现工具MyHeapempty size top popVector mhupdatepush 成员函数数据成员showWork(作业类): int s ;/ 作业进入的时间 int t;/作业的执行时间 int p;/作
7、业的优先级 int num;/作业标号 Work();/无参构造函数 Work(int n,int a,int b);/有参构造函数 Work / / 自减操作重载 Work/ 赋值操作重载 friend ostreamWork wk;bool isworkint T,end,SIZE void run ();/自动运作的工程srand(time(0); / 把时间作为种子,若不调用此函数,产生的随机数都是伪随机的,每次程序运行的结果都一样int tol = 0; / 表示作业的编号for(T=0; T3 函数调用形式 Sleep(500);/单位是毫秒 技术问题 1. 作业关系大小的设计错误
8、理解:以为只要比较作业的优先级就可以了,这样设计无法实现先进先出的原则解决:设计作业大小比较时,优先考虑作业的优先级,如果优先级一样的话,再根据作业的 num 值比较大小(即进入系统的先后顺序)2.优先队列的设计难点: 1 调整节点条件的分析当二叉树只有一个节点时,不需要进行下调工作因为进行下调操作的是一个最小堆,只要被调整元素比它的两个子节点都小,就可以直接跳出循环节点比较不需要考虑相等的情况,因为每个作业的 num 值一定是不一样的,所以就算优先级一样,也一定不会相等 调试错误1. 编写最小堆的时候,有几个函数不小心写成了最大堆的效果,找了好久的错误2. Work 类,在编写赋值操作符重载
9、时,用的是成员函数,却在函数里新创了个对象,结果出现了错误4、测试结果分析: 程序的运行结果刚开始只产生了一个作业,所以就执行改作业 产生了两个作业,且此时没有作业在执行,作业 4 的优先级比作业 3 大,所以先执行作业4作业 2、5 的优先级是相同的,而作业 2 先进入队列,所以先执行作业 2作业执行时,输出执行语句,并调用 Sleep 函数,表示正在执行5、附录Heap.h#include #include using namespace std;#ifndef MYHEAP#define MYHEAP / 防止因头文件被反复调用,而导致重复定义/ 应用模板类template class
10、MyHeapvector mh; / 用向量实现堆public:MyHeap(); / 构造函数bool empty(); / 判空函数DATA top(); / 取队首元素void pop(); / 删除队首元素void push(const DATA / 压入元素void update(); / 所有元素 -1int size(); / 获取元素个数void show(); / 调用 graph()private:void push_down(); / 向下更新void push_up(); / 向上更新void swap(DATA / 交换两个元素bool can_push(int po
11、s); / 判断是否需要向下更新void graph(int s); /输出队列的所有信息 ;template MyHeap:MyHeap()mh.clear(); / 清空队列template bool MyHeap:empty()return mh.size() = 0; / 判断向量里是否有元素template DATA MyHeap:top()return mh0; / 向量的第一个元素就是队首template void MyHeap:swap(DATAa = b;b = c;template bool MyHeap:can_push(int pos)int l = mh.size()
12、;/ 若没有左节点 或 当前节点小于左节点 且 没有右节点 或 当前节点小于右节点,那么就不需要再下移if(pos*2+1 = l | mhpos = l | mhpos void MyHeap:push_down()int pos = 0,l = mh.size();while(can_push(pos) / 若需要下移,则一定有左儿子,因为堆是一个完全二叉树int tar = pos*2+1; / tar 先指向左节点if(pos*2+2 void MyHeap:pop() swap(mh0,mhmh.size()-1); / 将队首元素放到最后vector:iterator it = m
13、h.end(); / 迭代器先指向 end();it-; / 自减操作,此时 it 指向最后一个元素mh.erase(it); / 删除最后一个元素,即删除了原队首元素push_down(); / 从队首向下更新template void MyHeap:push_up()int now = mh.size()-1,tar;while(now 0) / 从最后一个元素开始,直到队首tar = (now-1)/2; / tar 指向当前节点的父节点if(mhtar void MyHeap:push(const DATA / 先将新节点加到最后push_up(); / 再向上更新template v
14、oid MyHeap:update() / 所有元素 -1for(int i=0; iint MyHeap:size() return mh.size(); / 返回向量的大小template void MyHeap:show()graph(0);template void MyHeap:graph(int s) / 递归调用,输出以 s 为根节点的子树if(s*2+1 using namespace std;#ifndef MYWORK#define MYWORKclass Workpublic:int s,t,p,num;Work();Work(int n,int a,int b);fri
15、end bool operator#include #include #include / 系统自带的头文件都可以#include “Heap.h“ /自己写的头文件,要用引号#include “Work.h“#include using namespace std;class SystemMyHeap mw; / 队列,存储所有工作int T; / 记录系统运行的时间int end; / 如果有作业在运行,记录该作业结束时的系统时间bool iswork; / 是否有作业正在运行Work wk; / 若有作业在运行,记录该作业的信息int SIZE; / 系统允许新增工作的时间,超过此时间后
16、,系统执行完所有作业后,就结束public:System(); / 构造函数,在创建变量时才会调用bool run(); / 执行函数,模拟作业的调度;#endifWork.cpp#include “Work.h“Work:Work()Work:Work(int n,int a,int b) / 构造函数num = n; / 作业的编号s = a; / 作业进入的时间t = p = b; / 作业的执行时间和优先级bool operatorD;if(D SIZE;if(SIZE L;if(L N;if(N = 0)printf(“产生作业数的上限要大于 0!n“);Sleep(500);ret
17、urn false;srand(time(0); / 把时间作为种子,若不调用此函数,产生的随机数都是伪随机的,每次程序运行的结果都一样int tol = 0; / 表示作业的编号for(T=0; TSIZE; T+) / 这段时间会随机产生新的作业mw.update(); / 因为等待时间 +1,所以队列里所有的作业的优先级 -1if(iswork / 表示没有作业在运行cout“*程序运行时间为“T“, 作业“wk.num“处理完毕,用时“wk.t“,等待“T-wk.t-wk.s“nn“;/ 输出信息if(iswork) / 若有程序在运行cout“程序运行时间为“T“, 正在执行作业“w
18、k.num“.n“;Sleep(500); / 滞留 0.5s,表示正在运行if(T%D = 0) / 如果是 10 的倍数int num = rand()%N+1,t; / 随机产生 1-3 个新作业printf(“*新加入%d 作业:n“,num); for(int i=0; inum; i+)t = rand()%L+1; / 随机产生作业的执行时间mw.push(Work(+tol,T,t); / 调用构造函数printf(“作业%d,进入时间为%d,需执行时间为%d,优先级为%dn“,tol,T,t,t);/ 输出新作业的信息printf(“n“);/ 输出更新后的队列的信息prin
19、tf(“*此时共有%d 作业等待处理nn“,mw.size();mw.show(); / 输出队列里的所有的作业信息,无序printf(“nn“);if(!iswork mw.pop(); / 取出队首元素printf(“*开始执行作业%d,进入时间为%d,等待时间为%d,需执行时间%d,优先级为%dn“,wk.num,wk.s,T-wk.s,wk.t,wk.p);end = T+wk.t; / 更新结束时间iswork = true;while(!mw.empty() | iswork) / 不再加入新作业,将剩余的所有作业运行完if(iswork printf(“*作业%d 处理完毕,用时
20、%d,等待%dnn“,wk.num,wk.t,T-wk.t-wk.s);if(iswork) printf(“程序运行时间为%d, 正在执行作业%d.n“,T,wk.num);Sleep(500);if(!iswork mw.pop();printf(“*开始执行作业%d,进入时间为%d,等待时间为%d,需执行时间%d,优先级为%dn“,wk.num,wk.s,T-wk.s,wk.t,wk.p);end = T+wk.t;iswork = true;T+;printf(“执行完毕n“);return true;do.cpp#include “System.h“int main()System s;while(!s.run();return 0;