操作系统磁盘调度在C++中的实现

122 阅读4分钟

操作系统磁盘调度在C++中的实现

磁盘调度代码简介

磁盘是高速、大容量、旋转型、可直接存取的存储设备。它作为计算机系统的辅助存储器,担负着繁重的输入输出工作,在现代计算机系统中往往同时会有若干个要求访问磁盘的输入输出要求。系统可采用一种策略,尽可能按最佳次序执行访问磁盘的请求。由于磁盘访问时间主要受寻道时间 T 的影响,为此需要采用合适的寻道算法,以降低寻道时间。本代码模拟设计一个磁盘调度程序,观察调度程序的动态运行过程。

代码实现

头文件及宏定义

#include<iostream>
#include<map>
#include<time.h>
#include<string>
#include<cmath>
using namespace std;
#define random(x) rand()%(x)

由于要使用字符串定义来增强用户使用的可观方便性,输入字符串(比如:FIFO、LRU)会比输入数字(比如:1、2、3)更容易理解和操作,所以引入头文件string。

我们还需使用map来构建数据结构,因为map的一一对应的优越性,不但可以保存指令号还可以保存在指令流中的序号,大大增强代码的内聚性,所以这里我们引入map头文件。

另外,我们需要用到随机数给出每个指令构建指令流,所以添加宏定义,方便后续代码随机数的使用,也提高代码的可读性。

同时需要插入头文件time.h作为随机数种子,让随机数真正随机。 最后加入cmath,因为需要计算磁道之间的距离,需要用到绝对值abs()。

磁道请求流生成模块

srand(time(NULL));
	map<int, int>temp;
	map<int, int>diskstream;
	int count = 1;
	while (true) {
		int m = random(198) + 1;
		temp.insert(pair<int, int>(m + 1, count));
		if (temp.find(m + 1)->second == count) { count++; }
		if (temp.size() == 20) { break; }
		int m1 = random(m + 1) + 1;
		temp.insert(pair<int, int>(m1, count));
		if (temp.find(m1)->second == count) { count++; }
		if (temp.size() == 20) { break; }
		temp.insert(pair<int, int>(m1 + 1, count));
		if (temp.find(m1 + 1)->second == count) { count++; }
		if (temp.size() == 20) { break; }
		int m2 = random(200 - (m1 + 2) + 1) + m1 + 2;
		temp.insert(pair<int, int>(m2, count));
		if (temp.find(m2)->second == count) { count++; }
		if (temp.size() == 20) { break; }
	}
	for (auto t = temp.begin(); t != temp.end(); t++) {
		diskstream.insert(pair<int, int>(t->second, t->first));
	}
	cout << "磁盘访问请求顺序如下:" << endl;
	cout << "*****************************************************************************" << endl;
	for (auto disk = diskstream.begin(); disk != diskstream.end(); disk++) {
		cout << disk->second << " ";
	}
	cout << endl;
	cout << "*****************************************************************************" << endl;

这里我们首先在代码的最开始定义随机数种子,让随机数完全随机。

再定义一个map数据结构来存储磁盘访问请求,名为diskstream(磁道流)和一个名为temp的中间存储容器,这个容器将访问号设为key值,利用map自动去重,这样就不会出现相同的磁道访问,更能模拟实际情况。

然后定义一个计数器,用于对请求计数,可以在map里逐个给请求顺序标号。

这里我们的总磁道数就是200,生成访问磁道的随机数约束可参考我的这一篇页面置换算法里的相似的约束条件。在前区域生成25%,在后区域生成25%,顺序生成50%。

这里判断生成20个循环为什么不直接写在while()里而是在每次插入后都判断一次?是因为while一次循环就会插入4个元素,而如果有相同磁道的就不会插入,我们无法预知哪一个会不会插入,所以每次循环插入的元素数量实际上都是随机的,所以我们需要在每次插入后都判断一下是否到20个了,而不能在循环条件内判断。

另外我们在每次插入算法后用一个语句判断是否插入成功,如果成功计数器自增,插入失败并不能自增,生成的序列号才是1-20,而不是一堆乱数字。

最后我们将中间存储容器temp的key值都传到磁道流(diskstream)的value里,对应的value传到key里,形成了我们最终需要的随机不重复磁道请求流。

选择模块

int now = random(199) + 1;
	cout << "磁头当前所在磁道位置是:" << now << endl;
	string choose;
	while (true) {
	    int store = now;
		cout << "请选择您需要的算法(FCFS、SSTF、SCAN、CSCAN):" << endl;
		cout << "或输入S或s停止程序~" << endl;
		cin >> choose;
		double length = 0;
		if (choose == "FCFS" || "fcfs") {

		}
		else if (choose == "SSTF" || "sstf") {

		}
		else if (choose == "SCAN" || "scan") {

		}
		else if (choose == "CSCAN" || "cscan") {

		}
		else if (choose == "S" || "s") { break; }
		else {
			cout << "您的输入有误!请输入FCFS、SSTF、SCAN、CSCAN当中的一个!" << endl;
		}
	}

