实验二 磁盘调度

前言

  • 参考教材《操作系统 第四版》汤小丹

  • 因为实验要求为了更好地模拟和评价算法的性能,随机产生需寻道的磁道序列,以磁道序列的首磁道为磁头的当前位置;在SCAN算法中,允许用户指定当前寻道方向;随机产生数据不方便对比,所以做了两份代码(一种随机产生磁道序列以磁道序列的首磁道为磁头的当前位置(这里命名为A)即实验内容,一种自定义磁道序列自定义磁头位置(这里命名为B)),完整代码放在文末了,方便对比

  • SCAN算法做出了两种方法,分别写在了固定磁头位置和自定义磁头位置

  • 曾木欠的博客

    Monster丶ZF的博客

实验目的

编程模拟实现磁盘调度的常用算法或调试分析相关磁盘调度程序,加深对磁盘调度常用算法的理解和实现技巧

实验内容

1.自定义磁盘调度相关的数据结构

2.依据先来先服务算法(FCFS)、最短寻道优先算法(SSTF)、扫描(SCAN,也称电梯)算法的原理,编写对应函数,模拟系统的磁盘调度服务;

3.为了更好地模拟和评价算法的性能,随机产生需寻道的磁道序列,以磁道序列的首磁道为磁头的当前位置;在SCAN算法中,允许用户指定当前寻道方向;

4.统计以上算法总寻道次数和平均寻道距离;

比较/分析以上算法的寻道性能,并作出自己的评价

实验步骤

冒泡排序

先写一个冒泡排序,在后面调用会使用到

// 冒泡排序(从小到大)
public static void SortA(int[] list) {
for (int i = 0; i < list.length - 1; i++) {
for (int j = 0; j < list.length - 1 - i; j++) {
if (list[j] > list[j + 1]) {
int temp = list[j];
list[j] = list[j + 1];
list[j + 1] = temp;
}
}
}
}

// 冒泡排序(从大到小)
public static void SortB(int[] list) {
for (int i = 0; i < list.length - 1; i++) {
for (int j = 0; j < list.length - 1 - i; j++) {
if (list[j] < list[j + 1]) {
int temp = list[j];
list[j] = list[j + 1];
list[j + 1] = temp;
}
}
}
}

先来先服务FCFS

A

// 先来先服务FCSFS,即根据磁盘访问的先后次序进行调度,遍历list数组即可,所以不做赘述
private static void FCFS(int[] list) {
System.out.println("磁道开始位置为" + list[0]);

int num = 0;// 变量num记录寻道距离
for (int j = 0; j < list.length - 1; j++) {
num += Math.abs(list[j + 1] - list[j]);
}
System.out.println("磁盘扫描序列为:" + Arrays.toString(list));
System.out.println("寻道次数:" + num);
double w = ((float) num / (float) list.length);
System.out.println("平均寻道距离:" + w);
}

B

// 先来先服务FCSFS,即根据磁盘访问的先后次序进行调度,遍历list数组即可,所以不做赘述
private static void FCFS(int[] list) {
int i = 0;
System.out.println("请输入磁头初始位置");
Scanner sc = new Scanner(System.in);
i = sc.nextInt();
int num = 0;// 变量num记录寻道距离
num = Math.abs(i - list[0]);
for (int j = 0; j < list.length - 1; j++) {
num += Math.abs(list[j + 1] - list[j]);
}
System.out.println("磁盘扫描序列为:" + Arrays.toString(list));
System.out.println("寻道次数" + num);
double w = ((float) num / (float) list.length);
System.out.println("平均寻道距离" + w);
}

最短寻道时间优先SSTF

// 最短路径算法SSTF,即访问下一个磁道前,先计算一次当前磁头到访问各个磁道的距离
private static void SSTF(int[] list) {
int[] list1=new int[list.length];//因为下面修改了list,再次运行算法3时,list数组已经被修改,所以这里把值传给list1
System.arraycopy(list,0, list1, 0, list.length);
int i = list1[0];// 变量i记录当前磁头位置
System.out.println("磁道开始位置为" + list1[0]);
int num = 0;// num记录总寻道距离
int[] k = new int[list1.length];// k数组记录访问的每一个磁道到磁头距离
int[] templist = new int[list1.length];// 将结果保存在templist[]中

for (int a = 0; a < list1.length; a++) {
// j循环遍历出每一个访问的磁道到当前磁头的距离保存在k数组中
for (int j = 0; j < list1.length; j++) {
k[j] = Math.abs(list1[j] - i);
}
// 将k数组赋值给temp数组,对temp数组冒泡排序
int[] temp = new int[k.length];
for (int j = 0; j < k.length; j++) {
temp[j] = k[j];
}
SortA(temp);

int j = temp[0];// 找到磁头到下一个磁道距离最小的为temp[0]

// 循环遍历找到temp[0]在list磁道序列数组中的位置
for (int t = 0; t < k.length; t++) {
// if语句找到list[t]到当前磁头最近
if (k[t] == j) {
i = list1[t];// 变量i记录磁头位置
templist[a] = i;// 将结果存入templist数组中
list1[t] = 999999;// 将第g个磁道号设为999999,可以理解为权重设为99999,这样他在下一次遍历,在k数组中总是最大
break;
}
}
num += j;
}
System.out.println("磁盘扫描序列为:" + Arrays.toString(templist));
System.out.println("寻道次数:" + num);
double w = ((float) num / (float) list.length);
System.out.println("平均寻道距离:" + w);
}

