一、进程调度
无论是在批处理还是分时系统中,用户进程数一般都多于处理机数、这将导致它们互相争夺处理机。另外,系统进程也同样需要使用处理机。这就要求进程调度程序按一定的策略,动态地把处理机分配给处于就绪队列中的某一个进程,以使之执行。进程调度属于处理机调度。
处理机调度分为三个层次:
高级调度 :(High-Level Scheduling)又称为长程调度、作业调度,它决定把外存上处于后备队列中的作业调入内存运行,为他们创建进程、分配必要的资源,放入就绪队列
低级调度 :(Low-Level Scheduling)又称为短程调度、进程调度,它决定把就绪队列的某进程获得处理机,并由分派程序将处理机分配给被选中的进程
中级调度 :(Intermediate-Level Scheduling)又称为在虚拟存储器中引入,在内、外存对换区进行进程对换,把外存上那些已经预备运行条件的就绪进程再重新调入内存,放入就绪队列。
二、常用调度算法模拟
首先创建一个进程控制块(PCB)的类:
package controlblock;
/**
* 进程控制块
*
* @author wz
*
* @date 2015年11月10日
*/
public class PCB {
private int pid;
private double priority;
private int arriveTime;
private int needTime;
public PCB(int pid,int arriveTime,int needTime){
this.pid = pid;
this.arriveTime = arriveTime;
this.needTime = needTime;
}
public PCB(int pid, double priority, int arriveTime, int needTime) {
super();
this.pid = pid;
this.priority = priority;
this.arriveTime = arriveTime;
this.needTime = needTime;
}
public int getPid() {
return pid;
}
public double getPriority() {
return priority;
}
public void setPriority(double priority) {
this.priority = priority;
}
public int getArriveTime() {
return arriveTime;
}
public int getNeedTime() {
return needTime;
}
public void setNeedTime(int needTime) {
this.needTime = needTime;
}
}
1.先到先服务(first-come first-served,FCFS)调度算法
先来先服务(FCFS)调度算法是一种最简单的调度算法,该算法既可用于作业调度,也可用于进程调度。当在作业调度中采用该算法时,每次调度都是从后备作业队列中选择一个或多个最先进入该队列的作业,将它们调入内存,为它们分配资源、创建进程,然后放入就绪队列。在进程调度中采用FCFS算法时,则每次调度是从就绪队列中选择一个最先进入该队列的进程,为之分配处理机,使之投入运行。该进程一直运行到完成或发生某事件而阻塞后才放弃处理机。
模拟算法如下:
package process.schedule;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import controlblock.PCB;
/**
* 先到先服务进程调度
*
* @author wz
*
* @date 2015年11月10日
*/
public class FCFS {
protected LinkedList<PCB> processQueue;
public FCFS() {
processQueue = new LinkedList<PCB>();
}
public void schedule() {
sortByArriveTime(processQueue);
int currentTime = 0;
int arriveTime;
PCB process;
Iterator<PCB> iter = processQueue.iterator();
while (iter.hasNext()) {
process = iter.next();
arriveTime = process.getArriveTime();
System.out.print("进程:" + process.getPid() + ",");
System.out.print("到达时间:" + process.getArriveTime() + ",");
System.out.print("需要时间:" + process.getNeedTime() + ",");
if (arriveTime > currentTime)
currentTime = arriveTime;
System.out.print("开始时间:" + currentTime + ",");
currentTime += process.getNeedTime();
System.out.println("结束时间:" + currentTime);
iter.remove();
}
}
public void addProcess(int pid, int arriveTime, int needTime) {
processQueue.push(new PCB(pid, arriveTime, needTime));
}
/**
* 对进程队列按到达时间排序
*
* @param processQueue
*/
private <T> void sortByArriveTime(LinkedList<PCB> processQueue) {
processQueue.sort((p1, p2) -> {
Integer p1Time = p1.getArriveTime();
Integer p2Time = p2.getArriveTime();
return p1Time.compareTo(p2Time);
});
}
}
具体思想是用随机数生成多个PCB对象,放入FCFS的进程队列processQueue中,调度算法首先按照进程的到达时间递增排序,然后再从进程队列中依次取出进程,计算其开始时间、结束时间。
2.短作业优先(short job first,SJF)调度算法
短作业(进程)优先调度算法(SJF),是指对短作业或短进程优先调度的算法。它们可以分别用于作业调度和进程调度。短作业优先(SJF)的调度算法是从后备队列中选择一个或若干个估计运行时间最短的作业,将它们调入内存运行。而短进程优先(SPF)调度算法则是从就绪队列中选出一个估计运行时间最短的进程,将处理机分配给它,使它立即执行并一直执行到完成,或发生某事件而被阻塞放弃处理机时再重新调度。
模拟算法如下:
package process.schedule;
import java.util.Comparator;
import java.util.LinkedList;
import controlblock.PCB;
import dispatcher.Dispatcher;
/**
* 短作业优先调度
*
* @author wz
*
* @date 2015年11月10日
*/
public class SJF{
protected LinkedList<PCB> processQueue;
public SJF() {
processQueue = new LinkedList<PCB>();
}
public void schedule() {
sortByArriveTime(processQueue);
PCB process, tempProcess;
int arriveTime, needTime, minIndex, minNeedTime, currentTime = 0;
while (!processQueue.isEmpty()) {
process = processQueue.pollFirst();
arriveTime = process.getArriveTime();
needTime = process.getNeedTime();
if (currentTime < arriveTime)
currentTime = arriveTime;
minIndex = -1;
minNeedTime = Dispatcher.getMaxNeedTime() + 2;
// 要执行进程时,挑选已到达的需要作业时间最短的进程
for (int i = 0; i < processQueue.size(); i++) {
tempProcess = processQueue.get(i);
if (tempProcess.getArriveTime() > currentTime + needTime)
break;
// 到达时间相同,挑选最短作业为当前作业
if (tempProcess.getArriveTime() == arriveTime && tempProcess.getNeedTime() < needTime) {
processQueue.set(i, process);
process = tempProcess;
tempProcess = processQueue.get(i);
needTime = process.getNeedTime();
}
if (tempProcess.getNeedTime() < minNeedTime) {
minIndex = i;
minNeedTime = tempProcess.getNeedTime();
}
}
// 将最短作业放入队首
if (minIndex != -1) {
tempProcess = processQueue.remove(minIndex);
processQueue.addFirst(tempProcess);
}
System.out.print("进程:" + process.getPid() + ",到达时间:" + process.getArriveTime() + ",需要时间:"
+ process.getNeedTime() + ",开始时间:" + currentTime + ",");
currentTime += needTime;
System.out.println("结束时间:" + currentTime);
}
}
public void addProcess(int pid, int arriveTime, int needTime) {
PCB process = new PCB(pid, arriveTime, needTime);
processQueue.push(process);
}
/**
* 对进程队列按到达时间排序
*
* @param processQueue
*/
private <T> void sortByArriveTime(LinkedList<PCB> processQueue) {
processQueue.sort((p1, p2) -> {
Integer p1Time = p1.getArriveTime();
Integer p2Time = p2.getArriveTime();
return p1Time.compareTo(p2Time);
});
}
}
具体思想是用随机数生成多个PCB对象,放入SJF的进程队列processQueue中,然后:
①调度算法首先按照进程的到达时间递增排序,然后在队列不为空的情况下执行②
②队首PCB出队(本次要执行的作业)
③遍历进程队列,若有到达时间与出队PCB相同,则找出所需作业时间最短的作业,两者交换。(确保当前作业为最短作业)
④找出已出队进程的执行结束时间前到达的所有作业中所需作业时间最短作业程(查找下一个要执行的作业),放入队首
⑤计算已出队进程(本次的最短作业)运行的开始时间、结束时间。
⑥若队列不为空,执行②,否则结束
3.高响应比优先调度算法(Heigest Response Ratio Next,HRRN)
在批处理系统中,短作业优先算法是一种比较好的算法,其主要的不足之处是长作业的运行得不到保证,容易出现饥饿现象。为每个作业引入前面所述的动态优先权,并使作业的优先级随着等待时间的增加而以速率a 提高,则长作业在等待一定的时间后,必然有机会分配到处理机。该优先权的变化规律可描述为:
由于等待时间与服务时间之和就是系统对该作业的响应时间,故该优先权又相当于响应比RP。据此,又可表示为:
模拟算法如下:
package process.schedule;
import java.util.Comparator;
import java.util.LinkedList;
import controlblock.PCB;
/**
* 高响应比优先调度
*
* @author wz
*
* @date 2015年11月10日
*/
public class HRRN {
protected LinkedList<PCB> processQueue;
public HRRN() {
processQueue = new LinkedList<PCB>();
}
public void schedule() {
sortByArriveTime(processQueue);
PCB process, tempProcess;
int arriveTime, needTime, maxIndex, currentTime = 0;
double respRatio, maxPriority = 0;
while (!processQueue.isEmpty()) {
process = processQueue.pollFirst();
arriveTime = process.getArriveTime();
needTime = process.getNeedTime();
if (currentTime < arriveTime)
currentTime = arriveTime;
maxIndex = -1;
maxPriority = -1;
// 当前进程执行完后,挑选已到达的响应比最高的进程
for (int i = 0; i < processQueue.size(); i++) {
tempProcess = processQueue.get(i);
if (tempProcess.getArriveTime() > currentTime + needTime)
break;
respRatio = (currentTime + needTime - tempProcess.getArriveTime()) / (double) tempProcess.getNeedTime() + 1;
tempProcess.setPriority(respRatio);
if (respRatio > maxPriority) {
maxIndex = i;
maxPriority = respRatio;
}
}
// 将响应比最高的进程放入队首
if (maxIndex != -1) {
tempProcess = processQueue.remove(maxIndex);
processQueue.addFirst(tempProcess);
}
System.out.print("进程:" + process.getPid() + ",响应比:" + process.getPriority() + ",到达时间:"
+ process.getArriveTime() + ",需要时间:" + process.getNeedTime() + ",开始时间:" + currentTime + ",");
currentTime += needTime;
System.out.println("结束时间:" + currentTime);
}
}
public void addProcess(int pid, int arriveTime, int needTime) {
PCB process = new PCB(pid, arriveTime, needTime);
processQueue.push(process);
}
/**
* 对进程队列按到达时间排序
*
* @param processQueue
*/
private <T> void sortByArriveTime(LinkedList<PCB> processQueue) {
processQueue.sort((p1, p2) -> {
Integer p1Time = p1.getArriveTime();
Integer p2Time = p2.getArriveTime();
return p1Time.compareTo(p2Time);
});
}
}
具体思想是用随机数生成多个PCB对象,放入HRRN的进程队列processQueue中,然后:
①按照进程的到达时间递增排序,在队列不为空的情况下执行②
②队首PCB出队(本次要执行的作业)
③遍历进程队列中已出队作业的执行结束时间前到达的所有作业,计算响应比,找出最高响应比的作业,放入队首
④ 计算已出队进程(本次的响应比最高的作业)运行的开始时间、结束时间。
⑤若队列不为空,执行②,否则结束
4.时间片轮转法(round robin,RR)
在早期的时间片轮转法中,系统将所有的就绪进程按先来先服务的原则排成一个队列,每次调度时,把CPU 分配给队首进程,并令其执行一个时间片。时间片的大小从几ms 到几百ms。当执行的时间片用完时,由一个计时器发出时钟中断请求,调度程序便据此信号来停止该进程的执行,并将它送往就绪队列的末尾;然后,再把处理机分配给就绪队列中新的队首进程,同时也让它执行一个时间片。这样就可以保证就绪队列中的所有进程在一给定的时间内均能获得一时间片的处理机执行时间。换言之,系统能在给定的时间内响应所有用户的请求。
模拟算法如下:
package process.schedule;
import java.util.Comparator;
import java.util.LinkedList;
import controlblock.PCB;
/**
* 时间片轮转调度
*
* @author wz
*
* @date 2015年11月10日
*/
public class RR {
protected LinkedList<PCB> processQueue;
private static final int TIME_SLICE = 5;
public RR() {
processQueue = new LinkedList<PCB>();
}
public void schedule() {
sortByArriveTime(processQueue);
PCB process;
int currentTime = 0;
int arriveTime;
int needTime;
while (!processQueue.isEmpty()) {
process = processQueue.pollFirst();
needTime = process.getNeedTime();
arriveTime = process.getArriveTime();
System.out.print("进程:" + process.getPid() + ",");
System.out.print("到达时间:" + process.getArriveTime() + ",");
System.out.print("还需要时间:" + process.getNeedTime() + ",");
if (currentTime < arriveTime)
currentTime = arriveTime;
System.out.print("开始时间:" + currentTime + ",");
if (TIME_SLICE < needTime) {
currentTime += TIME_SLICE;
System.out.println("进程中断时间:" + currentTime);
process.setNeedTime(needTime - TIME_SLICE);
for (int i = 0; i < processQueue.size(); i++) {
if (processQueue.get(i).getArriveTime() > currentTime) {
processQueue.add(i, process);
break;
} else if (i == processQueue.size() - 1) {
processQueue.add(process);
break;
}
}
} else {
currentTime += needTime;
System.out.println("结束时间:" + currentTime);
}
}
}
public void addProcess(int pid, int arriveTime, int needTime) {
processQueue.push(new PCB(pid, arriveTime, needTime));
}
/**
* 对进程队列按到达时间排序
*
* @param processQueue
*/
private <T> void sortByArriveTime(LinkedList<PCB> processQueue) {
processQueue.sort((p1, p2) -> {
Integer p1Time = p1.getArriveTime();
Integer p2Time = p2.getArriveTime();
return p1Time.compareTo(p2Time);
});
}
}
具体思想是用随机数生成多个PCB对象,放入RR的进程队列processQueue中,然后:
①按照进程的到达时间递增排序,在队列不为空的情况下执行②
②队首PCB出队(本次要执行的作业)
③为已出队进程分配时间片,计算运行的开始时间、中断时间或结束时间。
④若出队进程没有执行完,则将该PCB插入到进程队列中当前进程中断时间前所到达的作业的最后
⑤若队列不为空,执行②,否则结束
5.优先级调度算法(priority-scheduling algorithm,PSA)
此算法常被用于批处理系统中,作为作业调度算法,也作为多种 操作系统中的进程调度算法,还可用于实时系统中。当把该算法用于作业调度时,系统将从后备队列中选择若干个优先权最高的作业装入内存。当用于进程调度时,该算法是把处理机分配给就绪队列中优先权最高的进程,这时,又可进一步把该算法分为 抢占式(Preemptive Mode) 和 非抢占式(Nonpreemptive Mode) 。
这里的调度算法采用 抢占式(Preemptive Mode) 优先级调度。
在这种方式下,系统同样是把处理机分配给优先权最高的进程,使之执行。但在其执行期间,只要又出现了另一个其优先权更高的进程,进程调度程序就立即停止当前进程(原优先权最高的进程)的执行,重新将处理机分配给新到的优先权最高的进程。因此,在采用这种调度算法时,是每当系统中出现一个新的就绪进程i 时,就将其优先权Pi与正在执行的进程j 的优先权Pj进行比较。如果Pi≤Pj,原进程Pj便继续执行;但如果是Pi>Pj,则立即停止Pj的执行,做进程切换,使i 进程投入执行。显然,这种抢占式的优先权调度算法能更好地满足紧迫作业的要求,故而常用于要求比较严格的实时系统中,以及对性能要求较高的批处理和分时系统中。
模拟算法如下:
package process.schedule;
import controlblock.PCB;
import java.util.LinkedList;
import java.util.List;
/**
* 优先权调度(抢占式)
*
* @author wz
*
* @date 2015年11月10日
*/
public class PSA {
protected LinkedList<PCB> processQueue;
public PSA() {
processQueue = new LinkedList<>();
}
public void schedule() {
sortByArriveTime(processQueue);
PCB process, tempProcess;
int arriveTime, needTime, runTime, currentTime = 0;
while (!processQueue.isEmpty()) {
process = processQueue.pollFirst();
arriveTime = process.getArriveTime();
if (currentTime < arriveTime)
currentTime = arriveTime;
for (int i = 0; i < processQueue.size(); i++) {
needTime = process.getNeedTime();
tempProcess = processQueue.get(i);
if (tempProcess.getArriveTime() > currentTime + needTime)
break;
// 当前进程执行至被高优先级进程抢占
if (tempProcess.getPriority() < process.getPriority()) {
if (tempProcess.getArriveTime() != currentTime) {
processQueue.remove(i);
System.out.print("进程:" + process.getPid() + ",优先级:" + (int) process.getPriority() + ",到达时间:"
+ process.getArriveTime() + ",需要时间:" + process.getNeedTime() + ",开始时间:" + currentTime
+ ",");
runTime = tempProcess.getArriveTime() - currentTime;
process.setNeedTime(needTime - runTime);
currentTime += runTime;
System.out.println("进程中断时间:" + currentTime);
processQueue.addFirst(process);
process = tempProcess;
} else {
processQueue.set(i, process);
process = tempProcess;
tempProcess = processQueue.get(i);
// needTime = process.getNeedTime();
}
} else {
subSortByPriority(processQueue, 0, i + 1);
}
}
System.out.print("进程:" + process.getPid() + ",优先级:" + (int) process.getPriority() + ",到达时间:"
+ process.getArriveTime() + ",需要时间:" + process.getNeedTime() + ",开始时间:" + currentTime + ",");
currentTime += process.getNeedTime();
System.out.println("结束时间:" + currentTime);
}
}
public void addProcess(int pid, int priority, int arriveTime, int needTime) {
processQueue.push(new PCB(pid, priority, arriveTime, needTime));
}
/**
* 对进程队列按到达时间排序
*
* @param processQueue
*/
private <T> void sortByArriveTime(LinkedList<PCB> processQueue) {
processQueue.sort((p1, p2) -> {
Integer p1Time = p1.getArriveTime();
Integer p2Time = p2.getArriveTime();
return p1Time.compareTo(p2Time);
});
}
/**
* 对指定子队列按优先级排序
*
* @param processQueue
* @param fromIndex
* @param toIndex
*/
private void subSortByPriority(LinkedList<PCB> processQueue, int fromIndex, int toIndex) {
List<PCB> subQueue = processQueue.subList(fromIndex, toIndex);
subQueue.sort((p1, p2) -> {
Double p1Priority = p1.getPriority();
Double p2Priority = p2.getPriority();
return p1Priority.compareTo(p2Priority);
});
}
}
具体思想是用随机数生成多个PCB对象,放入PSA的进程队列processQueue中,然后:
①按照进程的到达时间递增排序,在队列不为空的情况下执行②
②队首PCB出队(第一个到达的作业或优先级最高的作业)
③遍历进程队列,查找当前PCB结束时间前到达的所有PCB,若优先级高于当前执行的作业,执行④,否则执行⑤
④此时 当前作业执行至被高优先级作业抢占。 计算当前作业本次运行的开始时间、中断时间,并放入队首(优先级高的放在队首,当前执行作业是目前已查找到的优先级次高的作业),将③查找到的高优先级的作业出队,成为当前执行的作业(高优先级作业抢占处理机)。然后继续执行③向后遍历
⑤对队列中已经遍历的PCB按优先级递减就地排序(保证已到达进程按优先级递减排列)。若未遍历完,继续执行③向后遍历
⑥计算当前进程本次执行的开始时间、结束时间。
⑦若队列不为空,执行②,否则结束
暂时就写了这几种调度算法的模拟,下面是生成随机PCB测试的代码:
package dispatcher;
import java.util.Random;
import process.schedule.FCFS;
import process.schedule.HRRN;
import process.schedule.PSA;
import process.schedule.RR;
import process.schedule.SJF;
/**
* 分派程序
*
* @author wz
*
* @date 2015年11月10日
*/
public class Dispatcher {
private static final int MAX_ARRIVE_TIME = 5;
private static final int MAX_NEED_TIME = 10;
private static final int MAX_PROCESS_NUM = 5;
private static final int MAX_PRIORITY = 10;
private final static int MAX_PID = 10000;
public static void main(String[] args) {
FCFSSchedule();
SJFSchedule();
RRShedule();
PSAShedule();
HRRNShedule();
}
/**
* 高响应比优先调度
*/
private static void HRRNShedule() {
HRRN hrrn = new HRRN();
HRRN ps = hrrn;
Random random = new Random();
for (int i = 0; i < MAX_PROCESS_NUM; i++) {
ps.addProcess(random.nextInt(MAX_PID), random.nextInt(MAX_ARRIVE_TIME), random.nextInt(MAX_NEED_TIME) + 1);
}
ps.schedule();
}
/**
* 优先权调度(抢占式)
*/
private static void PSAShedule() {
PSA ps = new PSA();
Random random = new Random();
for (int i = 0; i < MAX_PROCESS_NUM; i++) {
ps.addProcess(random.nextInt(MAX_PID), random.nextInt(MAX_PRIORITY), random.nextInt(MAX_ARRIVE_TIME),
random.nextInt(MAX_NEED_TIME) + 1);
}
ps.schedule();
}
/**
* 时间片轮转调度
*/
private static void RRShedule() {
RR ps = new RR();
Random random = new Random();
for (int i = 0; i < MAX_PROCESS_NUM; i++) {
ps.addProcess(random.nextInt(MAX_PID), random.nextInt(MAX_ARRIVE_TIME), random.nextInt(MAX_NEED_TIME) + 1);
}
ps.schedule();
}
/**
* 先到先服务
*/
private static void FCFSSchedule() {
FCFS ps = new FCFS();
Random random = new Random();
for (int i = 0; i < MAX_PROCESS_NUM; i++) {
ps.addProcess(random.nextInt(MAX_PID), random.nextInt(MAX_ARRIVE_TIME), random.nextInt(MAX_NEED_TIME) + 1);
}
ps.schedule();
}
/**
* 短作业优先调度
*/
private static void SJFSchedule() {
SJF ps = new SJF();
Random random = new Random();
for (int i = 0; i < MAX_PROCESS_NUM; i++) {
ps.addProcess(random.nextInt(MAX_PID), random.nextInt(MAX_ARRIVE_TIME), random.nextInt(MAX_NEED_TIME) + 1);
}
ps.schedule();
}
public static int getMaxNeedTime() {
return MAX_NEED_TIME;
}
}