1、了解磁盘调度的策略和原理;
2、理解和掌握磁盘调度算法——先来先服务算法(FCFS)、最短寻道时间优先算法(SSTF)、电梯扫描算法(SCAN)
1、 模拟先来先服务法(First-Come, First-Served,FCFS),最短寻道时间优先法(Shortest Seek Time First, SSTF),电梯扫描算法(SCAN)三种磁盘调度算法;
2、 对三种算法进行对比分析。
3、 输入为一组请求访问磁道序列,输出为每种调度算法的磁头移动轨迹和移动的总磁道数。
1、先来先服务算法(FCFS): 按先来后到次序服务,未作优化。最简单的移臂调度算法是“先来先服务”调度算法,这个算法实际上不考虑访问者要求访问的物理位置,而只是考虑访问者提出访问请求的先后次序。 采用先来先服务算法决定等待访问者执行输入输出操作的次序时,移动臂来回地移动。先来先服务算法花费的寻找时间较长,所以执行输入输出操作的总时间也很长。
2、最短寻道时间优先算法(SSTF) : 最短寻找时间优先调度算法总是从等待访问者中挑选寻找时间最短的那个请求先执行的,而不管访问者到来的先后次序。与先来先服务、算法比较,大幅度地减少了寻找时间,因而缩短了为各访问者请求服务的平均时间,也就提高了系统效率。但最短查找时间优先(SSTF)调度,FCFS会引起读写头在盘面上的大范围移动,SSTF查找距离磁头最短(也就是查找时间最短)的请求作为下一次服务的对象。SSTF查找模式有高度局部化的倾向,会推迟一些请求的服务,甚至引起无限拖延(又称饥饿)。
3、扫描算法(SCAN): SCAN 算法又称电梯调度算法。SCAN算法是磁头前进方向上的最短查找时间优先算法,它排除了磁头在盘面局部位置上的往复移动,SCAN算法在很大程度上消除了SSTF算法的不公平性,但仍有利于对中间磁道的请求。“电梯调度”算法是从移动臂当前位置开始沿着臂的移动方向去选择离当前移动臂最近的那个柱访问者,如果沿臂的移动方向无请求访问时,就改变臂的移动方向再选择。但是,“电梯调度”算法在实现时,不仅要记住读写磁头的当前位置,还必须记住移动臂的当前前进方向。
实验只是模拟实现磁盘调度功能,不需要系统调用函数。
1、输入为一组请求访问磁道序列,该序列和所选磁道个数要求随机生成,输出为每种调度算法的磁头移动轨迹和移动的总磁道数;
2、输入磁道范围 0~1000 ,输入所选磁道个数0~1000;
#include
#include
#include
#include
//
#define MIN 10 //磁道个数最小值
#define MAX 20 //磁道个数最大值
#define RANGE 200 //磁道范围
int len; //磁道个数
int* ary = NULL; //初始请求访问磁道序列
int* array = NULL; //最终请求访问磁道序列
//初始化数据
int Init()
{
srand((unsigned)time(NULL));
len = rand() % MAX + MIN;
ary = (int*)malloc(sizeof(int) * len); //动态分配数组
array = (int*)malloc(sizeof(int) * (len + 1));
array[0] = rand() % RANGE; //将磁针初始位置添加进array数组
for (int i = 0; i < len; i++)
ary[i] = rand() % RANGE;
}
//计算从当前磁道移动到下一个磁道的距离
int Cal_Distance(int a, int b)
{
return (a - b > 0 ? a - b : b - a);
}
//显示调度过程
void Show(int a[])
{
printf("从:%d号磁道开始\n", a[0]);
for (int i = 1; i < len + 1; i++)
printf("下一个访问的磁道号:%d\t移动的距离:%d\n", a[i], Cal_Distance(a[i], a[i - 1]));
}
//计算平均寻道长度
void Cal_Average(int a[])
{
double count = 0;
double average = 0;
for (int i = 1; i < len + 1; i++)
count += Cal_Distance(a[i], a[i - 1]);
average = count / len;
printf("移动的总磁道数为:%f,平均寻道长度为:%f\n", count, average);
}
//先来先服务算法
void FIFS(int a[])
{
for (int i = 0; i < len; i++)//将申请的磁道依次添加进array数组
array[i + 1] = a[i];
Show(array);
Cal_Average(array);
}
//最短寻道时间优先
void SSTF(int a[])
{
int* a1 = (int*)malloc(sizeof(int) * len); //动态分配数组
for (int i = 0; i < len; i++)
{
a1[i] = a[i];
}
int x = 0, max = 0, temp = 0;
for (int i = 0; i < len; i++) //该循环为了找出访问顺序,array[];
{
x = i;
for (int j = i; j < len; j++) //遍历数组a[],找出与当前磁头最近的一个磁道。
{
if (Cal_Distance(a1[j + 1], array[i]) < Cal_Distance(a1[x], array[i]))
{
x = j + 1;
max = a1[j + 1];
}
else
max = a1[x];
}
array[i + 1] = max;
temp = a1[x]; //找出该磁道后将找到的数据置换到数组最前面
a1[x] = a1[i];
a1[i] = temp;
}
free(a1);
Show(array);
Cal_Average(array);
}
//折半插入排序
void half_insert_sort(int arr[], int len)
{
int i, j;
for (i = 1; i < len; i++)
{
int key = arr[i];
int left = 0;
int right = i - 1;
while (left <= right)
{ //半分查找找到插入的位置
int mid = (left + right) / 2;
if (key < arr[mid])
{
right = mid - 1;
}
else
{
left = mid + 1;
}
}
for (j = i - 1; j >= left; j--)
{ //把后面的元素往后移
arr[j + 1] = arr[j];
}
arr[j + 1] = key; //插入元素
}
}
//扫描算法,先向往右
void SCAN_Right(int a[])
{
int temp;
int* right = (int*)malloc(sizeof(int) * len); //动态分配数组
for (int i = 0; i < len; i++) //不能操作数组a,防止对a进行更改影响其他算法
right[i] = a[i];
half_insert_sort(right, len);
for (int i = 0; i < len; i++)
{
if (array[0] <= right[i])
{
temp = i;
break;
}
}
int x = 1;
for (int i = temp; i < len; i++)
{
array[x] = right[i];
x++;
}
for (int i = temp - 1; i >= 0; i--)
{
array[x] = right[i];
x++;
}
Show(array);
Cal_Average(array);
free(right);
}
//扫描算法,先向往左
void SCAN_Left(int a[]) //扫描算法
{
int temp;
int* left = (int*)malloc(sizeof(int) * len); //动态分配数组
for (int i = 0; i < len; i++) //不能操作数组a,防止对a进行更改影响其他算法
left[i] = a[i];
half_insert_sort(left, len);
for (int i = 0; i < len; i++)
{
if (array[0] <= left[i])
{
temp = i;
break;
}
}
int x = 1;
for (int i = temp - 1; i >= 0; i--)
{
array[x] = left[i];
x++;
}
for (int i = temp; i <= len; i++)
{
array[x] = left[i];
x++;
}
free(left);
Show(array);
Cal_Average(array);
}
void main()
{
Init(); //初始化
printf("申请访问磁道个数为:%d,磁道序列为:\n", len);
for (int i = 0; i < len; i++)
printf("%d ", ary[i]);
printf("\n");
printf("先来先服务算法(FCFS)算法\n");
FIFS(ary);
printf("最短寻道时间优先算法(SSTF)算法\n");
SSTF(ary);
printf("扫描算法(SCAN)算法(先往右)\n");
SCAN_Right(ary);
printf("扫描算法(SCAN)算法(先往左)\n");
SCAN_Left(ary);
free(ary);
free(array);
}
5、截屏输出实验结果;
结果一:
结果二:
结果八,生成的数据在磁盘两端左右摇摆。
结果九,生成的数据顺序申请访问:
结果十:一小部分最初申请访问磁道较小的位置,一大部分申请访问磁道较大的位置,磁头开始位置在磁道较大处。
6、根据实验结果与理论课讲述的原理进行实验分析。
实验结果一到实验结果七的请求访问磁道序列和所选磁道个数均为随机产生的,有结果可以看到,先来先服务算法(FCFS)所移动的总磁道数均为最大。如果请求访问磁道序列是在磁道两端左右摆动(如结果八),那么先来先服务算法移动的总磁道数将更大,平均寻道长度也将接近磁道总数的一半。而且移动的总磁道数随着请求访问磁道序列长度增大有明显增大的趋势。但是如果请求访问磁道序列是顺序进行的(如实验结果九),那么先来先服务算法的平均寻道长度又将明显减少。
先来先服务算法是按先来后到次序服务,这个算法实际上不考虑访问者要求访问的物理位置,而只是考虑访问者提出访问请求的先后次序。 采用先来先服务算法决定等待访问者执行输入输出操作的次序时,移动臂来回地移动。先来先服务算法花费的寻找时间较长,所以执行输入输出操作的总时间也很长。
由上面结果可知,一般情况下最短寻道时间优先算法平均寻道长度要比先来先服务要小,而且如果请求访问磁道序列比较聚集在一个区域或者请求访问磁道序列变长的情况下,最短寻道时间优先算法的平均寻道长度有明显的减小趋势。但是如果一小部分最初申请访问磁道较小的位置,一大部分申请访问磁道较大的位置,磁头开始位置在磁道较大处(如实验结果十),那么磁头就会在先访问磁道较大的位置,并且在该位置来回摆动,而最先申请访问磁道较小位置的服务就会被推迟,直到最后才处理。
最短寻道时间优先算法总是从等待访问者中挑选寻找时间最短的那个请求先执行的,而不管访问者到来的先后次序。与先来先服务、算法比较,大幅度地减少了寻找时间,因而缩短了为各访问者请求服务的平均时间,也就提高了系统效率。但最短查找时间优先调度模式有高度局部化的倾向,会推迟一些请求的服务,甚至引起无限拖延(又称饥饿)。
从实验结果看,扫描算法的平均寻道时间远比先来先服务算法低,和最短寻道时间优先算法的平均寻道时间差不多。但是它排除了磁头在盘面局部位置上的往复移动,扫描算法在很大程度上消除了最短寻道时间优先算法的不公平性,但仍有利于对中间磁道的请求。扫描算法算法是从移动臂当前位置开始沿着臂的移动方向去选择离当前移动臂最近的那个柱访问者,如果沿臂的移动方向无请求访问时,就改变臂的移动方向再选择。
1、通过对每个算法进行时间复杂度分析对比,每个算法的效率如何?
先来先服务算法的时间复杂度为O(n)最低。最短寻道时间优先算法的时间复杂度为O(n(n-1)/2) 。扫描算法中使用折半插入排序,时间复杂度为O(n^2),但是换成希尔排序可以换成时间复杂度可降为O(nlogn)。
FCFS算法 | 公平、简单 | 平均寻道距离大,仅应用在磁盘I/O较少的场合 |
---|---|---|
SSTF算法 | 性能比“先来先服务”好 | 不能保证平均寻道时间最短,可能出现“饥饿”现象 |
SCAN算法 | 寻道性能较好,可避免“饥饿”现象 | 不利于远离磁头一端的访问请求 |
2、若所有硬盘全部设计成电子硬盘,哪个磁盘调度算法最合适?
因为电子硬盘读取速度极快而且不涉及磁盘调度臂的问题,所以应该使用时间复杂度最低的FIFS算法,该算法时间复杂度为:O(n);