对于选择模块需要注意的就是界面的可观性强,以及要考虑到用户输入错误以及大小写问题,我们需要提升代码的鲁棒性。

我们生成出当前磁头所在磁道位置(1~200),对于电梯算法我们默认是先从小到大。

再定义一个浮点数length用于存储平均寻道长度。

另外,在循环的最开始,每次选择算法前,先定义一个整型store用于存储一开始磁头的位置,因为在算法运行中磁头位置会变化,为了控制一开始磁头位置不变来比较各种算法的优劣,我们需要在每个算法结束时将磁头位置再重置为store的值。

先来先服务(FCFS)算法模块

if (choose == "FCFS" || choose == "fcfs") {
			cout << "FCFS算法磁头访问磁道的顺序:" << endl;
			cout << "*****************************************************************************" << endl;
			for (auto disk = diskstream.begin(); disk != diskstream.end(); disk++) {
				length += abs(now - disk->second);
				now = disk->second;
				cout << disk->second << " ";
			}
			cout << endl;
			cout << "*****************************************************************************" << endl;
			length /= 20;
			cout << "FCFS算法平均寻道长度=" << length << endl;
			now = store;
		}

FCFS算法是先来的请求先访问,刚好符合我们按照序号的map定义,直接用一个迭代器按照磁道流顺序访问,最后计算输出一下平均寻道长度即可。

最短寻道时间优先(SSTF)算法模块

else if (choose == "SSTF" || choose == "sstf") {
			map<int, int>diskstream2;
			for (auto t = temp.begin(); t != temp.end(); t++) {
				diskstream2.insert(pair<int, int>(t->second, t->first));
			}
			int distant;
			map<int, int>visit;
			for (int i = 1; i <= 20; i++) {
				multimap<int, int>least;
				for (auto disk = diskstream2.begin(); disk != diskstream2.end(); disk++) {
					distant = abs(now - disk->second);
					least.insert(pair<int, int>(distant, disk->second));
				}
				visit.insert(pair<int, int>(i, least.begin()->second));
				now = least.begin()->second;
				for (auto disk = diskstream2.begin(); disk != diskstream2.end();) {
					if (disk->second == least.begin()->second) {
						diskstream2.erase(disk++);
					}
					else { disk++; }
				}
			}
			now = store;
			cout << "SSTF算法磁头访问磁道的顺序:" << endl;
			cout << "*****************************************************************************" << endl;
			for (auto v = visit.begin(); v != visit.end(); v++) {
				length += abs(now - v->second);
				now = v->second;
				cout << v->second << " ";
			}
			cout << endl;
			cout << "*****************************************************************************" << endl;
			length /= 20;
			cout << "SSTF算法平均寻道长度=" << length << endl;
			now = store;
		}

在最短寻道时间优先算法中,我们需要定义一个新的map存储磁道请求流,因为后面要对请求流进行删除操作,所以不能对后续还需使用的diskstream直接删除,就创建一个新的完全一样的diskstream2。

定义整型变量distant用于存放磁头在每个位置时对于每个未访问磁道的距离。

再定义一个名为visit的map用于存放SSTF算法磁头访问磁道的顺序。

用一个20次的循环嵌套一个磁道请求流(diskstream2)的迭代器,在外层循环的最开始定义一个名为length的map用于存放distant,以此筛选出每次的最短距离,继而将这个请求存入visit中,并在diskstream2中删除这个请求。

