磁盘调度算法有很多种,包括先来先服务(FCFS)、最短寻道优先算法(SSTF)、扫描算法(SCAN)、循环扫描算法等(CSCAN)等等,各种算法的详细介绍在操作系统等书中有详细介绍。这里选取前三种调度算法进行实现,语言采用Java。
三种算法的简介为:
1、FCFS:按磁盘访问请求的到来顺序进行响应。如若有六个磁盘访问请求依次到来,六个请求所请求访问的磁道号为33,78,55,120,63,70;则根据FCFS,将按请求到来顺序进行调度响应,所以磁盘访问顺序为:33,78,55,120,63,70。
2、SSTF:此算法优先选取欲访问的磁道距离当前磁头所在位置最近的请求进行调度。假设一组磁盘访问请求如上,并假设磁头初始时在磁道号为60的位置,则磁盘访问顺序为:63,70,78,55,33,120.
3、SCAN:此算法在SSTF的基础上规定了磁头的运动方向,按磁头初始运动方向,选取此方向上欲访问磁道距离磁头所在位置最近的请求进行调度。切磁头只能继续向该方向行进,直至该方向所有请求均调度完毕。若一组磁道访问请求如上,磁头初始位置仍为60,并假设此时磁头运动方向为磁道号增大方向,则磁盘访问顺序变为:63,70,78,120,55,33.
在进行编程实现时,磁盘访问请求所要访问的磁道号随机产生,产生的磁盘访问请求数量、磁道号的允许范围、磁头初始运动方向均有用户设定(在SACN算法运行时设定)。
一、磁盘访问请求的封装。一个磁盘访问请求包括请求号,欲请求的磁道号,是否已被调度的标记。实际上一个请求可以看作是一个进程。
package com.bean;
public class Request {
private int id; //请求号,唯一标识一个请求
private int number; //欲访问的磁道号
private int flag; //是否已被调度的标记,初始为0,表示为被调度。
public Request(){
//空白构造器
this.id=0;
this.number=0;
this.flag=0;
}
public Request(int id,int num){
//带参构造器
this.id=id;
this.number=num;
this.flag=0;
}
public void setId(int id){
this.id=id;
}
// 以下是一系列set和get方法
public int getId(){
return this.id;
}
public void setNumber(int number){
this.number=number;
}
public int getNumber(){
return this.number;
}
public void setFlag(int flag){
this.flag=flag;
}
public int getFlag(){
return this.flag;
}
}
二、作业类的封装。即模拟一个作业,其中有一个getRandomRequest()方法,在设定的磁道允许范围内,产生一个磁盘访问序列,也即一个request数组。欲产生的磁盘访问请求数量由用户运行时设定,每个访问请求的磁道号在设定的磁道允许范围内随机产生。
package com.bean;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
public class Work {
private int start; //允许访问的磁道号范围的起始磁道号
private int end; //允许访问的磁道号范围的终止磁道号
// 即请求可请求访问的磁道号在start与end之间
private int num; //所要产生的磁盘访问请求数量
public Work(){
//不带参构造器
this.start=0;
this.end=0;
this.num=0;
}
public Work(int start,int end,int num){
//带参构造器
this.start=start;
this.end=end;
this.num=num;
}
public int getStart(){
return this.start;
}
public int getEnd(){
return this.end;
}
public Request[] getRandomRequest(){
//产生磁盘访问请求的方法
Request req[]=new Request[num]; //数组声明num个请求
Random random=new Random();
Set<Integer> set=new HashSet<Integer>();
while(true){
//为num个请求依次生成要访问的磁道号,放入set集合
set.add((int)(random.nextInt(end-start)+start));
if(set.size()==num)
break;
}
int i=0;
//将set集合中的数取出依次付给各请求。使用set的目的是让num个访问请求要访问的磁道号各不同。
for(int temp:set){
req[i]=new Request(i+1,temp);
i++;
}
return req;
}
}
三、调度算法的实现。三个方法对应三个调度算法。
package com.diaodu;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.TreeSet;
import com.bean.Request;
import com.bean.Work;
public class DiaoDu {
Work work;
Request[] request;
int begin;//磁头初始位置
DecimalFormat df = new DecimalFormat("0.000");
public DiaoDu(Work work){
//构造器,接收一个作业
this.work=work;
request=work.getRandomRequest();
//磁头初始位置设为所允许访问磁道号范围的中间值。
//如若磁道访问允许范围为100~300,则磁头初始位置在200处。
begin=(work.getStart()+work.getEnd())/2;
}
public void FCFS(){
//先来先服务算法
begin=(work.getStart()+work.getEnd())/2;
Request[] req=request;
System.out.println("磁道号区间:"+work.getStart()+"到"+work.getEnd());
System.out.println("磁头初始时所在磁道号:"+begin);
double sum=0;
System.out.println("请求号 被访问的下一个磁道号 移动距离");
for(Request r:req){
System.out.println("--"+r.getId()+"----------------"+r.getNumber()+"-----------------"+Math.abs(begin-r.getNumber())+"--");
sum+=Math.abs(begin-r.getNumber());
begin=r.getNumber();
}
System.out.println("***********平均寻道长度为:"+df.format(sum/req.length)+"************");
}
public void SSTF(){
//最短寻道优先
begin=(work.getStart()+work.getEnd())/2;
Request[] req=request;
System.out.println("磁道号区间:"+work.getStart()+"到"+work.getEnd());
System.out.println("磁头初始时所在磁道号:"+begin);
int t=0;
double sum=0;
System.out.println("请求号 被访问的下一个磁道号 移动距离");
for(int i=0;i<req.length;i++){
int temp=work.getEnd();
for(int j=0;j<req.length;j++){
if(Math.abs(req[j].getNumber()-begin)<temp&&req[j].getFlag()==0){
temp=Math.abs(req[j].getNumber()-begin);
t=j;
}
}
System.out.println("--"+req[t].getId()+"----------------"+req[t].getNumber()+"-----------------"+temp+"--");
begin=req[t].getNumber();
req[t].setFlag(1);
sum+=temp;
}
System.out.println("***********平均寻道长度为:"+df.format(sum/req.length)+"***********");
}
public void SCAN(){
//扫描算法
begin=(work.getStart()+work.getEnd())/2;
Request[] req=request;
int direction;//磁头运动方向方向
System.out.println("设定磁头初始化运动方向(1:磁道号增大方向 -1:磁道号减小方向):");
direction=new Scanner(System.in).nextInt();
System.out.println("磁道号区间:"+work.getStart()+"到"+work.getEnd());
System.out.println("磁头初始时所在磁道号:"+begin);
if(direction==1)
System.out.println("磁头初始运动方向: 磁道号增大方向");
else
System.out.println("磁头初始运动方向: 磁道号减小方向");
Map<Integer,Request> map=new HashMap<Integer,Request>();
TreeSet<Integer> ts1=new TreeSet<Integer>();
TreeSet<Integer> ts2=new TreeSet<Integer>();
for(Request r:req){
if(r.getNumber()>=begin){
map.put(r.getNumber(), r);
ts1.add(r.getNumber());
}
else{
map.put(r.getNumber(), r);
ts2.add(r.getNumber());
}
}
System.out.println("请求号 被访问的下一个磁道号 移动距离");
double sum=0;
if(direction==1){
for(int temp:ts1){
System.out.println("--"+map.get(temp).getId()+"----------------"+temp+"-----------------"+Math.abs(temp-begin));
sum+=Math.abs(temp-begin);
begin=temp;
}
for(int temp:ts2.descendingSet()){
System.out.println("--"+map.get(temp).getId()+"----------------"+temp+"-----------------"+Math.abs(temp-begin));
sum+=Math.abs(temp-begin);
begin=temp;
}
}
else{
for(int temp:ts2.descendingSet()){
System.out.println("--"+map.get(temp).getId()+"----------------"+temp+"-----------------"+Math.abs(temp-begin));
sum+=Math.abs(temp-begin);
begin=temp;
}
for(int temp:ts1){
System.out.println("--"+map.get(temp).getId()+"----------------"+temp+"-----------------"+Math.abs(temp-begin));
sum+=Math.abs(temp-begin);
begin=temp;
}
}
System.out.println("***********平均寻道长度为:"+df.format(sum/req.length)+"***********");
}
}
四、场景类测试。在main方法中定义相关对象,进行测试。
package com.client;
import java.util.Scanner;
import com.bean.Work;
import com.diaodu.DiaoDu;
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
int start,end,num;
Scanner in=new Scanner(System.in);
System.out.println("设定磁道起止道号:");
System.out.print("起始道号:");
start=in.nextInt();
System.out.print("终止道号:");
end=in.nextInt();
System.out.print("输入磁盘访问的请求数:");
num=in.nextInt();
Work work=new Work(start,end,num);
DiaoDu diaodu=new DiaoDu(work);
System.out.println();
System.out.println("*************先来先服务调度法*************");
diaodu.FCFS();
System.out.println();
System.out.println("***********最短寻道时间优先调度法************");
diaodu.SSTF();
System.out.println();
System.out.println("****************扫描调度法*****************");
diaodu.SCAN();
}
}