一、系统描述
1、磁盘调度系统概述
磁盘调度是操作系统中对磁盘进行访问控制的一种策略,主要用于决定哪个进程将优先获得磁盘访问权限。在多个进程并发访问磁盘时,磁盘调度策略的好坏直接影响到系统的整体性能和响应时间。磁盘调度算法的主要目标是减少磁盘的平均访问时间,提高磁盘的吞吐量和I/O效率。
2、磁盘调度算法简述:
磁盘调度算法有多种,每种算法都有其特点和适用场景。常见的磁盘调度算法包括:先来先服务(FCFS)算法、最短寻道时间优先(SSTF)算法、扫描(SCAN)算法和循环扫描(CSCAN)算法等。
对于不同的磁盘调度算法,其性能可以从多个方面进行评估,包括平均寻道时间、最大寻道时间、寻道次数、磁头移动距离等。在应用中,需要根据系统的具体需求和磁盘的工作负载来选择合适的算法。
在实际应用中,磁盘调度算法通常需要根据系统的实际情况进行选择和调整。例如,在需要大量读/写操作的系统中,可能需要采用更高效的算法来减少寻道时间。此外,还可以通过一些优化手段来提高磁盘的访问效率,如采用缓存技术、预测技术、磁盘分区和条带化等。
二、磁盘调度算法解析
1、磁盘调度算法原理:
磁盘调度算法的原理主要是根据特定的规则和策略来决定磁盘访问的顺序,以优化磁盘访问的性能。这些算法旨在减少磁头的移动距离和延迟,提高磁盘的吞吐量和响应速度。
磁盘调度算法的核心思想是权衡磁头的移动和等待时间,以找到最优的访问顺序。不同的算法采用不同的策略来实现这一目标。例如,先来先服务(FCFS)算法按照请求到达的先后顺序进行调度,简单直观但可能不是最优的。最短寻道时间优先(SSTF)算法则选择距离当前磁头位置最近的请求进行处理,以减少磁头的移动距离。扫描(SCAN)算法和循环扫描(CSCAN)算法则通过规定磁头的移动方向来平衡磁头的移动和等待时间。
在实现磁盘调度算法时,操作系统会维护一个请求队列,用于存储待处理的磁盘访问请求。调度算法根据当前磁头的位置和请求队列中的请求信息,计算出最佳的访问顺序,并控制磁头的移动来依次处理这些请求。
1)先来先服务算法(FCFS):
先来先服务(First Come First Serve,FCFS)算法是最简单的磁盘调度算法。它按照请求到达的先后顺序进行服务,不考虑请求访问的物理位置。FCFS算法易于实现,但可能产生大量的寻道延迟,尤其是在请求混合长短时,因为请求可能来自磁盘的任意位置。
2)最短寻道时间优先算法(SSTF):
最短寻道时间优先(Shortest Seek Time First,SSTF)算法是一种动态的优先调度算法。它总是选择当前磁头位置到下一个请求位置距离最短的请求进行服务。SSTF算法可以大大减少平均寻道时间,简单的先短后长的策略,每次选择当前最短的请求进行服务。然而,这种算法可能导致某些长请求长时间等待,因为总是优先处理最短的请求,产生“饥饿”现象,即某些请求长时间得不到服务。
3)电梯调度(扫描)算法(SCAN):
扫描(SCAN)算法是对SSTF算法的一种改进,SCAN算法采用圆周扫描的方式,依次访问磁盘上的扇区,当遇到新的请求时,会在下一个空闲的扇区开始。它避免了SSTF的“饥饿”问题。SCAN算法规定磁头在一个方向上移动,直到到达该方向上的最后一个请求为止,然后快速返回到另一端,并在另一个方向上处理请求,如此往复,这能减少寻道次数,但可能会导致部分请求的等待时间增加。
4)电梯调度(循环扫描)算法2(C-SCAN):
循环扫描(Circular SCAN,CSCAN)算法是SCAN算法的变种。与SCAN算法不同的是,CSCAN算法要求磁头在处理完一个方向的所有请求后,必须返回到磁盘的另一端,而不是直接反向移动。这种策略有利于磁头的均匀磨损,提高了磁盘的寿命。
三、程序结构
1、执行目标:
模拟磁盘调度算法系统的目的是为了优化和管理计算机中磁盘I/O(输入输出)操作的效率。磁盘调度在计算机系统中扮演着关键角色,因为磁盘的访问速度通常远低于内存和CPU。当多个进程或线程同时请求数据时,如何高效地组织这些请求,避免不必要的磁头移动,降低寻道时间和减少等待时间,是系统性能的关键。
2、期望结果:
1)提高性能:减少磁盘访问延迟,提高整体系统响应速度。
2)公平性:确保多个请求公平地分享磁盘资源,避免一个进程长时间占用磁盘导致其他进程饿死。
3)效率优化:通过各种算法,如FCFS(先来先服务)、 SSTF(最短寻道时间优先)、 SCAN(电梯调度)等,选择最优的磁头移动路径。
4)预测和模拟:在真实环境中难以直接测试所有可能的情况,模拟环境可以帮助开发者理解和优化算法的行为。
5)教育和研究:对于操作系统、计算机体系结构和并发控制等领域,磁盘调度算法是教学和研究的重要内容。
3、运算方式:
1)FCFS:先来先服务
for (int track : requests)
{
cout << "移动到轨道: " << track << endl;
totalMovement += abs(head - track);
head = track;// 更新磁头位置
}
2)SSTF:最短寻道时间优先
while (!remainingRequests.empty()) // 当剩余请求不为空时循环
{
auto nearestTrack = min_element// 找到最近的磁道
(remainingRequests.begin(), remainingRequests.end(),
[head](int a, int b)
{
return abs(a - head) < abs(b - head); // Lambda函数,用于找到最近的磁道
}
);
cout << "移动到轨道:" << *nearestTrack << endl;// 输出磁头移动到的磁道
totalMovement += abs(*nearestTrack - head);// 计算并更新总移动距离
head = *nearestTrack;// 更新磁头位置
remainingRequests.erase(nearestTrack);// 从列表中删除已处理的请求
}
3)SCAN:电梯调度(扫描)
// 将磁道请求分成两个部分:位于磁头之前和之后的
for (int track : requests) {
if (track < head)
lowerRequests.push_back(track);// 较低的轨道请求
else
upperRequests.push_back(track);// 较高的轨道请求
}
// 对两部分进行排序
sort(lowerRequests.begin(), lowerRequests.end());
sort(upperRequests.begin(), upperRequests.end(), greater<int>());
int totalMovement = 0;
// 先扫描较高的轨道请求,再扫描较低的轨道请求
for (const auto& track : {upperRequests, lowerRequests}) {
// 遍历每个磁道请求,在磁头移动到目标位置的过程中计算总运动距离
for (int t : track) {
cout << "移动到轨道:" << t << endl;
totalMovement += abs(head - t);
head = t;// 更新磁头位置
}
}
4)C-SCAN:电梯调度(循环扫描)
for (int track : requests) {
if (track < head)
lowerRequests.push_back(track);// 较低的轨道请求
else
upperRequests.push_back(track);// 较高的轨道请求
}
// 对两部分进行排序
sort(lowerRequests.begin(), lowerRequests.end());
sort(upperRequests.begin(), upperRequests.end());
int totalMovement = 0;
// 先扫描较高的轨道请求,再扫描较低的轨道请求
for (const auto& track : { upperRequests, lowerRequests }) {
// 遍历每个磁道请求,在磁头移动到目标位置的过程中计算总运动距离
for (int t : track) {
cout << "移动到轨道:" << t << endl;
totalMovement += abs(head - t);
head = t;// 更新磁头位置
}
// 如果当前轨道请求不为空,则将磁头移动到0轨
if (!track.empty()) {
cout << "移动到轨道:0" << endl; // 移动到盘片的最大位置,即0轨
totalMovement += abs(head - 0);
head = 0;// 更新磁头位置
}
}
四、调度算法代码实现
1)先来先服务算法(FCFS):class FCFS
class FCFS : public DiskScheduler
{
public:
//执行FCFS调度算法
void schedule(const vector<int>& requests, int head) override
{
cout << "正在执行FCFS调度......" << endl;
int totalMovement = 0;
// 遍历每个磁道请求,按请求顺序移动磁头,并计算总运动距离
for (int track : requests)
{
cout << "移动到轨道: " << track << endl;
totalMovement += abs(head - track);
head = track;// 更新磁头位置
}
// 输出总的磁头移动距离
cout << "磁头总运动距离为: " << totalMovement << endl;
}
};
2)最短寻道时间优先算法(SSTF):class SSTF
class SSTF : public DiskScheduler
{
public:
//执行SSTF调度算法
void schedule(const vector<int>& requests, int head) override
{
cout << "正在执行SSTF调度......" << endl; // 显示正在执行SSTF调度的消息
vector<int> remainingRequests = requests;// 复制请求列表到remainingRequests向量中
int totalMovement = 0;// 初始化总移动距离变量为0
while (!remainingRequests.empty()) // 当剩余请求不为空时循环
{
auto nearestTrack = min_element// 找到最近的磁道
(remainingRequests.begin(), remainingRequests.end(),
[head](int a, int b)
{
return abs(a - head) < abs(b - head); // Lambda函数,用于找到最近的磁道
}
);
cout << "移动到轨道:" << *nearestTrack << endl;// 输出磁头移动到的磁道
totalMovement += abs(*nearestTrack - head);// 计算并更新总移动距离
head = *nearestTrack;// 更新磁头位置
remainingRequests.erase(nearestTrack);// 从列表中删除已处理的请求
}
// 输出总的磁头移动距离
cout << "磁头总运动距离为: " << totalMovement << endl;
}
};
3)电梯调度算法(SCAN):class SCAN
class SCAN : public DiskScheduler {
public:
//执行SCAN调度算法
void schedule(const vector<int>& requests, int head) override {
cout << "正在执行SCAN调度......" << endl;
vector<int> lowerRequests, upperRequests;
// 将磁道请求分成两个部分:位于磁头之前和之后的
for (int track : requests) {
if (track < head)
lowerRequests.push_back(track);// 较低的轨道请求
else
upperRequests.push_back(track);// 较高的轨道请求
}
// 对两部分进行排序
sort(lowerRequests.begin(), lowerRequests.end());
sort(upperRequests.begin(), upperRequests.end(), greater<int>());
int totalMovement = 0;
// 先扫描较高的轨道请求,再扫描较低的轨道请求
for (const auto& track : {upperRequests, lowerRequests}) {
// 遍历每个磁道请求,在磁头移动到目标位置的过程中计算总运动距离
for (int t : track) {
cout << "移动到轨道:" << t << endl;
totalMovement += abs(head - t);
head = t;// 更新磁头位置
}
}
// 输出总的磁头移动距离
cout << "总运动距离为: " << totalMovement << endl;
}
};
4)电梯调度算法2(C-SCAN):class C-SCAN
class C_SCAN : public DiskScheduler {
public:
//执行C - SCAN调度算法
void schedule(const vector<int>& requests, int head) override {
cout << "正在执行C-SCAN调度......" << endl;
vector<int> lowerRequests, upperRequests;
// 将磁道请求分成两个部分:位于磁头之前和之后的
for (int track : requests) {
if (track < head)
lowerRequests.push_back(track);// 较低的轨道请求
else
upperRequests.push_back(track);// 较高的轨道请求
}
// 对两部分进行排序
sort(lowerRequests.begin(), lowerRequests.end());
sort(upperRequests.begin(), upperRequests.end());
int totalMovement = 0;
// 先扫描较高的轨道请求,再扫描较低的轨道请求
for (const auto& track : { upperRequests, lowerRequests }) {
// 遍历每个磁道请求,在磁头移动到目标位置的过程中计算总运动距离
for (int t : track) {
cout << "移动到轨道:" << t << endl;
totalMovement += abs(head - t);
head = t;// 更新磁头位置
}
// 如果当前轨道请求不为空,则将磁头移动到0轨
if (!track.empty()) {
cout << "移动到轨道:0" << endl; // 移动到盘片的最大位置,即0轨
totalMovement += abs(head - 0);
head = 0;// 更新磁头位置
}
}
// 输出总的磁头移动距离
cout << "总运动距离为: " << totalMovement << endl;
}
};
五、模板数据运行结果
模板数据:
定义一组作业的磁道请求,输出为按照选择的算法执行时的磁头移动轨迹;
//模板作业磁道请求顺序
vector<int> requests = { 98, 183, 37, 122, 14, 124, 65, 67 };
......
//定义磁头读取初始位置为53:
int beginPosition = 53;
//依次执行四个不同的调度算法
FCFS fcfs;
fcfs.schedule(requests, beginPosition);
SSTF...
.
...
.
return 0;
六、不同数据测试结果
//测试系统案例作业请求(测试1.0:6月8日11:00)
// 按升序排列的请求
vector<int> requests = { 1, 7, 14, 37, 65, 98, 122, 124 };
//测试结果
FCFS:175 SSTF:199 SCAN:230 C-SCAN:269
vector<int> requests = { 4, 18, 55, 67, 122, 144, 183, 201 };
//测试结果
FCFS:246 SSTF:274 SCAN:359 C-SCAN:385
// 按降序排列的请求
vector<int> requests = { 201, 183, 153, 124, 122, 99, 78, 67 };
//测试结果
FCFS:282 SSTF:148 SCAN:282 C-SCAN:349
vector<int> requests = { 24, 45, 55, 66, 88, 98, 131, 177 };
//测试结果
FCFS:182 SSTF:186 SCAN:298 C-SCAN:391
// 随机请求
vector<int> requests = { 45, 102, 78, 201, 55, 131, 88, 99 };
//测试结果
FCFS:488 SSTF:168 SCAN:304 C-SCAN:439
vector<int> requests = { 66, 24, 153, 111, 177, 33, 198, 44 };
//测试结果
FCFS:755 SSTF:203 SCAN:339 C-SCAN:431
七、实验总结
磁道请求(Track Request)通常在计算机存储系统,特别是硬盘驱动器中,指的是读写操作过程中,硬盘控制器向磁盘表面的特定区域发出的请求。在机械硬盘中,数据是存储在一系列同心圆状的磁道上的,每个磁道又分为多个扇区。当用户程序需要访问数据时,会将磁盘地址转换成磁道和扇区的组合,形成磁道请求。
一组作业的磁道请求可能包括以下几个方面:
顺序请求:连续的数据请求倾向于在同一磁道上,这有利于提高磁头寻道速度,因为磁头不需要频繁地移动。
随机请求:如果数据分散在不同位置,磁头需要频繁地在磁盘上移动来响应请求,这可能导致寻道延迟和性能下降。
预读/滞后写:为了减少寻道时间,系统可能会同时发送后续请求(预读)或延迟写入请求,这样可以在磁头完成当前操作后继续下一个。
缓存策略:操作系统和硬件缓存机制会影响磁道请求的顺序和频率,例如使用读写缓存来减少对磁盘的直接访问。
错误重试和校验:磁道请求可能包含错误检测和重试机制,确保数据的正确性。
I/O调度算法:硬盘驱动器通常采用某种I/O调度算法(如最短寻道时间优先、电梯调度等),来优化磁道请求的执行顺序。
代码革新:
当我们使用for循环的范围-based形式时,语句for (const auto& variable : collection) 中的各部分含义如下:
const: 表示在遍历过程中,迭代的元素是不可变的。也就是说,在循环内部不会修改被遍历的元素。
auto: 是C++11中引入的关键字,用于自动类型推导。在这种情况下,编译器会自动推导集合中元素的类型,而无需显式指定它们。例如,如果{upperRequests, lowerRequests}的元素类型是vector<int>,那么auto将会推导为vector<int>。
&:表示对集合中的元素进行引用遍历,而非创建元素的拷贝。这样可以提高效率,并且允许在循环内部修改被遍历的元素。
因此,for (const auto& track : {upperRequests, lowerRequests}) 表示对{upperRequests, lowerRequests}中的每个元素进行不可变的引用遍历。总之,该语句表示在遍历{upperRequests, lowerRequests}的过程中,不可变地引用其中的元素,并且编译器将自动推导集合中元素的类型。