广州大学学生实验报告
开课学院及实验室:计算机科学与网络工程学院 软件实验室 2023年6月9日
学院 | 计算机科学与网络工程学院 | 年级/专业/班 | 姓名 | 学号 | |||
实验课程名称 | 操作系统实验 | 成绩 | |||||
实验项目名称 | 磁盘管理 | 指导老师 |
实验五 磁盘管理
一、实验目的
1、了解磁盘调度的策略和原理;
2、理解和掌握磁盘调度算法——先来先服务算法(FCFS)、最短寻道时间优先算法(SSTF)、电梯扫描算法(SCAN)。
二、实验环境
Visual Studio 2019
三、实验内容
1、模拟先来先服务法(First-Come, First-Served,FCFS),最短寻道时间优先法(Shortest Seek Time First, SSTF),电梯扫描算法(SCAN)三种磁盘调度算法;
2、对三种算法进行对比分析;
3、输入为一组请求访问磁道序列,输出为每种调度算法的磁头移动轨迹和移动的总磁道数。
四、实验原理,实验中用到的系统调用函数(包括实验原理中介绍的和自己采用的),实验步骤
1、实验原理:
①先来先服务算法(FCFS):按先来后到次序服务,未作优化。最简单的移臂调度算法是“先来先服务”调度算法,这个算法实际上不考虑访问者要求访问的物理位置,而只是考虑访问者提出访问请求的先后次序。采用先来先服务算法决定等待访问者执行输入输出操作的次序时,移动臂来回地移动。先来先服务算法花费的寻找时间较长,所以执行输入输出操作的总时间也很长。
②最短寻道时间优先算法(SSTF):最短寻找时间优先调度算法总是从等待访问者中挑选寻找时间最短的那个请求先执行的,而不管访问者到来的先后次序。与先来先服务、算法比较,大幅度地减少了寻找时间,因而缩短了为各访问者请求服务的平均时间,也就提高了系统效率。但最短查找时间优先(SSTF)调度,FCFS会引起读写头在盘面上的大范围移动,SSTF查找距离磁头最短(也就是查找时间最短)的请求作为下一次服务的对象。SSTF查找模式有高度局部化的倾向,会推迟一些请求的服务,甚至引起无限拖延(又称饥饿)。
③扫描算法(SCAN):SCAN算法又称电梯调度算法。SCAN算法是磁头前进方向上的最短查找时间优先算法,它排除了磁头在盘面局部位置上的往复移动,SCAN算法在很大程度上消除了SSTF算法的不公平性,但仍有利于对中间磁道的请求。“电梯调度”算法是从移动臂当前位置开始沿着臂的移动方向去选择离当前移动臂最近的那个柱访问者,如果沿臂的移动方向无请求访问时,就改变臂的移动方向再选择。但是,“电梯调度”算法在实现时,不仅要记住读写磁头的当前位置,还必须记住移动臂的当前前进方向。
2、C库函数
函数声明:
srand((unsigned long)time(0))//由时间确定随机序列,执行一次,即生成随机数种子
memset(used, 0, sizeof(used))//数组初始化,置为全零,记录该随机数是否使用过
rand()//rand()随机生成一个0~32767的整数,使用%操作,筛选随机数的值
void Init(int num, int range, int& start, int track[]) //随机初始化测试数据
void FCFS(int num, int& start, int track[])//先来先服务法(FCFS)
void SSTF(int num, int& start, int track[])//最短寻道时间优先法(SSTF)
void SCAN(int num, int& start, int track[])//扫描法(SCAN) //默认先向左扫描
void BubbleSort(int strack[], int n) //冒泡排序
void Swap(int& r1, int& r2) //交换函数
void Display(int start, int num, int track[]) //打印磁盘条件
函数实现介绍:
Init():初始化的磁盘序列中的磁道号不重复,使用一个数组used[max]来记录是否出现过该磁道号,出现过就不再使用该数作为新的磁道号。
FCFS():直接从当前磁头位于的位置开始从序列头遍历到序列尾。
SSTF():根据老师上课的答疑,对程序该算法进行了修改。不使用两层循环,这样子效率过低。而是先使用一层循环,将磁道序列进行排序,再根据左指针和右指针所指向的磁道进行比较,哪一个与当前磁道位置近就优先服务哪一个。
SCAN():与先来先服务算法类似,扫描法也先将磁道序列进行排序,然后从左扫描至下标为0的磁道,再扫描至磁道范围最大值的位置,即完成了向左扫描后向右扫描。
3、程序过程图:
主程序流程图:
这里以较为复杂的FCFS算法为例做出函数流程图,如下图所示:
五、实验结果分析(截屏的实验结果,与实验结果对应的实验分析)
(一)实验结果与实验程序、实验步骤、实验原理、操作系统原理的对应分析;
(二)不同条件下的实验结果反应的问题及原因;
(三)实验结果的算法时间、效率、鲁棒性等性能分析。
1、初始化磁道序列:
初始化操作要求输入磁道范围和磁道个数,调用函数Init()生成了5个范围在0~10不重复的磁道,如上图所示。
2、采用先来先服务法
磁头移动轨迹与待服务的磁道序列相同(多了初始时的磁头位置)。
移动的总磁道数计算无误,其所需要移动的总磁道数一般较多。
3、采用最短寻道时间优先法
该方法先使用一层循环将序列由小到大排序,再通过左右指针判断最近的待服务磁道,然后进行服务。其需要移动的总磁道数比先来先服务的低。
其移动的轨迹依据左右指针来移动,总是移动到距离当前位置最近的磁道。
4、采用扫描法
该方法同样是先使用一层循环将序列由小到大排序,然后默认向左扫描至磁盘最左侧(非待服务磁道中的最小的磁道),再向右扫描。
本例中,扫描法需要移动的总磁道数比最短寻道时间优先法的少,但比先来先服务法的多。
5、采用扫描法初始化一个较大的磁道序列,在调用三种算法进行分析,分析结果如下:
例子1:随机生成20个范围在0~500不重复磁道
例子2:随机生成50个范围在0~200不重复磁道
例子3:随机生成500个范围在0~900不重复磁道
通过上述例子分析可得:
先来先服务法的移动磁道总数一般较最短寻道时间优先法、扫描法多,尤其是当随机生成的磁道序列范围大数量多的时候,其效率较另外两个算法严重低下。
最短寻道时间优先法和扫描法在随机生成的磁道序列范围大数量多的时候,它们两个的移动磁道总数较为相近,但二者的代码实现截然不同。最短寻道时间优先算法依据的是排序后的当前磁道的左右指针的比较来进行读取下一个磁道;而扫描法的实现思路更加容易,它只需向左扫描,然后向右扫描即可。
但最短寻道时间它理想化了磁道序列,一般来说,磁道序列不是固定的,我们是不能已知后面所有的磁道的可能情况的。
六、实验总结
(一) 实验思考题的回答
1、通过对每个算法进行时间复杂度分析对比,每个算法的效率如何?
①先来先服务算法(FCFS):该算法的时间复杂度较低,就是遍历待服务的磁道序列然后将其逐一进行服务即可,算法设计较为简单。但是由于该算法所需要移动的磁道数目过多。效率较低,它对物理硬件的要求较高。
②最短寻道时间优先算法(SSTF):该算法的时间主要花费在确定移动磁道数目最短的目标磁道上,需要通过左指针、右指针来判断排序后的待服务磁道序列中的距离当前磁道最短距离的目标磁道。该算法的缺陷在于太远的磁道或者是一开始没有选择那边,从而导致太远的磁道越来越远,长期不能获得服务,即可能出现饥饿现象。
③扫描算法(SCAN):该算法没什么花费,只需根据冒泡排序对待服务磁道序列进行排序,然后向左扫描至下标为零的磁道,再向右扫描即可。该算法符合请求队列具有动态性质等规则。
2、若所有硬盘全部设计成电子硬盘,哪个磁盘调度算法最合适?
由于电子硬盘不存在物理寻磁道的操作,所以选择算法复杂度最低的先来先服务算法(FCFS)最合适。
(二)个人总结
1、刚开始我设计最短寻道时间优先算法时,没有考虑到时间复杂度而直接使用两层循环来进行操作,但在老师的实验课堂分析后,我的思路豁然开朗,然后就按照老师给予的思路进行代码修改,由于这个函数需要考虑的情况较多,我一开始写的时候写了好多个版本,都不能很好地兼顾每一种情况。然后我重新整理了这个算法的流程,将思路捋清楚,先写出了该函数的流程图,将流程清晰地展示出来,然后再进行代码的边写,达到了前所未有的事半功倍的效果!
2、我考虑到了实验的隐藏条件,输入的磁道个数不可能超过磁道范围,因为,一个磁道取过了一次之后,是没有必要重新再去取数据的,除非磁道的值发生了修改。
七、实验数据及源代码(学生必须提交自己设计的程序源代码,并有注释,源代码电子版也一并提交),包括思考题的程序。
注意: 实验报告文件名 学号-姓名-实验1-班级
实验数据与源代码 一个压缩包,名字和实验报告规则一样,需要有一个说明文件解释各个文件是什么文件。
代码源程序:
diskManage.h文件
#include<iostream>
#include <stdio.h>
#include <stdlib.h>
#include<math.h>
#include <string.h>
#include <time.h>
#define max 1000
using namespace std;
//随机初始化测试数据
void Init(int num, int range, int& start, int track[]);
//先来先服务法(FCFS)
void FCFS(int n, int& start, int track[]);
//最短寻道时间优先法(SSTF)
void SSTF(int n, int& start, int track[]);
//扫描法(SCAN)
void SCAN(int n, int& start, int track[]);
//冒泡排序
void BubbleSort(int track[], int n);
//交换函数
void Swap(int& r1, int& r2);
//打印磁盘条件
void Display(int start, int num, int track[]);
diskManage.cpp文件
#include"diskManage.h"
//随机初始化测试数据
void Init(int num, int range, int& start, int track[]) {//注意,这里的初始化序列不应该有相同元素
bool used[max];
memset(used, 0, sizeof(used));//数组初始化,置为全零,记录该随机数是否使用过
srand((unsigned long)time(0)); //随机数种子
start = rand() % (range + 1); //随机初始化磁头当前位置
for (int i = 0; i < num; i++) {
while (1) {
int randNum = rand() % (range + 1);//生成随机数
if (!used[randNum]) {//随机数值未重复
track[i] = randNum;//存入数组
used[randNum] = 1;//记录一下用过了该数字
break;
}
else {//随机数值重复
//获取下一个随机数
}
}
}
cout << "初始化" << num << "个范围在0~" << range << "不重复的磁道,磁道序列如下:";
for (int i = 0; i < num; i++) {
if (i % 30 == 0) { //每30个磁道为一行
cout << endl;
}
cout << track[i] << " ";
}
cout << endl;
cout << "磁盘当前的磁头位于第" << start << "个磁道" << endl;
}
//先来先服务法(FCFS)
void FCFS(int num, int& start, int track[]) {
cout << "磁头移动轨迹为:";
cout << start << "->";
for (int i = 0; i < num; i++) {//打印磁头移动轨迹
if (i != 0 && i % 30 == 0) { //每30个磁道为一行
cout << endl;
}
else if (i == num - 1) {
cout << track[i] << endl;
}
else {
cout << track[i] << "->";
}
}
int stepNum = 0;//磁头移动的总道数
stepNum = abs(start - track[0]);
for (int i = 0; i < num - 1; i++) {
stepNum += abs(track[i + 1] - track[i]);//移动的总磁道数为两两磁道之差的绝对值的和
}
cout << "移动的总磁道数为:" << stepNum << endl;
}
//最短寻道时间优先法(SSTF)
void SSTF(int num, int& start, int track[]) {
int stepNum = 0;
int current = 0;
int count = 0;//记录访问次数
int flag = 0;
int flagL = 0;
int flagR = 0;
int serveL = 0;
int serveR = 0;
int disp = 0;
int leftIndex, rightIndex;//左右指针
leftIndex = rightIndex = 0;
bool used[max];
memset(used, 0, sizeof(used));//记录是否已服务,初始时,全未使用过
BubbleSort(track, num); //采用冒泡排序对磁道序列进行从小到大排序
cout << "排序后的磁道序列为:";
for (int i = 0; i < num; i++) {//将磁道进行排序并找到最近的那个磁道
if (i % 30 == 0) { //每30个磁道为一行
cout << endl;
}
cout << track[i] << " ";
if (abs(track[i] - start) <= abs(track[current] - start))
current = i;
if (track[i] == start)//当前磁道属于磁盘序列,flag不为零
flag = i;
}
cout << endl;
cout << "磁头移动轨迹为:";
if (!flag) {//当前磁道不属于磁盘序列
cout << start << "->" << track[current] << "->";
disp++;
count++;
used[current] = 1;
}
else {//当前磁道属于磁盘序列
cout << track[current] << "->";
disp++;
count++;
used[current] = 1;
}
stepNum += abs(start - track[current]);//记录移动道数
while (count != num) { //循环访问磁道序列
if (current > 0 && current < num - 1) {//判断当前磁道是否位于磁盘边界
if (!flagL) {//上一阶段服务了左边,右指针不变;未服务左边,则右指针右移
rightIndex = current + 1;
}
if (!flagR) {//上一阶段服务了右边,左指针不变;未服务右边,则左指针左移
leftIndex = current - 1;
}
flagL = flagR = 0;
if (abs(track[current] - track[leftIndex]) < abs(track[current] - track[rightIndex])) {//左边近
if (count == num - 1) {//最后一个访问磁道
cout << track[leftIndex] << endl;
}
else {
if (disp % 30 == 0) {
cout << endl;
}
cout << track[leftIndex] << "->";
disp++;
}
count++;
stepNum += abs(track[leftIndex] - track[current]);
used[leftIndex] = 1;
current = leftIndex;//current指针指向当前服务磁道,
flagL = 1;//记录服务了左边,控制下一轮右指针不变
}
else {//服务右边
if (count == num - 1) {//最后一个访问磁道
cout << track[rightIndex] << endl;
}
else {
if (disp % 30 == 0) {
cout << endl;
}
cout << track[rightIndex] << "->";
disp++;
}
count++;
stepNum += abs(track[rightIndex] - track[current]);
used[rightIndex] = 1;
current = rightIndex;//current指针指向当前服务磁道
flagR = 1;//记录服务了右边,控制下一轮左指针不变
}
}
else if (current == 0) {//当前磁道位于左边界,一直服务右边
while (count != num) {
rightIndex = current + 1;
if (!used[rightIndex]) {//当前磁道未服务过
if (count == num - 1) {//最后一个访问磁道
cout << track[rightIndex] << endl;
}
else {
if (disp % 30 == 0) {
cout << endl;
}
cout << track[rightIndex] << "->";
disp++;
}
count++;
if (!serveR) {//向右扫描还未服务磁道
stepNum += abs(track[0] - track[rightIndex]);//记录移动道数
serveR = 1;
}
else {//向右扫描已服务了磁道
stepNum += abs(track[rightIndex] - track[current]);//记录移动道数
}
used[rightIndex] = 1;
current = rightIndex;//current指针指向当前服务磁道
}
else {//当前磁道服务过,指针再右移
current = rightIndex;
}
}
}
else {//当前磁道位于右边界,一直服务左边
while (count != num) {
leftIndex = current - 1;
if (!used[leftIndex]) {//当前磁道未服务过
if (count == num - 1) {//最后一个访问磁道
cout << track[leftIndex] << endl;
}
else {
if (disp % 30 == 0) {
cout << endl;
}
cout << track[leftIndex] << "->";
disp++;
}
count++;
if (!serveR) {//向左扫描还未服务磁道
stepNum += abs(track[num-1] - track[leftIndex]);//记录移动道数
serveR = 1;
}
else {//向左扫描已服务了磁道
stepNum += abs(track[leftIndex] - track[current]);//记录移动道数
}
used[leftIndex] = 1;
current = leftIndex;//current指针指向当前服务磁道
}
else {//当前磁道服务过,指针再左移
current = leftIndex;
}
}
}
}
cout << "移动的总磁道数为:" << stepNum << endl;
}
//扫描法(SCAN) //磁头从盘的一端移动到另一端 //默认先走左边
void SCAN(int num, int& start, int track[]) {
int stepNum = 0;
int current = 0;
int count = 0;//记录访问次数
int flag = 0;//记录当前磁道是否属于磁盘序列
int serveR = 0;//记录向右扫描有没有服务到磁道
int leftIndex, rightIndex;//左右指针
int disp = 0;
leftIndex = rightIndex = 0;
bool used[max];
memset(used, 0, sizeof(used));//记录是否已服务,初始时,全未使用过
BubbleSort(track, num); //采用冒泡排序对磁道序列进行从小到大排序
cout << "排序后的磁道序列为:";
for (int i = 0; i < num; i++) {//将磁道进行排序并找到当前磁道左边第一个磁道
if (i % 30 == 0) { //每30个磁道为一行
cout << endl;
}
cout << track[i] << " ";
if (track[i] < start) {//记录当前磁道左侧第一个的磁道
current = i;
}
if (track[i] == start)//当前磁道属于磁盘序列,flag不为零
flag = i;
}
cout << endl;
cout << "磁头移动轨迹为:";
if (!flag) {//当前磁道不属于磁盘序列
cout << start << "->" << track[current] << "->";
count++;
used[current] = 1;
stepNum += abs(start - track[current]);//记录移动道数
}
else {//当前磁道属于磁盘序列
cout << track[flag] << "->";
count++;
used[flag] = 1;
stepNum += 0;//记录移动道数
}
if (flag != 0) {
cout << track[current] << "->";
count++;
used[current] = 1;
stepNum += abs(track[flag] - track[current]);//记录移动道数
}
while (current != 0) {//一直扫描到最左侧
current--;
if (disp % 30 == 0) {
cout << endl;
}
cout << track[current] << "->";
disp++;
count++;
used[current] = 1;
stepNum += abs(track[current + 1] - track[current]);//记录移动道数
}
if (track[current] != 0) {
cout << "0->";
stepNum += track[current] - 0;//记录移动道数
}
while (current != num - 1) {//一直扫描到最右侧
current++;
if (!used[current]) {//未服务过
if (current == num - 1) {
cout << track[current];
}
else {
if (disp % 30 == 0) {
cout << endl;
}
cout << track[current] << "->";
disp++;
}
count++;
used[current] = 1;
if (!serveR) {//向右扫描还未服务磁道
if (track[current] != 0) {
stepNum += abs(0 - track[current]);//记录移动道数
}
else {
stepNum += abs(track[0] - track[current]);//记录移动道数
}
serveR = 1;
}
else {//向右扫描已服务了磁道
stepNum += abs(track[current - 1] - track[current]);//记录移动道数
}
}
else {//服务过,不进行任何操作
}
}
cout << endl;
cout << "移动的总磁道数为:" << stepNum << endl;
}
//冒泡排序
void BubbleSort(int strack[], int n) { //把小的元素冒在数组前面
int i, j;
bool exchange;
for (i = 0; i < n - 1; i++){
exchange = false; //判别因子
for (j = n - 1; j > i; j--)
if (strack[j] < strack[j - 1]) { //相邻元素反序时
Swap(strack[j], strack[j - 1]); //将两元素交换
exchange = true;
}
if (!exchange) //本趟没有发生交换,中途结束算法
break;
}
}
//交换函数
void Swap(int& r1, int& r2) {
int temp;
temp = r1;
r1 = r2;
r2 = temp;
}
//打印磁盘条件
void Display(int start, int num, int track[]) {
cout << "待服务的磁道序列为:";
for (int i = 0; i < num; i++) {
if (i % 30 == 0) { //每30个磁道为一行
cout << endl;
}
cout << track[i] << " ";
}
cout << endl;
cout << "磁盘当前的磁头位于第" << start << "个磁道" << endl;
}
Mian.cpp文件
#include"diskManage.h"
int main() {
int num = 0; //磁道个数
int range = 0;//磁道范围
int start = 0; //磁头的位置
int track[max];//请求服务序列
int choose = 0;
while (1){
cout << "-----------------------------------------控制台分隔符-----------------------------------------" << endl;
cout << "0.初始化操作 1.采用先来先服务法(FCFS) 2.采用最短寻道时间优先法(SSTF)" << endl;
cout << "3.采用扫描法(SCAN) 4.退出磁盘调度程序" << endl;
printf("请选择将要进行的操作:");
cin >> choose;
switch (choose){
case 0:
cout << "------------------------------------------初始化操作------------------------------------------" << endl;
cout << "请输入磁道范围(0~1000):0~";//注意隐含条件:磁道的范围应该大于等于磁道个数,因为每个磁道最多只需要扫描一次
while (1) {
cin >> range;//初始化磁道范围
if (range < 0 || range>1000)
cout << "输入的磁道范围超出接受范围,请重新输入磁道范围(0~1000):0~";
else
break;
}
cout << "请输入磁道个数(0~1000):";
while (1) {
cin >> num;//初始化磁道个数
if (num < 0 || num>1000)
cout << "输入的磁道个数超出接受范围,请重新输入磁道个数(0~1000):";
else
break;
}
while (1) {
if (range + 1 >= num) {
cout << "输入成功" << endl;
Init(num, range, start, track); //随机初始化测试数据
break;
}
else {
cout << "磁道范围+1应该大于等于磁道个数,请重新输入" << endl;
cout << "请输入磁道范围(0~1000):0~";//注意隐含条件:磁道的范围应该大于等于磁道个数,因为每个磁道最多只需要扫描一次
while (1) {
cin >> range;//初始化磁道范围
if (range < 0 || range>1000)
cout << "输入的磁道范围超出接受范围,请重新输入磁道范围(0~1000):0~";
else
break;
}
cout << "请输入磁道个数(0~1000):";
while (1) {
cin >> num;//初始化磁道个数
if (num < 0 || num>1000)
cout << "输入的磁道个数超出接受范围,请重新输入磁道个数(0~1000):";
else
break;
}
}
}
break;
case 1:
cout << "-------------------------------------先来先服务法(FCFS)-------------------------------------" << endl;
Display(start, num, track);
FCFS(num, start, track);
break;
case 2:
cout << "----------------------------------最短寻道时间优先法(SSTF)----------------------------------" << endl;
Display(start, num, track);
SSTF(num, start, track);
break;
case 3:
cout << "----------------------------------------扫描法(SCAN)----------------------------------------" << endl;
Display(start, num, track);
SCAN(num, start, track);
break;
case 4:
cout << "---------------------------------------退出磁盘调度程序---------------------------------------" << endl;
cout << "退出磁盘调度程序" << endl;
exit(0);
}
}
system("pause");
return 0;
}