B

// 最短路径算法SSTF,即访问下一个磁道前,先计算一次当前磁头到访问各个磁道的距离
private static void SSTF(int[] list) {
int i = 0;// 变量i记录当前磁头位置
int num = 0;// num记录总寻道距离
int[] templist = new int[list.length];// 将结果保存在templist[]中
int[] k = new int[list.length];// k数组记录访问的每一个磁道到磁头距离
System.out.println("请输入磁头初始位置");
Scanner sc = new Scanner(System.in);
i = sc.nextInt();

for (int a = 0; a < list.length; a++) {
// j循环遍历出每一个访问的磁道到当前磁头的距离保存在k数组中
for (int j = 0; j < list.length; j++) {
k[j] = Math.abs(list[j] - i);
}

// 将k数组赋值给temp数组,对temp数组冒泡排序
int[] temp = new int[k.length];
for (int j = 0; j < k.length; j++) {
temp[j] = k[j];
}
SortA(temp);

int j = temp[0];// 找到磁头到下一个磁道距离最小的为temp[0]

// 循环遍历找到temp[0]在list磁道序列数组中的位置
for (int t = 0; t < k.length; t++) {
// if语句找到list[t]到当前磁头最近
if (k[t] == j) {
i = list[t];// 变量i记录磁头位置
templist[a] = i;// 将结果存入templist数组中
list[t] = 999999;// 将第g个磁道号设为999999,可以理解为权重设为99999,这样他在下一次遍历,k数组中总是最大
break;
}
}
num += j;
}
System.out.println("磁盘扫描序列为:" + Arrays.toString(templist));
System.out.println("寻道次数:" + num);
double w = ((float) num / (float) list.length);
System.out.println("平均寻道距离:" + w);
}

扫描算法SCAN

写完扫描算法准备关机的时候,突然想到了另一种排序方法,B的第42行else语句中,好像可以修改修改。所以对A进行了修改。对A简单修改还可以实现CSCAN循环扫描算法,我认为A中的算法更容易理解其中的原理,所以下面着重解释!

A

/*
* 扫描算法SCAN 磁盘的0磁道在最外圈 扫描算法又叫电梯算法,即按从小到大或从大到小的顺序去依次访问磁道。
* 所以这里分两种情况,1.寻道从内向外(从大到小) 2.寻道从外向内(从小到大)
*
*
* 1.若当前磁头位置大于请求序列中最大者 2.若当前磁头位置小于请求序列中最小者 3.若当前磁道号大于请求序列中最小者且小于最大者
* 这里用同一个函数去解决三种情况
*/
private static void SCAN(int[] list) {
int[] list1=new int[list.length];//因为下面修改了list,再次运行算法3时,list数组已经被修改,所以这里把值传给list1
System.arraycopy(list,0, list1, 0, list.length);
Scanner sc = new Scanner(System.in);
int k = list1[0];// 变量i记录当前磁头位置
int tempk1 = 0;
int tempk2 = 0;
int flag=1;
int num = 0;// num记录总寻道距离
int[] templist = new int[list1.length];// 将访问顺序存入templist中
System.out.println("磁道开始位置为:" + list1[0]);
//while中先计算出磁道访问的顺序
while (true) {
System.out.println("1.磁头从外向内 2.磁头从内向外");
int choice = sc.nextInt();

if (choice == 1) {
flag=5;

SortA(list1);// 从小到大排序
// 找出重排序后,k在数组中的位置,templist记录区间[0,list.length-tempk1]
for (int i = 0; i < list1.length; i++) {
if (k == list1[i])
tempk1 = i;
}
System.arraycopy(list1, tempk1, templist, 0, list1.length - tempk1);

SortB(list1);// 从大到小
// 找出重排序后,k在数组中的位置,templist记录区间[tempk2+1,list.length]
for (int i = 0; i < list1.length; i++) {
if (k == list1[i])
tempk2 = i;
}
System.arraycopy(list1, tempk2 + 1, templist, tempk2 + 1, list1.length - tempk2 - 1);
break;
}

else if (choice == 2) {
flag=5;

SortB(list1);
// 找出重排序后,k在数组中的位置,templist记录区间[0,list.length-tempk1]
for (int i = 0; i < list1.length; i++) {
if (k == list1[i])
tempk1 = i;
}
System.arraycopy(list1, tempk1, templist, 0, list1.length - tempk1);

SortA(list1);// 从小到大
// 找出重排序后,k在数组中的位置,templist记录区间[tempk2+1,list.length]
for (int i = 0; i < list1.length; i++) {
if (k == list1[i])
tempk2 = i;
}
System.arraycopy(list1, tempk2 + 1, templist, tempk2 + 1, list1.length - tempk2 - 1);
break;
}

else if (flag==5)
break;

else
System.out.println("输入错误请重新输入");
}
//计算寻道距离
for (int j = 0; j < templist.length - 1; j++) {
num += Math.abs(templist[j + 1] - templist[j]);
}

System.out.println("磁盘扫描序列为:" + Arrays.toString(templist));
System.out.println("寻道次数" + num);
double w = ((float) num / (float) list.length);
System.out.println("平均寻道距离" + w);
}

