虚拟硬盘调度
编写C程序模拟实现硬盘柱面访问调度算法包括FCFS,SSTF,C-SCAN,LOOK,C-LOOK,并设计输入用例验证结果。
硬盘调度实际包括旋转和巡道,因为硬盘总是按一定速度15000r/s,同一方向旋转,所以旋转时间是我们没办法控制的。同时,旋转时间跟巡道时间相比很小,所以我们应该将重点放在减小巡道时间上。
我们可以通过不同的巡道策略来提高硬盘调度的效率。通过不同的巡道策略尽量,在对一个给定的柱面请求序列的情况下,使得磁头总的移动距离尽量小,进而使得总的巡道时间尽量小。
在下面的实现中,我们使用一个整型数组来表示一个柱面请求序列,在给定磁头初始位置的情况下依据不同的策略计算出磁头总的移动距离。
磁道的编号范围是:0~199
FCFS
FCFS是先来先服务原则,也就是顺序完成柱面请求序列的请求。
这种策略的实现很简单,但是总的磁头巡道距离也会很大。
代码细节
void FCFS()
{
printf("Please input the sequence with requests for cylinders:\n");
for (int i = 0; i < 8; i++)
{
scanf("%d", &c[i]);
}
for (int i = 0; i < 8; i++)
{
total = total + abs(head - c[i]);
head = c[i];
}
printf("Total distance covered: %d", total);
}
在输入柱面请求序列之后直接顺序处理请求。
测试
分析
已知输入的柱面请求序列:{98,183,37,122,14,124,65,67};
计算:(98-53)+(183-98)+(183-37)+(122-37)+(122-14)+(124-14)+(124-65)+(67-65) = 45 + 85 + 146+ 85 + 108 + 110 + 59 + 2 = 640;
正确!
SSTF
SSTF最近邻策略:从柱面请求中找到距离当前磁头所在磁道最近的请求磁道。
该算法策略相当于贪心策略,总是寻找所需巡道时间最短的磁盘请求并将他完成。
代码细节
void SSTF()
{
printf("Please input the sequence with requests for cylinders1:\n");
for (int i = 0; i < 8; i++)
{
scanf("%d", &c[i]);
}
for (int i = 0; i < 8; i++)
{
int temp = 1000;
int flag = 0;
for (int j = 0; j < 8; j++)
{
if (temp > abs(head - c[j]))
{
flag = j;
temp = abs(head - c[j]);
}
}
total = total + abs(head - c[flag]);
head = c[flag];
c[flag] = 10000; //标记为已访问
}
printf("Total distance covered: %d\n", total);
}
在给定磁头初始位置之后,在柱面请求序列中找到距离初始位置最近的磁盘并将该请求完成,更新磁头位置,以此循环。
测试
分析
已知输入的柱面请求序列:{98,183,37,122,14,124,65,67};
计算:(65-53)+(67-65)+(67-37)+(37-14)+(98-14)+(122-98)+(124-122)+(183-124)=12+2+30+23+84+24+2+59 = 236
正确!
SCAN
SCAN是一种电梯算法,磁头从初始位置开始,沿着某个方向移动知道该方向的边界,然后再沿着另一个方向移动直到完成所有请求。
在程序中,磁头的初始位置为53,磁头先朝着编号为0的磁道运动,中间完成请求磁道为0~53之间的请求,到达编号为0的磁道之后就朝着编号为199的磁道移动,同理,中间按从小到达的顺序完成请求。
代码细节
void SCAN()
{
printf("Please input the sequence with requests for cylinders:\n");
for (int i = 0; i < 8; i++)
{
scanf("%d", &c[i]);
}
// int c[8] = {98, 183, 37, 122, 14, 124, 65, 67};
int flag1 = 0; //看看是否还存在小于初始head的请求
int flag = 0;
for (int i = 0; i < 8; i++)
{
int temp = 1000;
if (flag1 == 0)
{
for (int j = 0; j < 8; j++)
{
if (c[j] < head)
{
flag1 = 1;
if (temp > abs(head - c[j]))
{
temp = abs(head - c[j]);
flag = j;
}
}
}
}
if (flag1 == 0) //没有比初始head小的请求了
{
flag1 = 2;
total = total + abs(head - 0);
i--;
head = 0;
}
else if (flag1 == 1) //还存在比初始head小的请求
{
flag1 = 0;
total = total + abs(head - c[flag]);
head = c[flag];
c[flag] = 10000; //标记为已完成
}
else if (flag1 == 2)
{
for (int j = 0; j < 8; j++)
{
if (c[j] > head && c[j] != 10000)
{
if (temp > abs(head - c[j]))
{
temp = abs(head - c[j]);
flag = j;
}
}
}
total = total + abs(head - c[flag]);
head = c[flag];
c[flag] = 10000; //标记为已完成
}
}
printf("%d", head);
printf("\n");
total = total + abs(head - 199);
printf("Total distance covered: %d\n", total);
}
具体的实现方法是:先找到小于初始磁道位置并举例初始位置最近的磁道的请求,完成并更新磁头位置,依次循环直到没有比磁头位置小的磁道请求,直接将磁头移动到编号为0的磁道,然后遍历剩下的请求序列,每次都是找到距离磁头最近的磁道请求并更新磁头位置,以此循环直到完成所有请求。
测试
分析
已知输入的柱面请求序列:{98,183,37,122,14,124,65,67};
计算:(53-37)+(37-14)+(14-0)+(65-0)+(67-65)+(98-67)+(122-98)+(124-122)+(183-124)=16+23+14+65+2+31+24+2+59 = 236
正确!
LOOK
LOOK算法与SCAN算法大同小异,只不过在朝着初始方向移动的时候不移动到编号为0的磁道,而是移动到编号最小的请求磁道。
代码细节
void LOOK()
{
printf("Please input the sequence with requests for cylinders:\n");
// for (int i = 0; i < 8; i++)
// {
// scanf("%d", &c[i]);
// }
int c[8] = {98, 183, 37, 122, 14, 124, 65, 67};
int flag1 = 0; //看看是否还存在小于初始head的请求
int flag = 0;
for (int i = 0; i < 8; i++)
{
int temp = 1000;
if (flag1 == 0)
{
for (int j = 0; j < 8; j++)
{
if (c[j] < head)
{
flag1 = 1;
if (temp > abs(head - c[j]))
{
temp = abs(head - c[j]);
flag = j;
}
}
}
}
if (flag1 == 0) //没有比初始head小的请求了
{
flag1 = 2;
i--;
}
else if (flag1 == 1) //还存在比初始head小的请求
{
flag1 = 0;
total = total + abs(head - c[flag]);
head = c[flag];
c[flag] = 10000; //标记为已完成
}
else if (flag1 == 2)
{
for (int j = 0; j < 8; j++)
{
if (c[j] > head && c[j] != 10000)
{
if (temp > abs(head - c[j]))
{
temp = abs(head - c[j]);
flag = j;
}
}
}
total = total + abs(head - c[flag]);
head = c[flag];
c[flag] = 10000; //标记为已完成
}
}
printf("Total distance covered: %d\n", total);
}
可以看到LOOK和SCAN算法的差别只是在到达初始移动方向的编号最小的磁道时,SCAN算法直接走到该方向的边界,而LOOK算法直接朝着反方向移动。
测试
分析
已知输入的柱面请求序列:{98,183,37,122,14,124,65,67};
计算:(53-37)+(37-14)+(14-0)+(65-0)+(67-65)+(98-67)+(122-98)+(124-122)+(183-124)=16+23+14+65+2+31+24+2+59 = 236-14*2=208
正确!
C-SCAN
对比SCAN算法,C-SCAN算法也是朝着初始方向移动到该方向的边界,不同的是SCAN算法到达边界之后就朝着相反方向移动,而C-SCAN算法是在到达初始方向的边界后,立刻移动到另一个边界,中间不完成任何请求,然后再朝着初始方向继续移动。
代码细节
void C_SCAN()
{
printf("Please input the sequence with requests for cylinders:\n");
for (int i = 0; i < 8; i++)
{
scanf("%d", &c[i]);
}
// int c[8] = {98, 183, 37, 122, 14, 124, 65, 67};
int flag1 = 0;
int head = 53;
int total = 0;
for (int i = 0; i < 8; i++)
{
int temp = 1000;
int flag = 0;
if (flag1 == 0)
{
for (int j = 0; j < 8; j++)
{
if (c[j] > head && c[j] != 10000)
{
flag1 = 1;
if (temp > abs(head - c[j]))
{
temp = abs(head - c[j]);
flag = j;
}
}
}
}
if (flag1 == 0) //运动方向上不存在请求了
{
flag1 = 2;
i--;
total = total + abs(head - 199) + 199;
head = 0;
}
else if (flag1 == 1) //运动方向上还存在请求
{
flag1 = 0;
total = total + abs(head - c[flag]);
head = c[flag];
c[flag] = 10000;
}
else if (flag1 == 2)
{
for (int j = 0; j < 8; j++)
{
if (c[j] > head && c[j] != 10000)
{
if (temp > abs(head - c[j]))
{
flag = j;
temp = abs(head - c[j]);
}
}
}
total = total + abs(head - c[flag]);
head = c[flag];
c[flag] == 10000;
}
}
printf("Total distance covered: %d\n", total);
}
测试
分析
已知输入的柱面请求序列:{98,183,37,122,14,124,65,67};
计算:(65-53)+(67-65)+(98-67)+(122-98)+(124-122)+(183-98)+(199-183)+(199-0)+(14-0)+(37-14)=12+2+31+24+2+85+16+199+14+23 =382
正确!
C-LOOK
C-LOOK算法与C-SCAN算法类似,但是在到达初始方向上最靠近边界的一个请求磁道之后,C-SCAN算法接下来直接去到边界而C-LOOK则是立马移动到另一个边界,然后继续按照S-SCAN算法的移动方式继续移动。
代码细节
void C_LOOK()
{
printf("Please input the sequence with requests for cylinders:\n");
// for (int i = 0; i < 8; i++)
// {
// scanf("%d", &c[i]);
// }
int c[8] = {98, 183, 37, 122, 14, 124, 65, 67};
int flag1 = 0;
int head = 53;
int total = 0;
for (int i = 0; i < 8; i++)
{
int temp = 1000;
int flag = 0;
if (flag1 == 0)
{
for (int j = 0; j < 8; j++)
{
if (c[j] > head && c[j] != 10000)
{
flag1 = 1;
if (temp > abs(head - c[j]))
{
temp = abs(head - c[j]);
flag = j;
}
}
}
}
if (flag1 == 0) //运动方向上不存在请求了
{
flag1 = 2;
i--;
total = total + abs(head - 0);
head = 0;
}
else if (flag1 == 1) //运动方向上还存在请求
{
flag1 = 0;
total = total + abs(head - c[flag]);
head = c[flag];
c[flag] = 10000;
}
else if (flag1 == 2)
{
for (int j = 0; j < 8; j++)
{
if (c[j] > head && c[j] != 10000)
{
if (temp > abs(head - c[j]))
{
flag = j;
temp = abs(head - c[j]);
}
}
}
total = total + abs(head - c[flag]);
head = c[flag];
c[flag] == 10000;
}
}
printf("Total distance covered: %d\n", total);
}
测试
分析
已知输入的柱面请求序列:{98,183,37,122,14,124,65,67};
计算:(65-53)+(67-65)+(98-67)+(122-98)+(124-122)+(183-98)+(199-183)+(199-0)+(14-0)+(37-14)=12+2+31+24+2+85+16+199+14+23 =382 -(199-183)-(199-183) = 350
正确!