1、进程调度与作业调度的区别:
作业调度:根据作业控制块(JCB)中的信息,检查系统中的资源是否满足作业对资源的需求,以及按照一定的调度算法,从外存的后备队列中选取某些作业调入内存,并为它们创建进程,分配必要的资源。然后再将新创建的进程排在就绪队列上等待调度。
进程调度:保存当前进程的处理机的现场信息,如程序计数器、多个通用寄存器中的内容等,然后按某种算法从就绪队列中选取一个进程,将其状态转换为运行状态,再把进程控制块内有关处理机现场的信息装入处理器相应的各个寄存器中,把处理器的控制权交予该进程,让它从上次的断点处恢复运行。
进程调度时让某个就绪状态的进程到处理机上运行,而作业调度只是使作业具有了竞争处理机的机会。
2、单道批处理系统与多道批处理系统的区别:
(1)单道批处理系统(Simple Batch Processing System):系统对作业的处理是成批进行的,但在内存中始终只保持一道作业。
特点:自动性、顺序性、单道性
主要问题:CPU与I/O设备忙闲不均,对计算为主的作业,外设空闲;对I/O为主的作业,CPU空闲。
多道批处理系统(Multiprogrammed Batch Processing System):在内存中同时存放几个作业,宏观上并行运行——都处于运行状态,但都没运行完;微观上串行运行——各作业交替使用CPU
特点:调度性、无序性、多道性
主要问题:①作业平均周转时间长:短作业的周转时间显著增长;
②无交互能力:整个作业完成后或者中间出错时,才与用户交互,不利于调试和修改。
3、用到的公式:
完成时间 = 开始时间 + 需要运行时间
周转时间 = 完成时间 - 到达时间
带权周转时间 = 周转时间 / 需要运行时间
等待时间 = 当前时间 - 到达时间
优先权 = (等待时间 + 需要运行时间) / 需要运行时间
4、高响应比优先算法特点:
①当等待时间相同时,短进程的优先权高;
②当需要运行时间相同时,作业的优先权又取决于等待时间,相当于先到先服务;
③长作业的优先级可以随着等待时间的增加而提高,因此长作业等待一段时间后仍能得到调度。
#include <stdio.h> #include <stdlib.h> #include <string.h> #define WAIT "Wait"//就绪状态 #define RUN "Run"//运行状态 #define FINISH "Finish"//完成状态 #define JOBNUMBER 5 //设置进程测试数为5 typedef struct JCB{ char jobName[10];//作业名 int arriveTime;//到达时间 int runTime;//需要运行时间 int startTime;//开始时间 int endTime;//完成时间 int turnoverTime;//周转时间 float useWeightTurnoverTime;//带权周转时间 char processStatus[10];//进程状态 }; static int currentTime = 0;//当前时间 static int finishNumber = 0;//进程完成数量 char JobArray[JOBNUMBER][10];//存放数组名信息的二元数组 float priority[JOBNUMBER];//存放进程优先级的一元数组 //创建JCB void createJCB(struct JCB* jcb){ freopen("input.txt","r",stdin); printf("从文件中读入三个参数的数据:\n"); printf("作业号 到达时间 需要运行时间\n"); for(int i = 0; i < 5; i++){ scanf("%s", &jcb[i].jobName);//作业号 scanf("%d", &jcb[i].arriveTime);//到达时间 scanf("%d", &jcb[i].runTime);//需要运行时间 jcb[i].startTime = 0; jcb[i].endTime = 0; jcb[i].turnoverTime = 0; jcb[i].useWeightTurnoverTime = 0.0; strcpy(jcb[i].processStatus, WAIT); printf("%s\t%d\t%d\n",jcb[i].jobName, jcb[i].arriveTime,jcb[i].runTime); } printf("---------------------------------------------\n"); freopen("CON", "r", stdin); } //打印用途 void printJob(struct JCB* jcb){ printf("当前时间为%d\n", currentTime); printf("作业号 到达时间 需要运行时间 开始时间 完成时间 周转时间 带权周转时间 进程状态\n"); for(int i = 0; i < JOBNUMBER; i++){ if(strcmp(jcb[i].processStatus, FINISH) == 0)//如果进程为finish状态,这样输出 printf("%s\t%d\t%4d\t\t%d\t%d\t %d\t %.2f\t %s\n", jcb[i].jobName, jcb[i].arriveTime, jcb[i].runTime, jcb[i].startTime, jcb[i].endTime, jcb[i].turnoverTime, jcb[i].useWeightTurnoverTime, jcb[i].processStatus); else if(strcmp(jcb[i].processStatus, RUN) == 0)//如果进程为run状态,这样输出 printf("%s\t%d\t%4d\t\t%d\t运行中\t none\t none %s\n", jcb[i].jobName, jcb[i].arriveTime, jcb[i].runTime, jcb[i].startTime, jcb[i].processStatus); else //如果进程为wait状态,这样输出 printf("%s\t%d\t%4d\t\t未运行\tnone\t none\t none %s\n", jcb[i].jobName, jcb[i].arriveTime, jcb[i].runTime, jcb[i].processStatus); } printf("---------------------------------------------\n"); } //计算平均带权周转时间 float weightTurnoverTimeCount(struct JCB* jcb){ float sum = 0.0; for(int i = 0; i < JOBNUMBER; i++) sum += jcb[i].useWeightTurnoverTime; return sum / JOBNUMBER; } //计算平均周转时间 float turnOverTimeCount(struct JCB* jcb){ float sum = 0.0; for(int i = 0; i < JOBNUMBER; i++) sum += jcb[i].turnoverTime; return sum / JOBNUMBER; } //比较各个进程之间的到达时间,按升序排列 void compare(struct JCB* jcb){ for(int i = 0; i < JOBNUMBER; i++){ int min = jcb[i].arriveTime, minIndex = i; for(int j = i + 1; j < JOBNUMBER; j++){ if(jcb[j].arriveTime < min){ min = jcb[j].arriveTime; minIndex = j; } } struct JCB temp = jcb[i]; jcb[i] = jcb[minIndex]; jcb[minIndex] = temp; } } //打印进程调度顺序,平均周转时间及平均带权周转时间 void printInfo(struct JCB* jcb){ printf("1、进程调度顺序为:%s -> %s -> %s -> %s -> %s\n", JobArray[0], JobArray[1], JobArray[2], JobArray[3], JobArray[4]); printf("2、平均周转时间为:%.2f\n",turnOverTimeCount(jcb)); printf("3、平均带权周转时间为:%.2f\n", weightTurnoverTimeCount(jcb)); printf("------------------测试完毕 版权归邓钦艺所有---------\n"); } //两算法共同循环遍历部分 void loop(struct JCB* jcb, int i){ jcb[i].startTime = currentTime; jcb[i].endTime = jcb[i].startTime + jcb[i].runTime; jcb[i].turnoverTime = jcb[i].endTime - jcb[i].arriveTime; jcb[i].useWeightTurnoverTime = jcb[i].turnoverTime * 1.0 / jcb[i].runTime; strcpy(jcb[i].processStatus, RUN); while(true){ if(currentTime == jcb[i].endTime){ strcpy(jcb[i].processStatus, FINISH); finishNumber++; if(finishNumber == JOBNUMBER) printJob(jcb); currentTime--; break; } else{ printJob(jcb); currentTime++; } } } //先来先服务调度算法 void firstComeFirstServed(struct JCB* jcb){ createJCB(jcb); compare(jcb); int i = 0; //进程调度currentTime每次加1,直到进程全部被调度完成为止 for(; finishNumber != JOBNUMBER; currentTime++){ if(currentTime < jcb[0].arriveTime)//当前时间小于第一个节点到来时间时,直接打印 printJob(jcb); else{ strcpy(JobArray[i], jcb[i].jobName); loop(jcb, i); i++; } } printInfo(jcb);//打印进程调度顺序,平均周转时间及平均带权周转时间 currentTime = 0;//静态变量当前时间置位 finishNumber = 0;//静态变量完成进程数量置位 } //高响应比优先调度算法 void highestResponseRatioNext(struct JCB* jcb){ createJCB(jcb); compare(jcb); int i = 0, j = 0; for(; finishNumber != JOBNUMBER; currentTime++){ float maxPriority = 0.0; int indexPriority = 0; if(currentTime < jcb[0].arriveTime)//当前时间小于第一个节点到来时间时,直接打印 printJob(jcb); else{ for(int i = 0; i < JOBNUMBER; i++){ if(strcmp(jcb[i].processStatus, FINISH) != 0){ int waitTime = currentTime - jcb[i].arriveTime; priority[i] = (waitTime + jcb[i].runTime) * 1.0 / jcb[i].runTime; if(priority[i] > maxPriority){ maxPriority = priority[i]; indexPriority = i; } } } strcpy(JobArray[j++], jcb[indexPriority].jobName); loop(jcb, indexPriority); } } printInfo(jcb);//打印进程调度顺序,平均周转时间及平均带权周转时间 currentTime = 0;//当前时间置位 finishNumber = 0;//完成进程数量置位 } //菜单函数 void menu(struct JCB* jcb){ int input; while(true){ printf("------------3114005847 邓钦艺-----------------\n"); printf("| 1、先来先服务调度算法 |\n"); printf("| 2、响应比高者优先调度算法 |\n"); printf("| 3、退出 |\n"); printf("----------------------------------------------\n"); printf("请输入序号以继续程序:"); scanf("%d", &input); switch(input){ case 1:firstComeFirstServed(jcb); break; case 2:highestResponseRatioNext(jcb); break; case 3: exit(0); default:printf("输入有误,请重新输入!!!\n"); break; } } } //主函数 int main(){ struct JCB jcb[JOBNUMBER]; menu(jcb); system("pause"); return 0; }
6、测试用例:其中三个参数分别是作业名称,到达时间以及需要运行时间
7、运行结果:
(1)菜单页面:
(2)先到先服务算法调度过程:
①当前时间为0时,模拟系统从外存的后备队列中选取五项作业(job1,job2,job3,job4,job5)调入内存,并为它们创建进程,分配必要的资源。然后再将新创建的进程排在就绪队列上等待调度,其进程状态为wait状态。
②当前时间为1时,job5到达,并开始执行,进程状态由wait转换为run;当前时间为3时,job5执行完毕,开始时间1 + 需要运行时间2 =完成时间3,进程状态由run转换为finish。与此同时,job2开始执行,进程状态由wait转换为run。
③当前时间为8时,job2执行完毕,进程状态由run转换为finish,与此同时job3开始执行,进程状态由wait转换为run。
④当前时间为12时,job3运行完毕,进程状态由run转换为finish,与此同时job4开始执行,进程状态由wait转换为run。
⑤当前时间为19时,job4运行完毕,进程状态由run转换为finish,与此同时job1开始执行,进程状态由wait转换为run。
⑥当前时间为21时,job5执行完毕,进程状态由run转换为finish,此时五个进程状态全部转换为finish,进程调度算法停止,打印出进程调度顺序,平均周转时间,以及平均带权周转时间。算法调度完毕后回到菜单界面:
(3)高响应比优先算法调度过程:
①当前时间为0时,模拟系统从外存的后备队列中选取五项作业(job1,job2,job3,job4,job5)调入内存,并为它们创建进程,分配必要的资源。然后再将新创建的进程排在就绪队列上等待调度,其进程状态为wait状态。
当前时间为1时,只有job5到达,并开始执行,进程状态由wait转换为run;
当前时间为3时,job5执行完毕,开始时间1 +需要运行时间2 =完成时间3,进程状态由run转换为finish。与此同时,job3开始执行,进程状态由wait转换为run。
②当前时间为7时,job3执行完毕,开始时间3 +需要运行时间4 =完成时间7,进程状态由run转换为finish。与此同时,job2开始执行,进程状态由wait转换为run。
③当前时间为12时,job2执行完毕,进程状态由run转换为finish,与此同时job1开始执行,进程状态由wait转换为run。
④当前时间为14时,job5执行完毕,进程状态由run转换为finish,与此同时job4开始执行,进程状态由wait转换为run。
⑤当前时间为21时,job4执行完毕,进程状态由run转换为finish,此时五个进程状态全部转换为finish,进程调度算法停止,打印出进程调度顺序,平均周转时间,以及平均带权周转时间。算法调度完毕后回到菜单界面:
⑥按3退出
(4)对实现高响应比的验证:
优先权 = (等待时间 + 需要运行时间) / 需要运行时间
①当前时间为1时,只有job5到了,那就先运行它,需要运行时间为2,运行到时间3结束,开始调度下一进程;
②当前时间为3时,job5运行完毕,job2,job3,job4也已经到达,比较三者的优先权:
Job2:(1 + 5)/ 5 = 1.2,job3:(1 + 4)/ 4 = 1.25,job4:(0 + 7)/ 7 = 1,优先权最大为job3,执行它,需要运行时间为4,因此直到时间7结束,开始调度下一进程;
③当前时间为7时,job3运行完毕,此时job2,job4,job1全都已经到达,比较三者的优先权:
Job2:(5 + 5)/ 5 = 2,job4:(4+ 7)/ 7 = 1.57,job1:(2 + 2)/ 2 = 2,优先权最大为job2和job1,优先权相同时执行先到的进程,因此执行进程job2,需要运行时间为5,因此直到时间12结束,开始调度下一进程;
④当前时间为12时,job2运行完毕,比较job1,job4的优先权:
Job1:(7 + 2)/ 2 = 4.5,job4:(9 + 7)/ 7 = 2.29, 优先权最大为job1,执行它,需要运行时间为2,因此直到时间14结束,开始调度下一进程;
⑤当前时间为14时,剩余进程job4,执行它,需要运行时间为7,因此直到时间21结束,此时后备队列中五个进程执行完毕,调度完毕。
验证结果为:job5 >> job3 >> job2 >> job1 >> job4,与程序运行结果一致。
(5)二者对比:
先来先服务算法:
高响应比优先算法:
二者在调度顺序上出现了不一致,
在平均周转时间上:先来先服务 > 高响应比优先
在平均带权周转时间上:先来先服务 > 高响应比优先
在这次运行的测试用例中可以看出,二者算法在两项指标性能上,高响应比优先算法要优于先来先服务算法。
首先高响应比优先算法兼顾了等待时间和需要运行时间,其短进程的优先权高,与此同时长作业的优先级可以随着等待时间的增加而提高,因此长作业等待一段时间后仍能得到调度而不会长期得不到服务。而先来先服务算法则很容易导致长作业到来过多时,短作业再到达造成的“饥饿”现象;也避免了短进程优先中存在大量短作业时造成的长作业“饥饿”现象;高响应比算法在优先权相同的时候,执行到达时间早的进程。