A的解释:可以通过以下代码及运行结果,更直白的理解最短寻道时间优先的算法。分别对list数组排序两次从而得到访问顺序

package 解释A;

import java.util.Arrays;
import java.util.Scanner;

public class paper {
// 冒泡排序(从小到大)
public static void SortA(int[] list) {
for (int i = 0; i < list.length - 1; i++) {
for (int j = 0; j < list.length - 1 - i; j++) {
if (list[j] > list[j + 1]) {
int temp = list[j];
list[j] = list[j + 1];
list[j + 1] = temp;
}
}
}
}

// 冒泡排序(从大到小)
public static void SortB(int[] list) {
for (int i = 0; i < list.length - 1; i++) {
for (int j = 0; j < list.length - 1 - i; j++) {
if (list[j] < list[j + 1]) {
int temp = list[j];
list[j] = list[j + 1];
list[j + 1] = temp;
}
}
}
}

public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int[] list = { 5, 2, 3, 8, 10, 12, 9 };// 7
int[] templist = new int[list.length];
int k = list[0];
int tempk1 = 0;
int tempk2 = 0;

System.out.println("1.磁头从外向内 2.磁头从内向外(从大到小)");
int choice = sc.nextInt();

if (choice == 1) {
System.out.println("排序前的list" + Arrays.toString(list));
SortA(list);
System.out.println("从小到大的list" + Arrays.toString(list));
//找出重排序后,k在数组中的位置,templist记录区间[0,list.length-tempk1]
for (int i = 0; i < list.length; i++) {
if (k == list[i])
tempk1 = i;
}

System.arraycopy(list, tempk1, templist, 0, list.length - tempk1);
System.out.println("第一次的templist" + Arrays.toString(templist));

SortB(list);
System.out.println("从大到小的list" + Arrays.toString(list));
//找出重排序后,k在数组中的位置,templist记录区间[tempk2+1,list.length]
for (int i = 0; i < list.length; i++) {
if (k == list[i])
tempk2 = i;
}
System.arraycopy(list, tempk2 + 1, templist, tempk2 + 1, list.length - tempk2 - 1);
System.out.println("第二次的templist" + Arrays.toString(templist));
}
else if(choice==2){
System.out.println("排序前的list" + Arrays.toString(list));
SortB(list);
System.out.println("从大到小的list" + Arrays.toString(list));
//找出重排序后,k在数组中的位置,templist记录区间[0,list.length-tempk1]
for (int i = 0; i < list.length; i++) {
if (k == list[i])
tempk1 = i;
}

System.arraycopy(list, tempk1, templist, 0, list.length - tempk1);
System.out.println("第一次的templist" + Arrays.toString(templist));

SortA(list);//从小到大
System.out.println("从小到大的list" + Arrays.toString(list));

for (int i = 0; i < list.length; i++) {
if (k == list[i])
tempk2 = i;
}
System.arraycopy(list, tempk2 + 1, templist, tempk2 + 1, list.length - tempk2 - 1);
System.out.println("第二次的templist" + Arrays.toString(templist));
}
}
}

image-20211207235233444

image-20211207235250685

B

