前言
在操作系统中作业调度的主要任务是根据PCB中的信息,检查系统中的资源能否满足作业对资源的需求,以及按照一定的调度算法从外存的后备队列中选取某些作业调入内存,并为它们创建进程、分配必要的资源。本文将以Java程序来实现先来先服务(FCFS)作业调度算法。
一、先来先服务(FCFS)是什么?
FCFS是操作系统中最简单的调度算法,该算法既可用于作业调度,也可以用于进程调度。当在作业调度中采用该算法时,系统将按照作业到达的先后次序来进行调度或者说它是优先考虑在系统中等待时间最长的作业,而不管该作业所需执行时间的长短,从后备作业队列中选择几个最先进入该队列的作业,将它们调入内存并为它们分配资源和创建进程。然后把它放入就绪队列。
当在进程调度中采用FCFS算法时,每次调度是从就绪的进程队列中选择一个最先进入该队列的进程,为之分配处理机,使之投入运行。该进程一直运行到完成或发生某事件而阻塞后,进程调度程序才将处理机分配给其它进程。
二、先来先服务(FCFS)算法分析
假设当前有四个进程先后到达系统进行调度:
作业名 | 到达时间 | 服务时间 |
A | 0 | 20 |
B | 5 | 15 |
C | 10 | 5 |
D | 15 | 10 |
在先来先服务算法中,由于在0时刻系统中只有作业A,因此系统将优先为作业A进行调度。作业A在完成的过程中作业B、C、D分别在5时刻、10时刻、15时刻都到达了系统中。也就是说在作业A调度完成后系统中会有B、C、D三个作业等待调度。根据先来先服务的思想,系统将依次按B、C、D的顺序为接下来的三个作业进行调度。
下面将依次分析各个作业的完成时间、周转时间、带权周转时间与平均带权周转时间:
1、作业A:
完成时间:因为作业A从0时刻开始服务,完成时间与服务时间相同为20
周转时间:周转时间为从作业提交到作业完成的时间间隔,此时作业A的提交时间为0,完成时间为20,因此作业A的周转时间为20
(20 - 0,也可以说是作业的完成时间 - 到达时间)
带权周转时间:带权周转时间是指作业的周转时间与系统为它提供服务的时间之比,因为作业A的周转时间为20,系统提供的服务时间为20,因此作业A的带权周转时间为1(20 / 20,作业的周转时间 / 服务时间)
2、作业B:
完成时间:因为作业A在20时刻结束服务,所以作业B在20时刻开始服务,完成时间为开始服务时间加服务时间为35(20 + 15)
周转时间:此时作业B的提交时间为5(从5时刻到达系统),完成时间为35,因此作业B的周转时间为30(35 - 5,完成时间 - 到达时间)
带权周转时间:因为作业B的周转时间为30,系统提供的服务时间为15,因此作业B的带权周转时间为2(30 / 15,周转时间 / 服务时间)
3、作业C:
完成时间:因为作业B在35时刻结束服务,所以作业C在35时刻开始服务,完成时间为开始服务时间加服务时间为40(35 + 5)
周转时间:此时作业C的提交时间为10(从10时刻到达系统),完成时间为40,因此作业C的周转时间为30(40 - 10,完成时间 - 到达时间)
带权周转时间:因为作业C的周转时间为30,系统提供的服务时间为5,因此作业C的带权周转时间为6(30 / 5,周转时间 / 服务时间)
4、作业D:完成时间:因为作业C在40时刻结束服务,所以作业D在40时刻开始服务,完成时间为开始服务时间加服务时间为50(40 + 10)
周转时间:此时作业D的提交时间为15(从15时刻到达系统),完成时间为50,因此作业D的周转时间为35(50 - 15,完成时间 - 到达时间)
带权周转时间:因为作业D的周转时间为35,系统提供的服务时间为10,因此作业A的带权周转时间为3.5(35 / 10,周转时间 / 服务时间)
作业名 | 到达时间 | 服务时间 | 完成时间 | 周转时间 | 带权周转时间 |
A | 0 | 20 | 20 | 20 | 1 |
B | 5 | 15 | 35 | 30 | 2 |
C | 10 | 5 | 40 | 30 | 6 |
D | 15 | 10 | 50 | 35 | 3.5 |
平均周转时间:( 20 + 30 + 30 + 35 ) / 4 = 28.75
平均带权周转时间:( 20 / 20 + 30 / 15 + 30 / 5 + 35 / 10 ) / 4 = 3.125
三、实现代码
1、作业数据类
classProcessData { // 到达时间publicintarriveTime; // 服务时间publicintserviceTime; // 完成时间publicintfinishTime; // 周转时间publicintturnTime; // 带权周转时间publicdoublepowerTime; // 作业的构造方法中传来的初值为到达时间和服务时间publicProcessData(intarriveTime, intserviceTime) { this.arriveTime=arriveTime; this.serviceTime=serviceTime; } // 重写toString方法便于之后的数据展示publicStringtoString() { returnarriveTime+"\t"+serviceTime+"\t"+finishTime+"\t"+turnTime+"\t"+powerTime; } }
2、作业调度类
publicclassProcessProject { // 平均周转时间publicstaticdoubleavgTotalTime; // 平均带权周转时间publicstaticdoubleavgPowerTime; publicstaticvoidmain(String[] args) { // 定义长度为4,类型为作业数据类的作业数组用于存储4个作业ProcessData[] processData=newProcessData[4]; // 定义四个作业processData[0] =newProcessData(0, 20); processData[1] =newProcessData(5, 15); processData[2] =newProcessData(10, 5); processData[3] =newProcessData(15, 10); // 调用先来先服务算法FCFS(processData); } // 先来先服务算法实现privatestaticvoidFCFS(ProcessData[] processData) { intpreFinished=0; // 前一个作业的完成时间即为下一个作业的开始时间avgTotalTime=0; // 平均周转时间avgPowerTime=0; // 平均带权周转时间// 初始化完成时间、周转时间、带权周转时间的初值为0for (inti=0; i<processData.length; i++) { processData[i].finishTime=0; // 设置初始完成时间为0processData[i].turnTime=0; // 设置初始周转时间为0processData[i].powerTime=0; // 设置初始带权周转时间为0 } // 如果第一个作业的到达时间不等于前一个作业的完成时间,就将前一个作业的完成时间定义为当前作业的到达时间if (processData[0].arriveTime!=preFinished) { preFinished=processData[0].arriveTime; } for (inti=0; i<processData.length; i++) { // 作业的完成时间为上一个作业的完成时间加当前作业的服务时间processData[i].finishTime=preFinished+processData[i].serviceTime; preFinished=processData[i].finishTime; // 周转时间 = 完成时间 - 到达时间processData[i].turnTime=processData[i].finishTime-processData[i].arriveTime; // 带权周转时间 = 作业的周转时间 / 系统提供服务的时间processData[i].powerTime= (double) processData[i].turnTime/ (double) processData[i].serviceTime; } System.out.println("先来先服务(FCFS)算法:"); // 打印进程的信息Display(processData); } privatestaticvoidDisplay(ProcessData[] processData) { System.out.println("到达时间\t"+"服务时间\t"+"完成时间\t"+"周转时间\t"+"带权周转时间\t"); for (inti=0; i<processData.length; i++) { System.out.println(processData[i]); avgTotalTime+=processData[i].turnTime; // 求总周转时间,此时avgTotalTime中存储的值为总周转时间avgPowerTime+=processData[i].powerTime; // 求总带权周转时间,此时avgPowerTime中存储的值为总带权周转时间 } avgTotalTime=avgTotalTime/processData.length; // 平均周转时间avgPowerTime=avgPowerTime/processData.length; // 平均带权周转时间System.out.println("平均周转时间:"+avgTotalTime); System.out.println("平均带权周转时间:"+avgPowerTime); } }
3、运行结果
总结
以上便为操作系统中先来先服务(FCFS)作业调度的Java实现,FCFS算法在单处理机操作系统中已很少作为主调算法,但是经常把它与其它调度算法相结合使用,形成一种更为有效的调度算法。例如,可以在系统中按进程的优先级设置多个队列,每个优先级对应一个队列,其中每一个队列的调度都基于FCFS算法。