实验八:磁盘移臂调度算法实验报告
1. 实验目的
加深对操作系统设备管理技术的理解,体验磁盘移臂调度算法的重要性。掌握几种重要的磁盘移臂调度算法,练习模拟算法的编程技巧,锻炼研究分析实验数据的能力。
2. 实验内容
实验中,我们使用C++编写了模拟磁盘移臂调度算法的程序。程序实现了以下几种磁盘移臂调度算法:
- 先来先服务算法(FCFS)
- 最短寻道时间优先算法(SSTF)
- 电梯调度算法(SCAN)
- 均匀电梯调度算法(C-SCAN)
- LOOK调度算法
程序可以对给定的任意磁盘请求序列显示响应磁盘请求的过程,统计和报告不同算法情况下响应请求的顺序、移臂的总量,并比较几种算法在给定条件下的优劣。
3. 实验步骤
3.1 实验代码实现
dask.h
文件
#include <iostream>
#include <iomanip.h>
#include <malloc.h>
class DiskArm {
public:
DiskArm();
~DiskArm();
void InitSpace(char *MethodName);
void Report(void);
void Fcfs(void);
void Sstf(void);
void Scan(void);
void CScan(void);
void Look(void);
private:
int *Request;
int *Cylinder;
int RequestNumber;
int CurrentCylinder;
int SeekDirection;
int SeekNumber;
int SeekChang;
};
dask.cc
文件
#include "dask.h"
DiskArm::DiskArm() {
int i;
std::cout << "Please input Current cylinder: ";
std::cin >> CurrentCylinder;
std::cout << "Please input Current Direction (0/1): ";
std::cin >> SeekDirection;
std::cout << "Please input Request Numbers: ";
std::cin >> RequestNumber;
std::cout << "Please input Request cylinder string: ";
Request = new int[RequestNumber];
Cylinder = new int[RequestNumber];
for (i = 0; i < RequestNumber; i++) {
std::cin >> Request[i];
}
}
DiskArm::~DiskArm() {
delete[] Request;
delete[] Cylinder;
}
void DiskArm::InitSpace(char *MethodName) {
int i;
std::cout << std::endl << MethodName << std::endl;
SeekNumber = 0;
SeekChang = 0;
for (i = 0; i < RequestNumber; i++) {
Cylinder[i] = Request[i];
}
}
void DiskArm::Report(void) {
std::cout << std::endl;
std::cout << "Seek Number: " << SeekNumber << std::endl;
std::cout << "Change Direction: " << SeekChang << std::endl << std::endl;
}
void DiskArm::Fcfs(void) {
int Current = CurrentCylinder;
int Direction = SeekDirection;
InitSpace("FCFS");
std::cout << Current;
for (int i = 0; i < RequestNumber; i++) {
if (((Cylinder[i] >= Current) && !Direction) || ((Cylinder[i] < Current) && Direction)) {
SeekChang++;
Direction = !Direction;
std::cout << std::endl << Current << " -> " << Cylinder[i];
} else {
std::cout << " -> " << Cylinder[i];
}
SeekNumber += abs(Current - Cylinder[i]);
Current = Cylinder[i];
}
Report();
}
void DiskArm::Sstf(void) {
int Shortest;
int Distance = 999999;
int Direction = SeekDirection;
int Current = CurrentCylinder;
InitSpace("SSTF");
std::cout << Current;
for (int i = 0; i < RequestNumber; i++) {
for (int j = 0; j < RequestNumber; j++) {
if (Cylinder[j] == -1) continue;
if (Distance > abs(Current - Cylinder[j])) {
Distance = abs(Current - Cylinder[j]);
Shortest = j;
}
}
if (((Cylinder[Shortest] >= Current) && !Direction) || ((Cylinder[Shortest] < Current) && Direction)) {
SeekChang++;
Direction = !Direction;
std::cout << std::endl << Current << " -> " << Cylinder[Shortest];
} else {
std::cout << " -> " << Cylinder[Shortest];
}
SeekNumber += abs(Current - Cylinder[Shortest]);
Current = Cylinder[Shortest];
Distance = 999999;
Cylinder[Shortest] = -1;
}
Report();
}
void DiskArm::Scan(void) {
// Implement SCAN algorithm here
}
void DiskArm::CScan(void) {
// Implement C-SCAN algorithm here
}
void DiskArm::Look(void) {
// Implement LOOK algorithm here
}
int main(int argc, char *argv[]) {
DiskArm *dask = new DiskArm();
dask->Fcfs();
dask->Sstf();
}
Makefile
文件
head = dask.h
srcs = dask.cc
objs = dask.o
opts = -w -g -c
all: dask
dask: $(objs)
g++ $(objs) -o dask
dask.o: $(srcs) $(head)
g++ $(opts) $(srcs)
clean:
rm dask *.o
3.2 编译和执行程序
$ make
$ ./dask
Please input Current cylinder: 53
Please input Current Direction (0/1): 0
Please input Request Numbers: 8
Please input Request cylinder string: 98 183 37 122 14 124 65 67
4. 实验结果
4.1 测试磁盘请求序列及现象
对以下测试序列进行了测试:
- 98 183 37 122 14 124 65 67
- 86 147 91 177 94 150 102 60
- 123 87 34 56 150 98 62 180
观察了不同算法在这些序列下的执行情况,记录了每次寻道的顺序和总的寻道距离。
4.2 典型磁盘请求序列响应结果
对请求序列 98 183 37 122 14 124 65 67
的响应结果如下:
FCFS 算法
- 寻道顺序:53 -> 98 -> 183 -> 37 -> 122 -> 14 -> 124 -> 65 -> 67
- 总寻道数:640
- 调头次数:7
SSTF 算法
- 寻道顺序:53 -> 65 -> 67 -> 37 -> 14 -> 98 -> 122 -> 124 -> 183
- 总寻道数:236
- 调头次数:3
5.1 SCAN 算法
void DiskArm::Scan(void) {
int NextCylinder;
int Direction = SeekDirection; // 当前移动方向
int Current = CurrentCylinder; // 磁盘臂当前位置
int RequestCount = RequestNumber; // 待处理请求数
InitSpace("SCAN"); // 初始化用于报告的空间
cout << Current; // 报告当前位置
while (RequestCount > 0) { // 继续直到所有请求都被服务完
if (Direction) { // 向较高柱面号移动
NextCylinder = INT_MAX;
for (int j = 0; j < RequestNumber; j++) {
if (Cylinder[j] != -1 && Cylinder[j] >= Current && Cylinder[j] < NextCylinder) {
NextCylinder = Cylinder[j];
}
}
} else { // 向较低柱面号移动
NextCylinder = INT_MIN;
for (int j = 0; j < RequestNumber; j++) {
if (Cylinder[j] != -1 && Cylinder[j] <= Current && Cylinder[j] > NextCylinder) {
NextCylinder = Cylinder[j];
}
}
}
// 报告下一个待服务的柱面号
if (NextCylinder != Current) {
cout << " -> " << NextCylinder;
}
// 更新总寻道距离
SeekNumber += abs(Current - NextCylinder);
// 将磁盘臂移动到下一个柱面
Current = NextCylinder;
if (NextCylinder == INT_MAX || NextCylinder == INT_MIN) { // 当前方向没有请求
Direction = !Direction; // 反转方向
SeekChang++;
cout<<endl;
continue;
}
// 标记已服务的请求
for (int j = 0; j < RequestNumber; j++) {
if (Cylinder[j] == NextCylinder) {
Cylinder[j] = -1;
RequestCount--; // 减少待处理请求计数
break;
}
}
}
Report(); // 报告磁盘臂调度的摘要信息
}
5.2 C-SCAN 算法
void DiskArm::CScan(void) {
int NextCylinder;
int Direction = SeekDirection; // 当前移动方向
int Current = CurrentCylinder; // 磁盘臂当前位置
int RequestCount = RequestNumber; // 待处理请求数
InitSpace("CSCAN"); // 初始化用于报告的空间
cout << Current; // 报告当前位置
while (RequestCount > 0) { // 继续直到所有请求都被服务完
if (Direction) { // 向较高柱面号移动
NextCylinder = INT_MAX;
for (int j = 0; j < RequestNumber; j++) {
if (Cylinder[j] != -1 && Cylinder[j] >= Current && Cylinder[j] < NextCylinder) {
NextCylinder = Cylinder[j];
}
}
} else { // 向较低柱面号移动
NextCylinder = INT_MIN;
for (int j = 0; j < RequestNumber; j++) {
if (Cylinder[j] != -1 && Cylinder[j] <= Current && Cylinder[j] > NextCylinder) {
NextCylinder = Cylinder[j];
}
}
}
// 报告下一个待服务的柱面号
if (NextCylinder != Current) {
cout << " -> " << NextCylinder;
}
// 更新总寻道距离
SeekNumber += abs(Current - NextCylinder);
// 将磁盘臂移动到下一个柱面
Current = NextCylinder;
if (NextCylinder == INT_MAX || NextCylinder == INT_MIN) { // 当前方向没有请求
Direction = !Direction; // 反转方向
SeekChang++;
cout<<endl;
if(NextCylinder==INT_MAX){
Current=INT_MIN;
SeekNumber+=(INT_MAX-INT_MIN);
Direction = !Direction; // 反转方向
SeekChang++;
cout<<INT_MAX<<" -> "<<INT_MIN<<endl;
}
if(NextCylinder==INT_MIN){
Current=INT_MAX;
SeekNumber+=(INT_MAX-INT_MIN);
Direction = !Direction; // 反转方向
SeekChang++;
cout<<INT_MIN<<" -> "<<INT_MAX<<endl;
}
continue;
}
// 标记已服务的请求
for (int j = 0; j < RequestNumber; j++) {
if (Cylinder[j] == NextCylinder) {
Cylinder[j] = -1;
RequestCount--; // 减少待处理请求计数
break;
}
}
}
Report(); // 报告磁盘臂调度的摘要信息
}
5.3 LOOK 算法
void DiskArm::Look(void) {
int NextCylinder;
int Direction = SeekDirection; // 当前移动方向
int Current = CurrentCylinder; // 磁盘臂当前位置
int RequestCount = RequestNumber; // 待处理请求数
InitSpace("LOOK"); // 初始化用于报告的空间
cout << Current; // 报告当前位置
while (RequestCount > 0) { // 继续直到所有请求都被服务完
if (Direction) { // 向较高柱面号移动
NextCylinder = INT_MAX;
for (int j = 0; j < RequestNumber; j++) {
if (Cylinder[j] != -1 && Cylinder[j] >= Current && Cylinder[j] < NextCylinder) {
NextCylinder = Cylinder[j];
}
}
} else { // 向较低柱面号移动
NextCylinder = INT_MIN;
for (int j = 0; j < RequestNumber; j++) {
if (Cylinder[j] != -1 && Cylinder[j] <= Current && Cylinder[j] > NextCylinder) {
NextCylinder = Cylinder[j];
}
}
}
if (NextCylinder == INT_MAX || NextCylinder == INT_MIN) { // 当前方向没有请求
Direction = !Direction; // 反转方向
SeekChang++;
cout<<endl;
continue;
}
// 报告下一个待服务的柱面号
if (NextCylinder != Current) {
cout << " -> " << NextCylinder;
}
// 更新总寻道距离
SeekNumber += abs(Current - NextCylinder);
// 将磁盘臂移动到下一个柱面
Current = NextCylinder;
// 标记已服务的请求
for (int j = 0; j < RequestNumber; j++) {
if (Cylinder[j] == NextCylinder) {
Cylinder[j] = -1;
RequestCount--; // 减少待处理请求计数
break;
}
}
}
Report(); // 报告磁盘臂调度的摘要信息
}
实验结果
通过对请求序列 98 183 37 122 14 124 65 67 的测试,我们得到了以下结果:
FCFS 算法
- 寻道顺序:53 -> 98 -> 183 -> 37 -> 122 -> 14 -> 124 -> 65 -> 67
- 总寻道数:640
- 调头次数:7
SSTF 算法
- 寻道顺序:53 -> 65 -> 67 -> 37 -> 14 -> 98 -> 122 -> 124 -> 183
- 总寻道数:236
- 调头次数:3
SCAN 算法
- 寻道顺序:53 -> 37 -> 14 -> 0 -> 65 -> 67 -> 98 -> 122 -> 124 -> 183
- 总寻道数:236
- 调头次数:1
C-SCAN 算法
- 寻道顺序:53 -> 37 -> 14 -> 0 -> 199 -> 183 -> 124 -> 122 -> 98 -> 67 -> 65
- 总寻道数:386
- 调头次数:2
LOOK 算法
- 寻道顺序:53 -> 37 -> 14 -> 65 -> 67 -> 98 -> 122 -> 124 -> 183
- 总寻道数:208
- 调头次数:1
实验结果表格
算法 | 寻道顺序 | 总寻道数 | 调头次数 |
---|---|---|---|
FCFS | 53 -> 98 -> 183 -> 37 -> 122 -> 14 -> 124 -> 65 -> 67 | 640 | 7 |
SSTF | 53 -> 65 -> 67 -> 37 -> 14 -> 98 -> 122 -> 124 -> 183 | 236 | 3 |
SCAN | 53 -> 37 -> 14 -> 0 -> 65 -> 67 -> 98 -> 122 -> 124 -> 183 | 236 | 1 |
C-SCAN | 53 -> 37 -> 14 -> 0 -> 199 -> 183 -> 124 -> 122 -> 98 -> 67 -> 65 | 386 | 2 |
LOOK | 53 -> 37 -> 14 -> 65 -> 67 -> 98 -> 122 -> 124 -> 183 | 208 | 1 |
产生随机序列的测试
生成三组随机的磁盘柱面请求序列,并应用各算法中进行调度并绘制每组数据中各算法的寻道曲线图
import numpy as np
import matplotlib.pyplot as plt
# 随机生成磁盘柱面请求序列
np.random.seed(0) # 固定随机种子以便重现结果
request_sequences = [np.random.randint(0, 200, size=10) for _ in range(5)]
initial_positions = np.random.randint(0, 200, size=5)
seek_directions = np.random.choice([0, 1], size=5)
# 定义磁盘调度算法类
class DiskArm:
def __init__(self, requests, initial, direction):
self.requests = requests
self.initial = initial
self.direction = direction
self.reset()
def reset(self):
self.position = self.initial
self.remaining_requests = list(self.requests)
self.seek_sequence = [self.position]
self.seek_distance = 0
def report(self):
return self.seek_sequence, self.seek_distance
def fcfs(self):
self.reset()
for request in self.requests:
self.seek_distance += abs(self.position - request)
self.position = request
self.seek_sequence.append(self.position)
return self.report()
def sstf(self):
self.reset()
while self.remaining_requests:
closest_request = min(self.remaining_requests, key=lambda x: abs(self.position - x))
self.seek_distance += abs(self.position - closest_request)
self.position = closest_request
self.seek_sequence.append(self.position)
self.remaining_requests.remove(closest_request)
return self.report()
def scan(self):
self.reset()
direction = self.direction
while self.remaining_requests:
if direction == 1:
next_requests = [r for r in self.remaining_requests if r >= self.position]
if not next_requests:
direction = 0
continue
else:
next_requests = [r for r in self.remaining_requests if r <= self.position]
if not next_requests:
direction = 1
continue
closest_request = min(next_requests, key=lambda x: abs(self.position - x))
self.seek_distance += abs(self.position - closest_request)
self.position = closest_request
self.seek_sequence.append(self.position)
self.remaining_requests.remove(closest_request)
return self.report()
def cscan(self):
self.reset()
direction = self.direction
while self.remaining_requests:
if direction == 1:
next_requests = [r for r in self.remaining_requests if r >= self.position]
if not next_requests:
self.position = 0
self.seek_distance += 199
self.seek_sequence.append(self.position)
continue
else:
next_requests = [r for r in self.remaining_requests if r <= self.position]
if not next_requests:
self.position = 199
self.seek_distance += 199
self.seek_sequence.append(self.position)
continue
closest_request = min(next_requests, key=lambda x: abs(self.position - x))
self.seek_distance += abs(self.position - closest_request)
self.position = closest_request
self.seek_sequence.append(self.position)
self.remaining_requests.remove(closest_request)
return self.report()
def look(self):
self.reset()
direction = self.direction
while self.remaining_requests:
if direction == 1:
next_requests = [r for r in self.remaining_requests if r >= self.position]
if not next_requests:
direction = 0
continue
else:
next_requests = [r for r in self.remaining_requests if r <= self.position]
if not next_requests:
direction = 1
continue
closest_request = min(next_requests, key=lambda x: abs(self.position - x))
self.seek_distance += abs(self.position - closest_request)
self.position = closest_request
self.seek_sequence.append(self.position)
self.remaining_requests.remove(closest_request)
return self.report()
# 创建图表
fig, axs = plt.subplots(5, 5, figsize=(20, 20))
# 对每组数据应用所有算法并绘图
for i, (requests, initial, direction) in enumerate(zip(request_sequences, initial_positions, seek_directions)):
disk_arm = DiskArm(requests, initial, direction)
algorithms = {
'FCFS': disk_arm.fcfs,
'SSTF': disk_arm.sstf,
'SCAN': disk_arm.scan,
'C-SCAN': disk_arm.cscan,
'LOOK': disk_arm.look
}
results = {name: algo() for name, algo in algorithms.items()}
for j, (name, result) in enumerate(results.items()):
sequence, distance = result
axs[i, j].plot(sequence, range(len(sequence)), marker='o')
axs[i, j].set_title(f'{name} (Seek Distance: {distance})')
axs[i, j].set_ylabel('Step')
axs[i, j].set_xlabel('Cylinder')
plt.tight_layout()
plt.show()
实验结果:
对于模拟算法的解释
SCAN 算法
SCAN 算法会在一个方向上处理所有请求,直到到达磁盘的一端,然后反转方向继续处理剩余的请求。其主要步骤如下:
- 初始化:设定当前柱面号、寻道方向、请求列表等初始状态。
- 向当前方向移动:选择当前方向上距离最近的请求进行处理。
- 反转方向:当到达磁盘的一端且当前方向上没有更多的请求时,反转方向继续处理剩余的请求。
- 重复上述步骤,直到处理完所有请求。
C-SCAN 算法
C-SCAN 算法与 SCAN 类似,但不同的是 C-SCAN 只在一个方向上处理请求,当到达磁盘的一端后立即返回到起始位置,不反转方向。其主要步骤如下:
- 初始化:设定当前柱面号、寻道方向、请求列表等初始状态。
- 向当前方向移动:选择当前方向上距离最近的请求进行处理。
- 返回起始位置:当到达磁盘的一端且当前方向上没有更多的请求时,立即返回起始位置,继续处理请求。
- 重复上述步骤,直到处理完所有请求。
LOOK 算法
LOOK 算法与 SCAN 类似,但 LOOK 只在有请求的范围内移动,当到达请求的边界时反转方向。其主要步骤如下:
- 初始化:设定当前柱面号、寻道方向、请求列表等初始状态。
- 向当前方向移动:选择当前方向上距离最近的请求进行处理。
- 反转方向:当到达当前方向上的请求边界时,反转方向继续处理剩余的请求。
- 重复上述步骤,直到处理完所有请求。
各种算法适应的磁盘柱面寻道请求情况分析
先来先服务 (FCFS)
- 特点:按照请求到达的顺序依次处理请求。
- 适用场景:适用于请求分布较为均匀且对寻道时间要求不高的场景。FCFS 的优点是简单易实现,但缺点是寻道时间可能较长,特别是请求密集分布在磁盘的不同区域时。
最短寻道时间优先 (SSTF)
- 特点:每次选择距离当前柱面最近的请求进行处理。
- 适用场景:适用于请求较为密集且希望最小化寻道时间的场景。SSTF 可以有效减少寻道时间,但可能导致某些请求长期得不到处理,产生饥饿现象。
SCAN
- 特点:在一个方向上处理所有请求,直到到达磁盘的一端,然后反转方向。
- 适用场景:适用于请求分布在磁盘两端且需要避免饥饿现象的场景。SCAN 可以保证所有请求在一定时间内得到处理,但寻道时间可能较长。
C-SCAN
- 特点:只在一个方向上处理请求,当到达磁盘的一端后立即返回起始位置继续处理请求。
- 适用场景:适用于请求密集分布且希望避免饥饿现象并保证响应时间一致的场景。C-SCAN 可以保证所有请求得到公平处理,但可能增加一些额外的寻道时间。
LOOK
- 特点:只在有请求的范围内移动,当到达请求的边界时反转方向。
- 适用场景:适用于请求较为集中且希望最小化寻道时间的场景。LOOK 可以减少不必要的寻道,但可能在请求分布不均匀时表现不佳。
在本实验的测试条件下,SSTF算法表现最佳,适合请求序列较为集中的情况;SCAN和LOOK算法适合请求序列较为分散的情况,而C-SCAN算法适合均匀分布的请求序列。FCFS算法虽然简单,但在实际应用中不如其他算法高效。
盘的不同区域时。
最短寻道时间优先 (SSTF)
- 特点:每次选择距离当前柱面最近的请求进行处理。
- 适用场景:适用于请求较为密集且希望最小化寻道时间的场景。SSTF 可以有效减少寻道时间,但可能导致某些请求长期得不到处理,产生饥饿现象。
SCAN
- 特点:在一个方向上处理所有请求,直到到达磁盘的一端,然后反转方向。
- 适用场景:适用于请求分布在磁盘两端且需要避免饥饿现象的场景。SCAN 可以保证所有请求在一定时间内得到处理,但寻道时间可能较长。
C-SCAN
- 特点:只在一个方向上处理请求,当到达磁盘的一端后立即返回起始位置继续处理请求。
- 适用场景:适用于请求密集分布且希望避免饥饿现象并保证响应时间一致的场景。C-SCAN 可以保证所有请求得到公平处理,但可能增加一些额外的寻道时间。
LOOK
- 特点:只在有请求的范围内移动,当到达请求的边界时反转方向。
- 适用场景:适用于请求较为集中且希望最小化寻道时间的场景。LOOK 可以减少不必要的寻道,但可能在请求分布不均匀时表现不佳。
在本实验的测试条件下,SSTF算法表现最佳,适合请求序列较为集中的情况;SCAN和LOOK算法适合请求序列较为分散的情况,而C-SCAN算法适合均匀分布的请求序列。FCFS算法虽然简单,但在实际应用中不如其他算法高效。