/*扫描算法SCAN   磁盘的0磁道在最外圈
扫描算法又叫电梯算法,即按从小到大或从大到小的顺序去依次访问磁道。
所以这里分两种情况,1.寻道从内向外(从大到小) 2.寻道从外向内(从小到大)

注意:实验要求直观的看出三种算法的区别,是以磁道序列的首磁道为磁头,用户自定义磁头方向。
这里分三种情况,按教材的案例,磁头方向从内向外
1.若当前磁头位置大于请求序列中最大者,则直接由内向外依次给予请求服务
2.若当前磁头位置小于请求序列中最小者,则直接由外向内依次给予各请求服务
3.若当前磁道号大于请求序列中最小者且小于最大者,调用SCAN算法
*/
private static void SCAN(int[] list) {
SortA(list);//先进行一次从小到大排序
int num = 0;// num记录总寻道距离
System.out.println("请输入磁头初始位置");
Scanner sc = new Scanner(System.in);
int i = sc.nextInt();//变量i记录磁头位置
int k = 0;
int[] templist=new int[list.length];//将访问顺序存入templist中

//1.若当前磁头位置大于请求序列中最大者,则直接由内向外依次给予请求服务
if(i>list[list.length-1]) {
SortB(list);//从大到小(从内向外)依次访问
System.arraycopy(list, 0, templist, 0, list.length);//将访问顺序存入templist中
//计算距离
num +=Math.abs(i- list[list.length-1]);//磁头移动到第一个磁道的距离
for (int j = 0; j < list.length - 1; j++) {
num += Math.abs(list[j + 1] - list[j]);
}
}

//2.若当前磁头位置小于请求序列中最小者,则直接由外向内依次给予各请求服务
else if(i<list[0]) {
//已经进行过从小到大排序,所以这里不用再次调用SortA
System.arraycopy(list, 0, templist, 0, list.length);
//计算距离
num +=Math.abs(i- list[list.length-1]);//磁头移动到第一个磁道的距离
for (int j = 0; j < list.length - 1; j++) {
num += Math.abs(list[j + 1] - list[j]);
}
}
//3.若当前磁头大于请求序列中最小者且小于最大者
else {
//寻找离磁头最近的访问磁道
for (int j = 0; j < list.length; j++) {
if (i <= list[j]) {
k = j;
break;
}
}
//记录磁道访问顺序到templist中
System.arraycopy(list, k, templist, 0, list.length-k);
SortB(list);
System.arraycopy(list,list.length-k, templist, list.length-k, k);

num = Math.abs(i - templist[0]);//磁头移动到第一个磁道的距离
//每个访问磁道之间的距离
for (int j = 0; j < templist.length - 1; j++) {
num += Math.abs(templist[j + 1] - templist[j]);
}
/*以下注释比较难懂,用上面这段计算距离*/
// //找到后由外向内访问(由小到大),然后再由内向外访问(从大到小)
// //两次for计算总距离
// num += list[k] - i;//磁头移动到第一个磁道的距离
// //由外向内,区间[k,list.length-1)
// for (int j = k; j < list.length - 1; j++) {
// num += list[j + 1] - list[j];
// }
// //到达最里面,再由内向外访问剩余磁道,区间[0,k-1)
// for (int j = 0; j < k - 1; j++) {
// num += Math.abs(list[j + 1] - list[j]);
// }
// num += list[list.length - 1] - list[k - 1];
}

System.out.println("磁盘扫描序列为:" + Arrays.toString(templist));
System.out.println("寻道次数" + num);
double w = ((float) num / (float) list.length);
System.out.println("平均寻道长度" + w);
}

主函数

A

public static void main(String[] args) {
// TODO Auto-generated method stub
int Tracknum;// 磁道数
System.out.println("请输入磁道数量Tracknum");
Scanner sc = new Scanner(System.in);
Tracknum = sc.nextInt();

// 随机生成磁道序列
Random r = new Random();
int[] TrackSequence = new int[Tracknum];
for (int i = 0; i < Tracknum; i++) {
TrackSequence[i] = r.nextInt(100)+1;
}
System.out.println("磁道序列为" + Arrays.toString(TrackSequence));

// 菜单
while (true) {
System.out.println("请选择算法 1.FCFS 2.SSTF 3.SCAN 0.退出");
int k = sc.nextInt();
switch (k) {
case 1:
FCFS(TrackSequence);
break;
case 2:
SSTF(TrackSequence);
break;
case 3:
SCAN(TrackSequence);
break;
case 0:
System.exit(0);
default:
System.out.println("输的不对");
}
}
}

B

public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
int Tracknum;// 磁道数

