一、实验内容
选择一个调度算法,实现处理器调度。
二、实验目的
在采用多道程序设计的系统中,往往有若干个进程同时处于就绪状态。当就绪状态进程个数大于处理器数时,就必须依照某种策略来决定哪些进程优先占用处理器。本实验模拟在单处理器情况下处理器调度,帮助学生加深了解处理器调度的工作。
三、实验题目
设计一个按优先数调度算法实现处理器调度的进程。
(1)假定系统有5个进程,每一个进程用一个进程控制块PCB来代表。进程控制块的格式为:
进程名 |
指针 |
要求运行时间 |
优先数 |
状态 |
其中,
进程名——作为进程的标识,假设五个进程的进程名分别是P1,P2,P3,P4,P5。
指针——按优先数的大小把五个进程连成队列,用指针指出下一个进程的进程控制块首地址,最后一个进程中的指针为“0”。
要求运行时间——假设进程需要运行的单位时间数。
优先数——赋予进程的优先数,调度时总是选取优先数大的进程先执行。
状态——可假设有两种状态,“就绪”状态和“结束“状态,五个进程的初始状态都为“就绪”状态,用“R”表示,当一个进程运行结束后,它的状态变为“结束”,用“E”表示。
(2)在每次运行你所设计的处理器调度程序之前,为每个进程任意地确定它的“优先数”和“要求运行时间”。
(3)为了调度方便,把五个进程按给定的优先数从大到小连成队列,用一单元指出队首进程,用指针指出队列的连接情况。例:
(4)处理器调度总是选队首进程运行。采用动态改变优先数的办法,进程每运行次优先数就减1。由于本实验是模拟处理器调度,所以,对被选中的进程并不实际的启动运行,而是执行:优先数-1、要求运行时间-1,来模拟进程的一次运行。提醒注意的是:在实际的系统中,当一个进程被选中运行时,必须恢复进程的现场,它占有处理器运行,直到出现等待事件或运行结束。在这里省去了这些工作。
(5)进程运行一次后,若要求运行时间≠0,则再将它加入队列(按优先数大小插入,且置队首标志);若要求运行时间=0,则把它的状态修改为“结束”,且退出队列。
(6)若“就绪”状态的进程队列不为空,则重复上面(4)和(5)的步骤,直到所有进程都成为“结束”状态。
(7)在所设计的程序中应有显示或打印语句,能显示或打印每次被选中进程的进程名以及运行一次后进程队列的变化。
(8)为五个进程任意确定一组“优先数”和“要求运行时间”,启动所设计的处理器调度程序,显示或打印逐次被选中进程的进程名以及进程控制块的动态变化过程。
四、实验报告
(1)实验题目
见上文
(2)程序中使用的数据结构及符号说明
系统有5个进程,用PCB结构体表示,如图2。这5个进程都存放在一个数组中。但PCB结构体自带next指针,所以PCB结构体也是自带了头指针的、用链表实现的队列结构。队列头指针为head。PCB的name表示进程名,reqtime表示要求运行时间,rank表示优先级,state表示状态。
需要说明的是,实验指导书并未规定进程的优先级是否能够为负数。
State字符有两种值,R表示正在运行,E表示已经结束,不需要加入就绪进程的队列。
class PCB{//进程块 public: PCB* next;//指针 string name;//进程名 int reqtime;//要求运行时间 int rank;//优先级 char state;//状态,R running E end }; PCB* head; PCB* runningprocess; PCB P[N];
在对PCB指针进行“比较”时,需要比较其优先级大小,也需要比较剩余运行时间。
bool cmprank(PCB a, PCB b){//优先级从大到小比较函数 return a.rank > b.rank; } bool cmpreqtime(PCB a, PCB b){ return a.reqtime > b.reqtime; }
利用cnt变量进行计数,记录当前有几个变量没有运行结束。
int cnt=0;
(3)流程图
(4)打印一份源程序并附上注释
1.算法说明
在main函数中,开始时运行init()置进程数为0,队列head指针为空,正在运行进程为空。运行input()输入进程,输入进程名、优先级、时间,并置状态为“就绪”。将各进程按照优先级排序后,形成队列。打印程序运行时的初值。运行run()函数,对cpu的工作进行模拟。
初始化代码如下
void init() { head = NULL;//进程队列队首 runningprocess = NULL;//正在运行的进程 cnt = 0;//进程数 }
Main函数中,初始化完成、输入完成后,就能模拟cpu进行处理、调度、工作了。
模拟cpu工作时,每次选优先数最大的一个进程,将其运行一个时间单位并降低优先级后再打印各进程状态。运行完成后对进程按优先数和时间排序,对优先级进行排序时直接调用c++ stl中的algorithm库的sort,并重新按优先级对进程队列进行连接。时间为0的自动退出队列。如此循环直到所有进程运行完成,如流程图所示。
void run(PCB* head, PCB* runningprocess){ if (cnt == 0) { cout << "\n******every pcb ends******\n"; return; } cout << "\n##########process" << runningprocess->name << "running##########\n" ; runningprocess->reqtime--;//剩余运行时间-1 runningprocess->rank--;//优先级-1 cout << "-----------------every pcb status------------------"; for (int i = 0; i < cnt; i++) //显示已就绪进程 print(&head[i]); cout << "\n---------------------------------------------------\n"; if (runningprocess->reqtime == 0){//某个进程运行结束,打印提示信息 runningprocess->state = 'E';//赋予结束状态E print(runningprocess); sort(head, head + cnt, cmpreqtime);//按照进程剩余时间排序,使该结束的进行排在队尾 cnt--; } sort(head, head + cnt, cmprank);//按照进程优先级排序 for (int i = 1; i < cnt; i++) //改变指针位置,当一个进程运行结束后退出队列 head[i - 1].next = &head[i]; runningprocess = head;//下一次运行的进程为队首进程 run(head, runningprocess);//递归调用进程运行函数 }
2.完整代码
#include<iostream> #include<string> #include<iomanip> #include<algorithm> #define N 5 using namespace std; class PCB{//进程块 public: PCB* next;//指针 string name;//进程名 int reqtime;//要求运行时间 int rank;//优先级 char state;//状态,R running E end }; PCB* head; int cnt=0; PCB* runningprocess; PCB P[N]; bool cmprank(PCB a, PCB b){//优先级从大到小比较函数 return a.rank > b.rank; } void print(PCB* pcb) { cout << "\nprocess name " << pcb->name <<endl; cout << "remaining running time " << pcb->reqtime <<endl; cout << "priority rank: " << pcb->rank <<endl; cout << "status: " << pcb->state<<endl; } bool cmpreqtime(PCB a, PCB b){ return a.reqtime > b.reqtime; } void run(PCB* head, PCB* runningprocess){ if (cnt == 0) { cout << "\n******every pcb ends******\n"; return; } cout << "\n##########process" << runningprocess->name << "running##########\n" ; runningprocess->reqtime--;//剩余运行时间-1 runningprocess->rank--;//优先级-1 cout << "-----------------every pcb status------------------"; for (int i = 0; i < cnt; i++) //显示已就绪进程 print(&head[i]); cout << "\n---------------------------------------------------\n"; if (runningprocess->reqtime == 0){//某个进程运行结束,打印提示信息 runningprocess->state = 'E';//赋予结束状态E print(runningprocess); sort(head, head + cnt, cmpreqtime);//按照进程剩余时间排序,使该结束的进行排在队尾 cnt--; } sort(head, head + cnt, cmprank);//按照进程优先级排序 for (int i = 1; i < cnt; i++) //改变指针位置,当一个进程运行结束后退出队列 head[i - 1].next = &head[i]; runningprocess = head;//下一次运行的进程为队首进程 run(head, runningprocess);//递归调用进程运行函数 } void init() { head = NULL;//进程队列队首 runningprocess = NULL;//正在运行的进程 cnt = 0;//进程数 } void input() { for (int i = 0; i < N; i++)//输入各进程状态 { cout << "\nplease input the " << i + 1 << "th process name :\n"; cin >> P[i].name; cout << "\nplease input the rank of the process\n"; cin >> P[i].rank; cout << "\nplease input the required time of the process\n"; cin >> P[i].reqtime; P[i].state = 'R';//初始赋予就绪状态Running P[i].next = NULL;//进程指针初始化为空指针 cnt++; } } int main(){ init(); input(); sort(P, P + cnt, cmprank);//按照进程优先级排序 for (int i = 1; i < cnt; i++)//对各指针赋值,形成队列 P[i - 1].next = &P[i]; head = &P[0];//头指针赋值 runningprocess = &P[0];//正在运行的进程赋值 cout << "\n---------------every pcb init status----------------\n";//打印各进程初始状态 for (int i = 0; i < cnt; i++) print(&P[i]); cout << "\n---------------------------------------------------\n"; run(head, runningprocess);//进程运行 system("pause"); return 0; }
五、运行结果
说明:初始时输入a进程剩余运行时间2,优先数1。设置b进程剩余运行时间3,优先数5。设置c进程剩余运行时间1,优先数3。设置d进程剩余运行时间2,优先数4。设置e进程剩余运行时间4,优先数2。
在对各进程初始化并连接成队列后,队头指针指向的第一个进程为优先级最大的b进程,第二个进程为d进程,第三个进程为c进程,第四个进程为e进程,第五个进程为a进程。
初始时选择优先数最大的b进程运行,将其优先数、运行时间-1,如图6所示。在运行后,将其重新插入队列。
因为b的优先数仍然最大,所以继续运行b进程。在运行后,将其重新插入队列,并按优先数将进程重新排序。
此时优先数最大的进程为d进程,优先数为4,所以选择运行d进程。在运行后,将其重新插入队列。
此时优先数最大的进程仍然为d进程,优先数为3,所以选择运行d进程。在运行后,检测到d进程剩余运行时间为0,将其状态置为“E”(完成),并将其退出队列。
剩下的进程中b进程优先数最大,将其运行完成后检测到剩余时间为0,也将其退出队列,并置其状态为“完成”。
剩下的进程中c进程优先数最大,将其运行完成后检测到剩余时间为0,也将其退出队列,并置其状态为“完成”。
剩下的进程中e进程优先数最大,将其运行一次后没有运行完成,运行第二次后仍然没有运行完成,且优先数小于另一个剩下的进程a进程。
a进程优先级最大,将其运行一次后优先级仍然最大,将其运行第二次后运行完成,置状态为“E”,退出队列。
队列中只剩下一个进程e进程,将其剩余的时间运行完。由于实验指导书并未规定优先数是否允许为负数,这里每次运行完成时仍然扣除优先级并可以将优先级扣成负数。e进程运行完成后,所有进程运行完成,按照流程图,处理机结束运行。