一、实验目的
通过这次实验,加深对磁盘调度算法的理解,进一步掌握先来先服务FCFS,最短寻道时间优先SSTF,SCAN和循环SCAN算法的实现方法。
二、需求分析
2.1 程序设计任务
本程序旨在模拟磁盘调度算法,包括以下四种算法:
- 先来先服务调度算法(FCFS);
- 最短寻道时间优先调度算法(SSTF);
- 扫描算法(SCAN);
- 循环扫描算法(C-SCAN)。
程序能够按照用户输入的参数,依次执行上述调度算法,计算并输出每种算法的磁头移动路径、总寻道距离和平均寻道长度。
2.2 输入内容、形式及范围
1.磁道数量 (n):
输入内容:正整数 n,表示磁盘上磁道访问序列的数量。
2.磁道访问序列:
输入内容:磁道号组成的正整数序列。
3.起始磁道号 (m):
输入内容:程序开始运行时的磁头所在磁道号。
4.磁头移动方向(仅适用于 SCAN 和 C-SCAN):
输入内容:用户选择磁头移动方向(1 表示磁道号增加的方向,0 表示磁道号减少的方向)。
5.调度算法选择:
输入内容:整数值,用于选择调度算法:
1 - FCFS
2 - SSTF
3 - SCAN
4 - C-SCAN。
2.3 输出形式
算法过程展示:
程序根据所选调度算法,输出磁头访问顺序和每次移动的寻道距离。
统计信息:
磁头移动的总寻道距离;
平均寻道长度。
优化对比:
用户可以运行多个调度算法或输入多种磁道序列,比较它们的性能(总寻道距离和平均寻道长度)。
2.4 测试数据
2.4.1 正确的输入
2.4.2 错误的输入
算法选择不在1-5间
三、概要设计
3.1 抽象数据类型的定义
1.磁盘调度数据类型
用于表示磁盘调度算法中的核心属性和辅助变量,包括:
核心数据结构和属性:
- vector<int> cidao:磁道访问序列,用于记录请求访问的磁道号。
- int currentTrack:磁头当前的位置(开始磁道号)。
- int sum:记录磁头移动的总距离,即磁头从一个磁道移动到另一个磁道所经过的总磁道数。
- float averageDistance:平均寻道长度(计算公式为 sum / 磁道数量)。
- int direction:磁头移动方向,仅适用于 SCAN 和 C-SCAN 算法(1 表示增大方向,0 表示减小方向)。
辅助变量:
- vector<int> sortedCidao:用于存储排序后的磁道号序列(适用于 SSTF、SCAN 和 C-SCAN 算法的调度需求)。
- int l 和 int r:左指针和右指针,分别表示当前磁道在排序后序列中的左右邻居(SSTF 算法中使用)。
- int tempTrack:临时变量,用于保存当前磁头位置,便于更新。
2.辅助变量描述
- sum:记录磁头移动总距离,供每种算法统计每次磁头移动距离之和,用于最终计算。
- averageDistance:每种算法计算完毕后自动求得的平均寻道长度。
- direction:在 SCAN 和 C-SCAN 算法中,辅助决定磁头是否反向处理边界条件或者回绕。
- k:索引变量,用于记录磁头访问序列排序中第一个大于等于 currentTrack 的元素(在 SCAN 和 C-SCAN 中用于判断方向)。
- vector<int>::iterator:用于遍历磁道序列。
3.2 主程序流程
1.输入阶段
输入磁道数量、访问序列、磁头起始位置、方向(用于 SCAN 和 C-SCAN),以及选择的算法类型(1. FCFS, 2. SSTF, 3. SCAN, 4. C-SCAN)。
校验数据合法性(非负整数、范围正确、方向为 0 或 1)。
2.算法处理阶段
- FCFS:按输入顺序访问磁道,累计移动距离。
- SSTF:每次优先访问最近的磁道,重复直到访问完所有磁道。
- SCAN:磁头按方向扫描到边界后反向,累计移动距离。
- C-SCAN:磁头按方向扫描到边界后回绕到另一端继续扫描。
3.结果输出阶段
输出每种算法的访问顺序和总移动距离。
计算并输出平均寻道长度 averageDistance。
四、详细设计
4.1 磁盘调度算法实现
1.输入阶段
用户输入:
磁道数量 n,即磁盘调度中要访问的磁道数量;
磁道访问序列,存入 cidao 数组;
当前磁头起始位置,存入 currentTrack;
对于 SCAN 和 C-SCAN 的算法,还需输入磁头移动的方向(1 表示向正方向,即磁道号增大的方向,0 表示向负方向,即磁道号减小的方向);
用户选择的调度算法类型:1 表示 FCFS,2 表示 SSTF,3 表示 SCAN,4 表示 C-SCAN。
数据校验:
确保磁道访问序列是非负整数;
确保磁头起始位置 currentTrack 在磁道访问序列的合理范围内;
确保输入的方向符合要求(SCAN 和 C-SCAN 中的方向需为 0 或 1)。
2.算法处理阶段
FCFS(先来先服务)实现:
从 cidao 队列按顺序访问磁道号。
计算两相邻磁道号之间的绝对差值,并累积到 sum 中记录总移动距离。
SSTF(最短寻道时间优先)实现:
将磁道访问序列cidao排序成sortedCidao。
找到 currentTrack 在 sortedCidao 中的位置 k,然后比较左右两侧(索引 l = k-1 和 r = k)的距离,从距离最近的开始访问并更新磁头当前位置。
重复上述过程,直到访问完所有磁道号。
SCAN 实现:
将磁道访问序列 cidao 排序生成 sortedCidao。
根据磁头的移动方向(direction = 1 表示递增,direction = 0 表示递减):
- 递增方向:从当前磁头位置 currentTrack 向右访问比它大的磁道号依次处理。直到达到最大磁道号后切换方向(如果到达边界则旋转回边界磁道)。
- 递减方向:从当前磁头位置向左访问比它小的磁道号。直到达到最小磁道号后切换方向。
在每次移动操作中,记录移动距离,并累积到 sum 中。
C-SCAN(循环扫描)实现:
将磁道访问序列 cidao 排序生成 sortedCidao。
根据磁头的移动方向固定为循环扫描逻辑:
- 递增方向:从 currentTrack 开始向右扫描比它大的磁道号,访问到达最大磁道号后直接回绕到最小磁道号,然后继续从小到大访问。
- 递减方向:从 currentTrack 开始向左扫描比它小的磁道号,访问到达最小磁道号后直接回绕到最大磁道号,然后继续从大到小访问。
在每次移动操作中,记录磁头移动距离,并累积到 sum 中。
五、调试分析
5.1 调试过程中遇到的问题及解决方案
问题 1: SCAN算法找到第一个位置后不知如何用迭代器反向遍历
解决方案: 使用rend()和make_reverse_iterator()
5.2 算法的时空复杂度分析
5.2.1 算法时间复杂度分析
(1) FCFS(先来先服务)
主要操作是计算磁道之间的绝对差值,进行一次遍历即可完成。
时间复杂度:O(m),其中 m 为磁道数量。
(2) SSTF(最短寻道时间优先)
需要对磁道序列进行排序,时间复杂度为 O(m log m)。
在排序后的数组中,每次选择距离当前磁道最近的磁道(线性搜索),时间复杂度为 O(m)。
总时间复杂度:O(m log m)。
(3) SCAN(扫描算法)
需要对磁道序列进行排序,时间复杂度为 O(m log m)。
根据磁头的移动方向,依次遍历磁道,时间复杂度为 O(m)。
总时间复杂度:O(m log m)。
(4) C-SCAN(循环扫描算法)
同样需要对磁道序列进行排序,时间复杂度为 O(m log m)。
遍历磁道两次(顺序扫描一次,回绕后再扫描一次),时间复杂度为 O(m)。
总时间复杂度:O(m log m)。
5.2.2 空间复杂度分析
(1) FCFS
仅需存储磁道序列(O(m))和一些辅助变量(O(1))。
空间复杂度:O(m)。
(2) SSTF
需要存储排序后的磁道序列(O(m))以及一些辅助变量(O(1))。
空间复杂度:O(m)。
(3) SCAN
需要存储排序后的磁道序列(O(m))以及辅助变量(O(1))。
空间复杂度:O(m)。
(4) C-SCAN
同样需要存储排序后的磁道序列(O(m))和辅助变量(O(1))。
空间复杂度:O(m)。
5.2.3 改进设想
减少磁道排序的负担:对磁道序列使用更高效的排序算法,或者仅排序当前位置附近的子序列,减少排序开销。
并行化处理:针对排序操作,可以采用多线程技术并行实现,从而提高效率。
缓存访问结果:通过缓存访问过的磁道序列,避免重复计算移动距离,从而降低时间复杂度。
5.3 经验和体会
通过本次项目,深入理解了磁盘调度算法的原理及实现方法,尤其是在处理数据校验和优化算法效率方面积累了经验。
- FCFS 和 SSTF 算法实现相对简单,但需要注意边界条件处理。
- SCAN 和 C-SCAN 算法更复杂,但提供更合理的寻道性能,在实现过程中需要严格遵循磁头移动方向和边界条件的处理逻辑。
通过对比不同算法,深刻体会到算法选择的合理性对系统性能的影响,同时也了解到在实现过程中注重代码可读性和效率的重要性。
六、用户使用说明
1.输入参数
程序会提示用户输入磁道数量、磁道序列、磁头当前位置,以及磁头移动方向(仅适用于 SCAN 和 C-SCAN 算法)。
2.选择算法
用户可根据提示选择调度算法(1-FCFS, 2-SSTF, 3-SCAN, 4-C-SCAN)。
输入 5 退出程序。
3.查看结果
程序会输出磁道访问顺序、总移动距离,以及平均寻道长度。
七、测试结果
1.输入:
2.先来先服务算法(FCFS):
3.最短服务时间优先算法(SSTF)
4.扫描算法(SCAN)
5.循环扫描算法(C-SCAN)
#include <bits/stdc++.h>
using namespace std;
// 先来先服务调度算法 FCFS
void FCFS(const vector<int>& array, int m) {
int sum = 0;
float avg;
int now;
cout << "\n请输入当前的磁道号: ";
cin >> now;
cout << "\nFCFS 调度结果: ";
for (int i = 0; i < m; i++)
cout << array[i] << " ";
sum = abs(now - array[0]);
for (int i = 1; i < m; i++)
sum += abs(array[i] - array[i - 1]); // 移动的总道数
avg = static_cast<float>(sum) / m; // 计算平均寻道长度
cout << "\n移动的总道数: " << sum << "\n";
cout << "平均寻道长度: " << avg << "\n";
}
// 最短服务时间优先调度算法 SSTF
void SSTF(vector<int> array, int m) {
sort(array.begin(), array.end());
for (int i = 0; i < m; i++)
cout << array[i] << " ";
int now, sum = 0;
cout << "\n请输入当前的磁道号: ";
cin >> now;
cout << "\nSSTF 调度结果: ";
// 找到当前磁道位置右邻位置
int k = 0;
while (k < m && array[k] < now)
k++;
int l = k - 1;
int r = k;
while (l >= 0 || r < m) {
if (l >= 0 && (r == m || (abs(now - array[l]) <= abs(array[r] - now)))) {
sum += abs(now - array[l]);
now = array[l--];
cout << now << " ";
}
else if (r < m) {
sum += abs(array[r] - now);
now = array[r++];
cout << now << " ";
}
}
float avg = static_cast<float>(sum) / m;
cout << "\n移动的总道数: " << sum << "\n";
cout << "平均寻道长度: " << avg << "\n";
}
// 扫描算法 SCAN
void SCAN(vector<int> array, int m) {
sort(array.begin(), array.end()); // 使用 std::sort 进行排序
for (int i = 0; i < m; i++)
cout << array[i] << " ";
int now, direction;
cout << "\n请输入当前的磁道号: ";
cin >> now;
cout << "请输入磁头移动方向 (1代表正向,0代表反向): ";
cin >> direction;
cout << "\nSCAN 调度结果:";
int sum = 0;
if (direction == 1) { // 正向扫描
auto it = lower_bound(array.begin(), array.end(), now);
for (auto i = it; i != array.end(); i++) {
cout << *i << " ";
sum += abs(now - *i);
now = *i;
}
for (auto i = make_reverse_iterator(it); i != array.rend(); i++) {
cout << *i << " ";
sum += abs(now - *i);
now = *i;
}
}
else { // 反向扫描
auto it = lower_bound(array.begin(), array.end(), now);
for (auto i = make_reverse_iterator(it); i != array.rend(); i++) {
cout << *i << " ";
sum += abs(now - *i);
now = *i;
}
for (auto i = it; i != array.end(); i++) {
cout << *i << " ";
sum += abs(now - *i);
now = *i;
}
}
float avg = static_cast<float>(sum) / m;
cout << "\n移动的总道数: " << sum << "\n";
cout << "平均寻道长度: " << avg << "\n";
}
// 循环扫描算法 C-SCAN
void CSCAN(vector<int> array, int m) {
sort(array.begin(), array.end());
for (int i = 0; i < m; i++)
cout << array[i] << " ";
int now, direction;
cout << "\n请输入当前的磁道号: ";
cin >> now;
cout << "请输入磁头移动方向 (1代表正向,0代表反向): ";
cin >> direction;
cout << "\nC-SCAN 调度结果:";
int sum = 0;
// 定位开始位置
auto it = lower_bound(array.begin(), array.end(), now);
if (direction == 1) { // 正向移动
// 扫描到磁道号最大的地方
for (auto i = it; i != array.end(); i++) {
cout << *i << " ";
sum += abs(now - *i);
now = *i;
}
// 回绕到磁道号最小的地方
if (!array.empty()) {
sum += abs(now - array[0]);
now = array[0];
for (auto i = array.begin(); i != it; i++) {
cout << *i << " ";
sum += abs(now - *i);
now = *i;
}
}
}
else { // 反向移动
// 从当前磁道号开始反向扫描
for (auto i = make_reverse_iterator(it); i != array.rend(); i++) {
cout << *i << " ";
sum += abs(now - *i);
now = *i;
}
// 回绕到磁道号最大的地方
if (!array.empty()) {
sum += abs(now - array.back());
now = array.back();
for (auto i = array.end(); i != it; i++) {
cout << *i << " ";
sum += abs(now - *i);
now = *i;
}
}
}
float avg = static_cast<float>(sum) / m;
cout << "\n移动的总道数: " << sum << "\n";
cout << "平均寻道长度: " << avg << "\n";
}
// 主函数
int main() {
vector<int> cidao; // 磁道号序列
int count;
cout << "请先输入磁道数量: ";
cin >> count;
cidao.resize(count);
cout << "请先输入磁道序列: \n";
for (int i = 0; i < count; i++)
cin >> cidao[i];
cout << "\n磁道读取结果: \n";
for (int i = 0; i < count; i++)
cout << cidao[i] << " ";
cout << "\n";
while (true) {
int choice;
cout << "\n算法选择: \n";
cout << " 1. 先来先服务算法(FCFS) \n";
cout << " 2. 最短服务时间优先算法(SSTF) \n";
cout << " 3. 扫描算法(SCAN) \n";
cout << " 4. 循环扫描算法(C-SCAN) \n";
cout << " 5. 退出\n";
cout << "\n请选择: ";
cin >> choice;
if (choice == 5)
break;
switch (choice) {
case 1:
FCFS(cidao, count);
break;
case 2:
SSTF(cidao, count);
break;
case 3:
SCAN(cidao, count);
break;
case 4:
CSCAN(cidao, count);
break;
default:
cout << "无效输入,请重新选择!\n";
}
}
return 0;
}