// 菜单
while (true) {
System.out.println("请输入磁道数量Tracknum");
Tracknum = sc.nextInt();
// 输入磁道序列
int[] TrackSequence = new int[Tracknum];
System.out.println("磁道序列");
for (int i = 0; i < Tracknum; i++) {
TrackSequence[i] = sc.nextInt();
}
System.out.println("磁道序列为" + Arrays.toString(TrackSequence));

System.out.println("请选择算法 1.FCFS 2.SSTF 3.SCAN 0.退出");
int k = sc.nextInt();
switch (k) {
case 1:
FCFS(TrackSequence);
break;
case 2:
SSTF(TrackSequence);
break;
case 3:
SCAN(TrackSequence);
break;
case 0:
System.exit(0);
default:
System.out.println("输的不对");
}
}
}

完整代码

A

package 操作系统实验;

import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
/*
* 磁盘调度FCFS、SSTF、SCAN
* @author 张时贰
* @data 2021年12月7日
*/
public class 磁盘调度算法 {
// 冒泡排序(从小到大)
public static void SortA(int[] list) {
for (int i = 0; i < list.length - 1; i++) {
for (int j = 0; j < list.length - 1 - i; j++) {
if (list[j] > list[j + 1]) {
int temp = list[j];
list[j] = list[j + 1];
list[j + 1] = temp;
}
}
}
}

// 冒泡排序(从大到小)
public static void SortB(int[] list) {
for (int i = 0; i < list.length - 1; i++) {
for (int j = 0; j < list.length - 1 - i; j++) {
if (list[j] < list[j + 1]) {
int temp = list[j];
list[j] = list[j + 1];
list[j + 1] = temp;
}
}
}
}

// 先来先服务FCSFS,即根据磁盘访问的先后次序进行调度,遍历list数组即可,所以不做赘述
private static void FCFS(int[] list) {
System.out.println("磁道开始位置为" + list[0]);

int num = 0;// 变量num记录寻道距离
for (int j = 0; j < list.length - 1; j++) {
num += Math.abs(list[j + 1] - list[j]);
}
System.out.println("磁盘扫描序列为:" + Arrays.toString(list));
System.out.println("寻道次数:" + num);
double w = ((float) num / (float) list.length);
System.out.println("平均寻道距离:" + w);
}

// 最短路径算法SSTF,即访问下一个磁道前,先计算一次当前磁头到访问各个磁道的距离
private static void SSTF(int[] list) {
int[] list1=new int[list.length];//因为下面修改了list,再次运行算法3时,list数组已经被修改,所以这里把值传给list1
System.arraycopy(list,0, list1, 0, list.length);
int i = list1[0];// 变量i记录当前磁头位置
System.out.println("磁道开始位置为" + list1[0]);
int num = 0;// num记录总寻道距离
int[] k = new int[list1.length];// k数组记录访问的每一个磁道到磁头距离
int[] templist = new int[list1.length];// 将结果保存在templist[]中

for (int a = 0; a < list1.length; a++) {
// j循环遍历出每一个访问的磁道到当前磁头的距离保存在k数组中
for (int j = 0; j < list1.length; j++) {
k[j] = Math.abs(list1[j] - i);
}
// 将k数组赋值给temp数组,对temp数组冒泡排序
int[] temp = new int[k.length];
for (int j = 0; j < k.length; j++) {
temp[j] = k[j];
}
SortA(temp);

int j = temp[0];// 找到磁头到下一个磁道距离最小的为temp[0]

// 循环遍历找到temp[0]在list磁道序列数组中的位置
for (int t = 0; t < k.length; t++) {
// if语句找到list[t]到当前磁头最近
if (k[t] == j) {
i = list1[t];// 变量i记录磁头位置
templist[a] = i;// 将结果存入templist数组中
list1[t] = 999999;// 将第g个磁道号设为999999,可以理解为权重设为99999,这样他在下一次遍历,在k数组中总是最大
break;
}
}
num += j;
}
System.out.println("磁盘扫描序列为:" + Arrays.toString(templist));
System.out.println("寻道次数:" + num);
double w = ((float) num / (float) list.length);
System.out.println("平均寻道距离:" + w);
}

/*
* 扫描算法SCAN 磁盘的0磁道在最外圈 扫描算法又叫电梯算法,即按从小到大或从大到小的顺序去依次访问磁道。
* 所以这里分两种情况,1.寻道从内向外(从大到小) 2.寻道从外向内(从小到大)
*
*
* 1.若当前磁头位置大于请求序列中最大者 2.若当前磁头位置小于请求序列中最小者 3.若当前磁道号大于请求序列中最小者且小于最大者
* 这里用同一个函数去解决三种情况
*/
private static void SCAN(int[] list) {
int[] list1=new int[list.length];//因为下面修改了list,再次运行算法3时,list数组已经被修改,所以这里把值传给list1
System.arraycopy(list,0, list1, 0, list.length);
Scanner sc = new Scanner(System.in);
int k = list1[0];// 变量i记录当前磁头位置
int tempk1 = 0;
int tempk2 = 0;
int flag=1;
int num = 0;// num记录总寻道距离
int[] templist = new int[list1.length];// 将访问顺序存入templist中
System.out.println("磁道开始位置为:" + list1[0]);
//while中先计算出磁道访问的顺序
while (true) {
System.out.println("1.磁头从外向内 2.磁头从内向外");
int choice = sc.nextInt();

if (choice == 1) {
flag=5;

SortA(list1);// 从小到大排序
// 找出重排序后,k在数组中的位置,templist记录区间[0,list.length-tempk1]
for (int i = 0; i < list1.length; i++) {
if (k == list1[i])
tempk1 = i;
}
System.arraycopy(list1, tempk1, templist, 0, list1.length - tempk1);

SortB(list1);// 从大到小
// 找出重排序后,k在数组中的位置,templist记录区间[tempk2+1,list.length]
for (int i = 0; i < list1.length; i++) {
if (k == list1[i])
tempk2 = i;
}
System.arraycopy(list1, tempk2 + 1, templist, tempk2 + 1, list1.length - tempk2 - 1);
break;
}

else if (choice == 2) {
flag=5;

SortB(list1);
// 找出重排序后,k在数组中的位置,templist记录区间[0,list.length-tempk1]
for (int i = 0; i < list1.length; i++) {
if (k == list1[i])
tempk1 = i;
}
System.arraycopy(list1, tempk1, templist, 0, list1.length - tempk1);

SortA(list1);// 从小到大
// 找出重排序后,k在数组中的位置,templist记录区间[tempk2+1,list.length]
for (int i = 0; i < list1.length; i++) {
if (k == list1[i])
tempk2 = i;
}
System.arraycopy(list1, tempk2 + 1, templist, tempk2 + 1, list1.length - tempk2 - 1);
break;
}

else if (flag==5)
break;

else
System.out.println("输入错误请重新输入");
}
//计算寻道距离
for (int j = 0; j < templist.length - 1; j++) {
num += Math.abs(templist[j + 1] - templist[j]);
}

System.out.println("磁盘扫描序列为:" + Arrays.toString(templist));
System.out.println("寻道次数" + num);
double w = ((float) num / (float) list.length);
System.out.println("平均寻道距离" + w);
}

public static void main(String[] args) {
// TODO Auto-generated method stub
int Tracknum;// 磁道数
System.out.println("请输入磁道数量Tracknum");
Scanner sc = new Scanner(System.in);
Tracknum = sc.nextInt();

// 随机生成磁道序列
Random r = new Random();
int[] TrackSequence = new int[Tracknum];
for (int i = 0; i < Tracknum; i++) {
TrackSequence[i] = r.nextInt(100)+1;
}
System.out.println("磁道序列为" + Arrays.toString(TrackSequence));

// 菜单
while (true) {
System.out.println("请选择算法 1.FCFS 2.SSTF 3.SCAN 0.退出");
int k = sc.nextInt();
switch (k) {
case 1:
FCFS(TrackSequence);
break;
case 2:
SSTF(TrackSequence);
break;
case 3:
SCAN(TrackSequence);
break;
case 0:
System.exit(0);
default:
System.out.println("输的不对");
}
}
}
}