注意,删除时因为使用了迭代器,不能直接删除,需要用一个条件判断,在自增前将值传给erase函数,并自增,或者直接自增。(这里使用goto也可以,不过循环嵌套很少,且判断只有一个,所以没有必要使用

最后,像FCFS算法类似的模块,用一个迭代器按照visit磁道流顺序访问,最后计算输出一下平均寻道长度即可。

扫描(SCAN/电梯)算法模块

else if (choose == "SCAN" || choose == "scan") {
			map<int, int>diskstream2;
			for (auto disk = diskstream.begin(); disk != diskstream.end(); disk++) {
				diskstream2.insert(pair<int, int>(disk->second, disk->first));
			}
			cout << "SCAN算法磁头访问磁道的顺序:" << endl;
			cout << "*****************************************************************************" << endl;
			for (auto disk = diskstream2.begin(); disk != diskstream2.end(); disk++) {
				if (disk->first >= now) {
					length += abs(now - disk->first);
					now = disk->first;
					cout << disk->first << " ";
				}
			}
			length += abs(now - store);
			now = store;
			for (auto disk = diskstream2.rbegin(); disk != diskstream2.rend(); disk++) {
				if (disk->first < now) {
					length += abs(now - disk->first);
					now = disk->first;
					cout << disk->first << " ";
				}
			}
			cout << endl;
			cout << "*****************************************************************************" << endl;
			length /= 20;
			cout << "SCAN算法平均寻道长度=" << length << endl;
			now = store;
		}

由于扫描/电梯算法需要根据磁道顺序来访问,所以我们定义新的map:diskstream2来将diskstream中的key值存为value值,value值存为key值,这样就完成了按照磁道号的自动排序。

由于我们一开始就定义过,电梯算法时,磁头始终先从低磁道到高磁道,所以用正向迭代器判断,如果磁道在当前位置的后面,就按次序访问,直到最后一个。然后用逆向迭代器判断,如果磁道在初始磁头位置的前面,就按逆序访问,直到第一个。

这里有个细节,就是需要在正逆两个迭代器之间将位置初始化到初始位置,并且增加相应的寻道长度,否则磁头位置已经变化,用逆向迭代器输出时会又将整个磁道流又输出一遍。(当然这个地方也可以用接下来循环扫描使用的方法:每访问一个就删除一个

循环扫描(CSCAN)算法模块

else if (choose == "CSCAN" || choose == "cscan") {
			map<int, int>diskstream3;
			for (auto disk = diskstream.begin(); disk != diskstream.end(); disk++) {
				diskstream3.insert(pair<int, int>(disk->second, disk->first));
			}
			cout << "CSCAN算法磁头访问磁道的顺序:" << endl;
			cout << "*****************************************************************************" << endl;
			for (auto disk = diskstream3.begin(); disk != diskstream3.end();) {
				if (disk->first >= now) {
					length += abs(now - disk->first);
					now = disk->first;
					cout << disk->first << " ";
					diskstream3.erase(disk++);
				}
				else { disk++; }
			}
			length += abs(now - store);
			now = store;
			for (auto disk = diskstream3.begin(); disk != diskstream3.end(); disk++) {
					length += abs(now - disk->first);
					now = disk->first;
					cout << disk->first << " ";
			}
			cout << endl;
			cout << "*****************************************************************************" << endl;
			length /= 20;
			cout << "CSCAN算法平均寻道长度=" << length << endl;
			now = store;
		}

循环扫描算法和扫描算法实现很类似,因为循环扫描算法始终是正序的,所以不能使用逆向迭代器,只能采用访问一个删除一个的算法。

源代码及运行过程图片

#include<iostream>
#include<map>
#include<time.h>
#include<string>
#include<cmath>
using namespace std;
#define random(x) rand()%(x)
int main() {
	srand(time(NULL));
	map<int, int>temp;
	map<int, int>diskstream;
	int count = 1;
	while (true) {
		int m = random(198) + 1;
		temp.insert(pair<int, int>(m + 1, count));
		if (temp.find(m + 1)->second == count) { count++; }
		if (temp.size() == 20) { break; }
		int m1 = random(m + 1) + 1;
		temp.insert(pair<int, int>(m1, count));
		if (temp.find(m1)->second == count) { count++; }
		if (temp.size() == 20) { break; }
		temp.insert(pair<int, int>(m1 + 1, count));
		if (temp.find(m1 + 1)->second == count) { count++; }
		if (temp.size() == 20) { break; }
		int m2 = random(200 - (m1 + 2) + 1) + m1 + 2;
		temp.insert(pair<int, int>(m2, count));
		if (temp.find(m2)->second == count) { count++; }
		if (temp.size() == 20) { break; }
	}
	for (auto t = temp.begin(); t != temp.end(); t++) {
		diskstream.insert(pair<int, int>(t->second, t->first));
	}
	cout << "*************************************************" << endl;
	cout << "欢迎使用本程序~" << endl;
	cout << "磁盘访问请求顺序如下:" << endl;
	cout << "*****************************************************************************" << endl;
	for (auto disk = diskstream.begin(); disk != diskstream.end(); disk++) {
		cout << disk->second << " ";
	}
	cout << endl;
	cout << "*****************************************************************************" << endl;
	int now = random(199) + 1;
	cout << "磁头当前所在磁道位置是:" << now << endl;
	string choose;
	while (true) {
		int store = now;
		cout << "请选择您需要的算法(FCFS、SSTF、SCAN、CSCAN):" << endl;
		cout << "或输入S或s停止程序~" << endl;
		cin >> choose;
		double length = 0;
		if (choose == "FCFS" || choose == "fcfs") {
			cout << "FCFS算法磁头访问磁道的顺序:" << endl;
			cout << "*****************************************************************************" << endl;
			for (auto disk = diskstream.begin(); disk != diskstream.end(); disk++) {
				length += abs(now - disk->second);
				now = disk->second;
				cout << disk->second << " ";
			}
			cout << endl;
			cout << "*****************************************************************************" << endl;
			length /= 20;
			cout << "FCFS算法平均寻道长度=" << length << endl;
			now = store;
		}
		else if (choose == "SSTF" || choose == "sstf") {
			map<int, int>diskstream2;
			for (auto t = temp.begin(); t != temp.end(); t++) {
				diskstream2.insert(pair<int, int>(t->second, t->first));
			}
			int distant;
			map<int, int>visit;
			for (int i = 1; i <= 20; i++) {
				multimap<int, int>least;
				for (auto disk = diskstream2.begin(); disk != diskstream2.end(); disk++) {
					distant = abs(now - disk->second);
					least.insert(pair<int, int>(distant, disk->second));
				}
				visit.insert(pair<int, int>(i, least.begin()->second));
				now = least.begin()->second;
				for (auto disk = diskstream2.begin(); disk != diskstream2.end();) {
					if (disk->second == least.begin()->second) {
						diskstream2.erase(disk++);
					}
					else { disk++; }
				}
			}
			now = store;
			cout << "SSTF算法磁头访问磁道的顺序:" << endl;
			cout << "*****************************************************************************" << endl;
			for (auto v = visit.begin(); v != visit.end(); v++) {
				length += abs(now - v->second);
				now = v->second;
				cout << v->second << " ";
			}
			cout << endl;
			cout << "*****************************************************************************" << endl;
			length /= 20;
			cout << "SSTF算法平均寻道长度=" << length << endl;
			now = store;
		}
		else if (choose == "SCAN" || choose == "scan") {
			map<int, int>diskstream2;
			for (auto disk = diskstream.begin(); disk != diskstream.end(); disk++) {
				diskstream2.insert(pair<int, int>(disk->second, disk->first));
			}
			cout << "SCAN算法磁头访问磁道的顺序:" << endl;
			cout << "*****************************************************************************" << endl;
			for (auto disk = diskstream2.begin(); disk != diskstream2.end(); disk++) {
				if (disk->first >= now) {
					length += abs(now - disk->first);
					now = disk->first;
					cout << disk->first << " ";
				}
			}
			length += abs(now - store);
			now = store;
			for (auto disk = diskstream2.rbegin(); disk != diskstream2.rend(); disk++) {
				if (disk->first < now) {
					length += abs(now - disk->first);
					now = disk->first;
					cout << disk->first << " ";
				}
			}
			cout << endl;
			cout << "*****************************************************************************" << endl;
			length /= 20;
			cout << "SCAN算法平均寻道长度=" << length << endl;
			now = store;
		}
		else if (choose == "CSCAN" || choose == "cscan") {
			map<int, int>diskstream3;
			for (auto disk = diskstream.begin(); disk != diskstream.end(); disk++) {
				diskstream3.insert(pair<int, int>(disk->second, disk->first));
			}
			cout << "CSCAN算法磁头访问磁道的顺序:" << endl;
			cout << "*****************************************************************************" << endl;
			for (auto disk = diskstream3.begin(); disk != diskstream3.end();) {
				if (disk->first >= now) {
					length += abs(now - disk->first);
					now = disk->first;
					cout << disk->first << " ";
					diskstream3.erase(disk++);
				}
				else { disk++; }
			}
			length += abs(now - store);
			now = store;
			for (auto disk = diskstream3.begin(); disk != diskstream3.end(); disk++) {
					length += abs(now - disk->first);
					now = disk->first;
					cout << disk->first << " ";
			}
			cout << endl;
			cout << "*****************************************************************************" << endl;
			length /= 20;
			cout << "CSCAN算法平均寻道长度=" << length << endl;
			now = store;
		}
		else if (choose == "S" || choose == "s") { break; }
		else {
			cout << "您的输入有误!请输入FCFS、SSTF、SCAN、CSCAN当中的一个!" << endl;
		}
	}
	cout << "感谢您的使用~祝您生活愉快!" << endl;
	cout << "*************************************************" << endl;
}

在这里插入图片描述 在这里插入图片描述

总结

磁盘调度的四种算法实现难度没有页面置换算法高,而比起进程调度银行家算法则需要更灵活的思维。在独立实现这四个程序后,对于map这种数据结构可以非常熟悉了,同时我们也发现map是写操作系统模拟算法的不二之选,对于各种标记都可以使用一一对应的一组数据来进行存储,大大增加了代码效率,是其他数据结构所不能比拟的。本文章作于2021.5.26。