B

package 操作系统实验;

import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;
/*
* 磁盘调度FCFS、SSTF、SCAN
* @author 张时贰
* @data 2021年12月7日
*/

public class 磁盘调度算法自定义磁头 {
// 冒泡排序(从小到大)
public static void SortA(int[] list) {
for (int i = 0; i < list.length - 1; i++) {
for (int j = 0; j < list.length - 1 - i; j++) {
if (list[j] > list[j + 1]) {
int temp = list[j];
list[j] = list[j + 1];
list[j + 1] = temp;
}
}
}
}

// 冒泡排序(从大到小)
public static void SortB(int[] list) {
for (int i = 0; i < list.length - 1; i++) {
for (int j = 0; j < list.length - 1 - i; j++) {
if (list[j] < list[j + 1]) {
int temp = list[j];
list[j] = list[j + 1];
list[j + 1] = temp;
}
}
}
}

// 先来先服务FCSFS,即根据磁盘访问的先后次序进行调度,遍历list数组即可,所以不做赘述
private static void FCFS(int[] list) {
int i = 0;
System.out.println("请输入磁头初始位置");
Scanner sc = new Scanner(System.in);
i = sc.nextInt();
int num = 0;// 变量num记录寻道距离
num = Math.abs(i - list[0]);
for (int j = 0; j < list.length - 1; j++) {
num += Math.abs(list[j + 1] - list[j]);
}
System.out.println("磁盘扫描序列为:" + Arrays.toString(list));
System.out.println("寻道次数" + num);
double w = ((float) num / (float) list.length);
System.out.println("平均寻道距离" + w);
}

// 最短路径算法SSTF,即访问下一个磁道前,先计算一次当前磁头到访问各个磁道的距离
private static void SSTF(int[] list) {
int i = 0;// 变量i记录当前磁头位置
int num = 0;// num记录总寻道距离
int[] templist = new int[list.length];// 将结果保存在templist[]中
int[] k = new int[list.length];// k数组记录访问的每一个磁道到磁头距离
System.out.println("请输入磁头初始位置");
Scanner sc = new Scanner(System.in);
i = sc.nextInt();

for (int a = 0; a < list.length; a++) {
// j循环遍历出每一个访问的磁道到当前磁头的距离保存在k数组中
for (int j = 0; j < list.length; j++) {
k[j] = Math.abs(list[j] - i);
}

// 将k数组赋值给temp数组,对temp数组冒泡排序
int[] temp = new int[k.length];
for (int j = 0; j < k.length; j++) {
temp[j] = k[j];
}
SortA(temp);

int j = temp[0];// 找到磁头到下一个磁道距离最小的为temp[0]

// 循环遍历找到temp[0]在list磁道序列数组中的位置
for (int t = 0; t < k.length; t++) {
// if语句找到list[t]到当前磁头最近
if (k[t] == j) {
i = list[t];// 变量i记录磁头位置
templist[a] = i;// 将结果存入templist数组中
list[t] = 999999;// 将第g个磁道号设为999999,可以理解为权重设为99999,这样他在下一次遍历,k数组中总是最大
break;
}
}
num += j;
}
System.out.println("磁盘扫描序列为:" + Arrays.toString(templist));
System.out.println("寻道次数:" + num);
double w = ((float) num / (float) list.length);
System.out.println("平均寻道距离:" + w);
}

/*扫描算法SCAN 磁盘的0磁道在最外圈
扫描算法又叫电梯算法,即按从小到大或从大到小的顺序去依次访问磁道。
所以这里分两种情况,1.寻道从内向外(从大到小) 2.寻道从外向内(从小到大)

注意:实验要求直观的看出三种算法的区别,是以磁道序列的首磁道为磁头,用户自定义磁头方向。
这里分三种情况,按教材的案例,磁头方向从内向外
1.若当前磁头位置大于请求序列中最大者,则直接由内向外依次给予请求服务
2.若当前磁头位置小于请求序列中最小者,则直接由外向内依次给予各请求服务
3.若当前磁道号大于请求序列中最小者且小于最大者,调用SCAN算法
*/
private static void SCAN(int[] list) {
SortA(list);//先进行一次从小到大排序
int num = 0;// num记录总寻道距离
System.out.println("请输入磁头初始位置");
Scanner sc = new Scanner(System.in);
int i = sc.nextInt();//变量i记录磁头位置
int k = 0;
int[] templist=new int[list.length];//将访问顺序存入templist中

//1.若当前磁头位置大于请求序列中最大者,则直接由内向外依次给予请求服务
if(i>list[list.length-1]) {
SortB(list);//从大到小(从内向外)依次访问
System.arraycopy(list, 0, templist, 0, list.length);//将访问顺序存入templist中
//计算距离
num +=Math.abs(i- list[list.length-1]);//磁头移动到第一个磁道的距离
for (int j = 0; j < list.length - 1; j++) {
num += Math.abs(list[j + 1] - list[j]);
}
}

//2.若当前磁头位置小于请求序列中最小者,则直接由外向内依次给予各请求服务
else if(i<list[0]) {
//已经进行过从小到大排序,所以这里不用再次调用SortA
System.arraycopy(list, 0, templist, 0, list.length);
//计算距离
num +=Math.abs(i- list[list.length-1]);//磁头移动到第一个磁道的距离
for (int j = 0; j < list.length - 1; j++) {
num += Math.abs(list[j + 1] - list[j]);
}
}
//3.若当前磁头大于请求序列中最小者且小于最大者
else {
//寻找离磁头最近的访问磁道
for (int j = 0; j < list.length; j++) {
if (i <= list[j]) {
k = j;
break;
}
}
//记录磁道访问顺序到templist中
System.arraycopy(list, k, templist, 0, list.length-k);
SortB(list);
System.arraycopy(list,list.length-k, templist, list.length-k, k);

num = Math.abs(i - templist[0]);//磁头移动到第一个磁道的距离
//每个访问磁道之间的距离
for (int j = 0; j < templist.length - 1; j++) {
num += Math.abs(templist[j + 1] - templist[j]);
}
/*以下注释比较难懂,用上面这段计算距离*/
// //找到后由外向内访问(由小到大),然后再由内向外访问(从大到小)
// //两次for计算总距离
// num += list[k] - i;//磁头移动到第一个磁道的距离
// //由外向内,区间[k,list.length-1)
// for (int j = k; j < list.length - 1; j++) {
// num += list[j + 1] - list[j];
// }
// //到达最里面,再由内向外访问剩余磁道,区间[0,k-1)
// for (int j = 0; j < k - 1; j++) {
// num += Math.abs(list[j + 1] - list[j]);
// }
// num += list[list.length - 1] - list[k - 1];
}

System.out.println("磁盘扫描序列为:" + Arrays.toString(templist));
System.out.println("寻道次数" + num);
double w = ((float) num / (float) list.length);
System.out.println("平均寻道长度" + w);
}

public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
int Tracknum;// 磁道数

// 菜单
while (true) {
System.out.println("请输入磁道数量Tracknum");
Tracknum = sc.nextInt();
// 输入磁道序列
int[] TrackSequence = new int[Tracknum];
System.out.println("磁道序列");
for (int i = 0; i < Tracknum; i++) {
TrackSequence[i] = sc.nextInt();
}
System.out.println("磁道序列为" + Arrays.toString(TrackSequence));

System.out.println("请选择算法 1.FCFS 2.SSTF 3.SCAN 0.退出");
int k = sc.nextInt();
switch (k) {
case 1:
FCFS(TrackSequence);
break;
case 2:
SSTF(TrackSequence);
break;
case 3:
SCAN(TrackSequence);
break;
case 0:
System.exit(0);
default:
System.out.println("输的不对");
}
}
}
}

运行结果

这里使用课本P233页的磁道序列:55 58 39 18 90 160 150 38 184,磁头初始位置都为100。因为A的磁道序列是随机生成的,这里运行结果使用B代码,方便对比 先来先服务FCFS

被访问的下一个磁道号移动距离(磁道数)
5545
583
3919
1821
9072
16070
15010
38112
184146
平均寻道长度55.3

最短寻道时间优先SSTF

被访问的下一个磁道号移动距离(磁道数)
9010
5832
553
3916
381
1820
150132
16010
18424
平均寻道长度27.5

扫描算法SCAN

被访问的下一个磁道号移动距离(磁道数)
15050
16010
18424
9094
5832
553
3916
381
1820
平均寻道长度27.8

image-20211208004946604

A的运行结果

image-20211208084319142

学习到的新东西!

1.这段代码看着好像没什么问题,菜单也一直这样写,但是他是一个死循环(菜单也是死循环但是菜单之后不执行其它代码所以不影响)

while (true) {
System.out.println("1.从内向外依次访问 2.从外向内依次访问");
Scanner sc = new Scanner(System.in);
int choice = sc.nextInt();

switch (choice) {
case 1:
Sort(list);// 对list从小到大冒泡排序,即从内向外依次访问
break;
case 2:
SortB(list);// 对list从大到小冒泡排序,即从外向内依次访问
break;
default:
System.out.println("输的不对,重新输入");
}
}
int flag=0;
while (true) {
System.out.println("1.从内向外依次访问 2.从外向内依次访问");
Scanner sc = new Scanner(System.in);
int choice = sc.nextInt();

switch (choice) {
case 1:
Sort(list);// 对list从小到大冒泡排序,即从内向外依次访问
flag++;
break;
case 2:
SortB(list);// 对list从大到小冒泡排序,即从外向内依次访问
flag++;
break;
default:
System.out.println("输的不对,重新输入");
}
if(flag!=0)
break;
}

image-20211207185621486

2.对数组遍历

System.out.println(Arrays.toString(templist));

3.对数组的值传递

System.arraycopy(list, 0, templist, 0, list.length);//(数组名,数组位置,数组名,数组位置,传递个数)

总结

磁盘访问时间主要有哪些部分构成?要提高磁盘调度效率,主要在哪方面下手更好?

磁盘访问时间主要由寻道时间,旋转延迟时间,传输时间构成。集中数据传输,有利于提高传输效率

最短寻道时间优先算法与扫描(电梯)算法有什么异同?如何改进SCAN算法?

最短寻道时间优先算法是基于优先级的调度算法,因此就可能导致优先级低的进程发生饥饿现象,而扫描算法不仅考虑距离,更优先考虑磁头的移动方向。改进SCAN算法,规定磁头单向移动,即从内向外访问,到达最外面,磁头立即返回内侧