源代码
第1章 概述
磁盘调度在多道程序设计的计算机系统中,各个进程可能会不断提出不同的对磁盘进行读/写操作的请求。由于有时候这些进程的发送请求的速度比磁盘响应的还要快,因此我们有必要为每个磁盘设备建立一个等待队列,常用的磁盘调度算法有以下四种:先来先服务算法(FCFS),最短寻道时间优先算法(SSTF),扫描算法(SCAN),循环扫描算法(CSCAN)。
在多道程序设计的计算机系统中,各个进程随时都可能提出对磁盘进行读写的请求。因为进程请求速度远高于磁盘读写速度,所以我们会为磁盘设备建立一个等待队列,然后按照调度算法,来调度磁盘执行读写操作。
1.2算法简述和对比
1.2.1算法简述
(1)先来先服务算法(FCFS)----FirstComeFirstServer
可使用链表(若有数量上限则单设头结点记录请求数,超出则拒绝)和数组(数组长度限制为请求数量,防止越界),依据请求时间先后,对io请求进行队列排列,依次出队
优劣:公平、简单;平均寻道时间可能较长
(2)最短寻道算法(SSTF)
其访问规则为距离当前磁头最近的io请求进行服务,由“最近”一词和磁盘读写顺序可知,其可能会返回对一个柱面的磁道进行多次读写,造成磁盘黏着现象
基本实现:动态处理IO请求,可使用链表(双向链表,避免越界和前置判空操作)或者数组(内存允许则最好用数组,减少寻址时间)实现,使用插入排序算法,对IO请求进行动态排序,指针p指向磁头的当前磁道和扇区对应的线形距离数字,对前置后驱元素进行判定,以距离较短者作为下次磁盘访问对象。
优劣:平均寻道时间比FCFS算法短,但可能会出现“饥饿现象”和“磁臂粘着”现象。
(3)扫描算法(电梯算法SCAN)
原理:将各磁道请求映射为线形地址,进行单向线形扫描,一方为空,则反向,直至该磁道请求为空,切换下一磁道
算法实现:外嵌for对各柱面请求进行扫描,内嵌while包含俩个for循环,对选择的柱面进行来回扫描(一个增,一个减,双向),请求空则break。
优劣:消除了“饥饿”现象,但可能会出现“磁臂粘着”现象
(4)循环扫描算法(CSCAN)
原理:改进SCAN,不改变方向,单方向扫描,若无则返回到最外层需要访问的磁道(没就0)
算法实现:参考SCAN,while去除内置为单个for,设为单向
优劣: 改进了对于边缘区磁道访问的不公平,但可能会出现“磁臂粘着”现象。
1.2.2对比
优点 | 缺点 | |
FCFS算法 | 公平、简单 | 平均寻道距离大,仅应用在磁盘I/O较少的场合 |
SSTF算法 | 性能比“先来先服务”好 | 不能保证平均寻道时间最短,可能出现“饥饿”现象 |
SCAN算法 | 寻道性能较好,可避免“饥饿”现象 | 不利于远离磁头一端的访问请求 |
C-SCAN算法 | 消除了对两端磁道请求的不公平 | -- |
第2章 系统分析和概要设计
2.1系统分析
2.1.1实验目的
通过这次实验,加深对磁盘调度算法的理解,进一步掌握先来先服务FCFS、最短寻道时问优先SSTF、SCAN和循环SCAN算法的实现方法。
2.1.2问题描述
设计程序模拟先来先服务FCFS、最短寻道时间优先 SSTF、SCAN和循环SCAN算法的工.作过程。假设有n个磁道号所组成的磁道访问序列,给定开始磁道号m和磁头移动的方向(正向或者反向),分别利用不同的磁盘调度算法访问磁道序列,给出每一次访问的磁头移动距离,计算每种算法的平均寻道长度。
2.1.3程序要求
1)利用先来先服务FCFS、最短寻道时间优先SSTF、SCAN和循环SCAN 算法模拟磁道访问过程。
2)模拟四种算法的磁道访问过程,给出每个磁道访问的磁头移动距离。
3)输入:磁道个数n和磁道访问序列,开始磁道号m和磁头移动方向(对SCAN和循环SCAN算法有效),算法选择1-FCFS,2-SSTF,3-SCAN,4-循环SCAN。
4)输出:每种算法的平均寻道长度。
2.2概要设计
2.2.1程序中的变量
a)磁盘数:int num;
b)请求磁盘序列:int request[100];
c)开始磁盘位置:int begin;
d)横跨的总数:int sum;
e)每次横跨的磁盘数:int k;
f)复制初始序列:int re[100];
g)记录每个算法执行后序列:int r[100];
h)算法序列及寻道方向:int n, m;
2.2.2主要函数说明
a)获取用户输入的磁盘个数和磁盘的访问序列:void input()
b)先来先服务算法:void FIFO();
c)最短寻道时间优先算法: void SSTF();
d)扫描算法: void SCAN();
e)循环扫描算法: void C_SCAN();
f)返回离开始磁盘b最近的磁盘下标:int Smin(int b, int re[]) ;
g)递归函数:void choose_algorithm();
2.2.3设计原理
1) 寻找时间Ts:活动头磁盘在读写信息前,将磁头移动到指定磁道所需要的时间。这个时间除跨越n条磁道的时间外,还包括启动磁臂的时间s,即:Ts = m * n + s。式中,m是与磁盘驱动器速度有关的常数,约为0.2ms,磁臂的启动时间约为2ms。
2)延迟时间Tr:磁头定位到某一磁道的扇区(块号)所需要的时间,设磁盘的旋转速度为r,则:Tr = 1 / (2 * r)。对于硬盘,典型的旋转速度为5400r/m,相当于一周11.1ms,则Tr为5.55ms;对于软盘,其旋转速度在300~600r/m之间,则Tr为50~100ms。
3) 传输时间Tt:从磁盘读出或向磁盘写入数据所经历的时间,这个时间取决于每次所读/写的字节数b和磁盘的旋转速度:Tt = b / (r * N)。式中,r为磁盘每秒钟的转数;N为一个磁道上的字节数。
在磁盘存取时间的计算中,寻道时间与磁盘调度算法相关,下面将会介绍分析几种算法,而延迟时间和传输时间都与磁盘旋转速度相关,且为线性相关,所以在硬件上,转速是磁盘性能的一个非常重要的参数。
总平均存取时间Ta可以表示为:Ta = Ts + Tr + Tt。
虽然这里给出了总平均存取时间的公式,但是这个平均值是没有太大实际意义的,因为在实际的磁盘I/O操作中,存取时间与磁盘调度算法密切相关。调度算法直接决定寻找时间,从而决定了总的存取时间。
(1)运行程序后进入初始菜单界面;
(2)输入数字1—4选择想要模拟的算法,或是输入数字0退出程序;
(3)按提示依次输入调度磁道数量、磁道调度序列、当前磁道号;
(4)SCAN算法与C_SCAN算法需要输入数字1或2选择磁头移动的方向(1为从小到大,2为从大到小);
图3-1 主程序流程图
图3-2初始界面实现代码1
图3-3初始界面实现代码2
图3-4初始界面实现代码3
(1)定义sum=0,用于累加记录每一次的寻道长度
(2)按输入的顺序遍历整个序列,将前后两个数据之差的绝对值累加入sum中
(3)按顺序输出初始的序列,平均寻道长度为sum的值除以整个序列的长度num,结果保留3位小数
图3-5先来先服务算法实现代码
3.3最短寻道时间优先算法
3.3.1最短寻道时间优先算法流程
(1)定义c=0,用于记录磁道下标;
(2)定义sum=0,用于累加记录每一次的寻道长度;
(3)按输入的顺序遍历整个序列,将前后两个数据之差的绝对值累加入sum中
(4)按顺序输出初始的序列,平均寻道长度为sum的值除以整个序列的长度num,结果保留3位小数
3.3.2实现代码
图3-6最短寻道时间优先算法实现代码
3.4电梯调度算法
3.4.1电梯调度算法流程
(2)定义sum=0,用于累加记录每一次的寻道长度;
(3)按输入的顺序遍历整个序列,将前后两个数据之差的绝对值累加入sum中
(4)按顺序输出初始的序列,平均寻道长度为sum的值除以整个序列的长度num,结果保留3位小数
3.4.2实现代码
图3-7 SCAN算法实现代码1
图3-8 SCAN算法实现代码2
3.5循环扫描算法
3.5.1循环扫描算法流程
(1)定义c=0,用于记录磁道下标;
(2)定义sum=0,用于累加记录每一次的寻道长度;
(3)按输入的顺序遍历整个序列,将前后两个数据之差的绝对值累加入sum中
(4)按顺序输出初始的序列,平均寻道长度为sum的值除以整个序列的长度num,结果保留3位小数
3.5.2实现代码
图3-9 C_SCAN算法实现代码1
图3-10 C_SCAN算法实现代码2
第4章 测试与运行
4.1系统使用说明
使用系统详细步骤如下:
(1)运行程序后进入初始菜单界面;
(2)输入数字1—4选择想要模拟的算法,或是输入数字0退出程序;
(3)按提示依次输入调度磁道数量、磁道调度序列、当前磁道号;
(4)SCAN算法与C_SCAN算法需要输入数字1或2选择磁头移动的方向(1为从小到大,2为从大到小);
(5)执行结束后,会再次出现主菜单,此时可以选择再次进行模拟或是退出系统
初始界面显示如图4-1所示:
图4-1 初始界面
开始运行FIFO,如图4-2:
图4-2 FIFO算法
4.3最短寻道时间优先算法运行
SSTF运行过程,如图4-3:
图4-3 SSTF运行过程
4.4电梯扫描算法运行
SCAN运行过程,如图4-4(增大方向),4-5(减小方向):
图4-4 SCAN运行过程1
图4-5 SCAN运行过程2
4.5循环扫描算法运行
CSCAN运行过程,如图4-6(增大方向),4-7(减小方向):
图4-6 CSCAN运行过程(1)
图4-7 CSCAN运行过程(2)
4.6过程中遇到的问题
问题1:在一开始进行运行测试时,发现SCAN扫描算法和C_SCAN循环扫描算法代码中未设置磁头移动的方向,只有从小到大没有从大到小。此时,只需改进一下SCAN扫描算法和C_SCAN循环扫描算法代码,设置了SCAN1(),SCAN2()和C_SCAN1(),C_SCAN2()函数,分别代表磁头从小到大方向和从大到小方向。但是,这样就提高了代码的空间复杂度,我认为可以进一步改进,使得只需SCAN()与C_SCAN()两个函数就可以分别控制磁头移动的两个方向。
问题2:改进完代码之后,再次运行,发现只能测试四种算法的其中一种,而不是一次性可以测试完四种算法。此时,再次加入choose_algorithm()函数,利用递归的思想,顺利地使此代码能够一次性测试四种算法。
问题3:再次运行代码,输入时不小心输入了错误的编号,此时测试结果发生了错误。便再次对代码进行改进,使得电脑能够识别错误的编号,并提醒操作者进行正确的输入,并且在运行完后可输入“0”退出磁盘调度系统。
第5章 总结与心得
5.1总结
本课程是计算机科学与技术专业的主要专业基础课和主干课。操作系统对计算机系统资源实施管理,是所有其他软件与计算机硬件的唯一接口,所有用户在使用计算机时都要得到操作系统提供的服务。本课程的学习目的在于使学生掌握操作系统的基本概念、基本原理、设计方法和实现技术,具有初步分析实际操作系统的能力,为其今后在相关领域开展工作打下坚实的基础。
操作系统是一门工程性很强的课程,它不仅要求学生掌握操作系统的工作原理和理论知识,也要求学生的实际动手能力,以加深对所学习内容的理解,使学生熟练地掌握计算机的操作方法,使用各种软件工具,加强对课程内容的理解。这次课程设计,就是通过模拟磁盘调度来加深对操作系统理解。
在算法实现上要有一定的思路要更能体现设计的目的。同时上机调试也是十分重要的,在调试的过程中能够不断的发现在编写算法时应该注意的一些细节和算法语句的非法使用,在调试过程中通过对算法的不断测试、更正、扩充功能、修饰细节,使算法程序不断的得到完善。
通过这次的课程设计使我认识到要将操作系统这门计算机专业的课学好不仅仅是要把书上的基本知识学好而且还要不断进行实践,将所学的跟实践操作结合起来才能更好地巩固所学,才能提高自己实践能力.通过这次的设计使我认识到只停留在表面理解问题是很难使问题得到很好的解决的,实践能力与理论知识同样重要。可以说此课程设计的理论难度并不大,但是若要深入发掘其中的东西,并且实际去编程实现,就遇到了相当大的难度。因为与之涉及的很多方面并没有学过,需要自己去自学和实践检验。
所以在以后的学习中一方面我要不断的巩固自己所学的理论知识,一方面还要多参加实际操作工作以便提高自己的实际操作能力。
其实这次课程设计的最大收获应该是找到了解决问题的儿个很好的途径:1.讨论⒉.通过网络,在自己的网站上也收获了很多,共享让我们共同进步。此外,我学会了对程序的效用和算法的效率角度去思考。并体会到:问别人只能帮你开拓思路,真正解决问题还是要靠自己去摸索。当然讨论是很好的学习途径,它会让你事半功倍.
由于时间不是很充裕,程序中有很多值得改善的地方,所以需要继续努力。
通过本次课程设计,通过模拟磁盘调度及进程排队算法来加深对操作系统中各个磁臂调度算法概念的理解。模拟磁盘调度算法(FCFS,SSTF, SCAN, CSCAN),实现各种不同调度算法的过程,并计算各算法的平均寻道长度,以便于我们判断各种算法的优劣以及各种算法使用的场合。
5.2心得
十几天的课设时间很快就过去了,其中既有欢乐又有痛苦。通过这次的课程设计,我认识到要将操作系统这门计算机专业的课学好不仅仅是要把书上的基本知识学好,还要不断进行实践,将所学的跟实践操作结合起来才能更好地巩固所学,才能提高自己实践能力。通过这次的设计使我认识到只停留在表面理解问题是很难使问题得到很好的解决的,实践能力与理论知识同样重要。这次试验的理论难度并不大,但是若要深入发掘其中的东西,并且实际去编程实现,就遇到了相当大的难度。我们的编程能力并不是特别突出,又要在很短的时间内给出算法的实现,非常困难。因此我们不仅要在课上认真试验,课后还要查资料、做实验,终于经过不懈的努力,我们克服了以上的种种困难,按时完成了课设的任务,再次感谢帮助我们的老师和同学。
5.3工程认证指标点
(1)使用devc++编译程序进行代码的编译、运行、调试;
(2)在互联网中,联系专业教师探讨代码的优化问题;
(3)使用网上绘图工具绘制主程序流程图。
[1]孙钟秀等.《操作系统教程》[M].高等教育出版社.
[2]张丽芬,刘利雄.《操作系统实验教程》[M].清华大学出版社.
[3]孟静.《操作系统教程——原理和实例分析》[M].高等教育出版社.
[4]周长林.《计算机操作系统教程》[M].高等教育出版社.
[5]张坤.《操作系统实验教程》[M].清华大学出版社.
源代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define max 300
int num; //磁盘数
int request[100]; //请求磁盘序列
int begin; //开始磁盘位置
int sum; //横跨的总数
int k; //每次横跨的磁盘数
int re[100]; //复制初始序列
int r[100]; //记录每个算法执行后序列
int n, m;
void input();
void FIFO() { //先进先出
sum = abs(begin - request[0]);
printf("\n先进先出算法:\n FIFO调度: %3d", begin);
for (int i = 0; i < num; i++)
printf(" %3d", request[i]);
printf("\n 横跨磁道数为: %3d", abs(begin - request[0]));
for (int i = 1; i < num; i++) {
k = abs(request[i - 1] - request[i]);
printf(" %3d", k);
sum += k;
}
printf("\n 横跨的总磁道数: %3d", sum);
printf("\n 平均寻道长度: %.2f\n", 1.0 * sum / num);
}
int Smin(int b, int re[]) { //返回离开始磁盘b最近的磁盘下标
int min = abs(b - re[0]);//初始化横跨磁道数
int j = 0;
for (int i = 1; i < num; i++)
if (abs(b - re[i]) < min) {
min = abs(b - re[i]);
j = i;
}
return j;
}
void SSTF() { //最短寻道算法
int c = 0, b = begin;
printf("\n最短服务时间优先算法:\n SSTF调度: %3d", begin);
for (int i = 0; i < num; i++) {
c = Smin(b, re); //返回最近的磁道下标
b = re[c]; //将最近的磁盘作为开始
re[c] = 9999999; //将已经访问过的磁盘 设为很大值
printf(" %3d", b);
r[i] = b;
}
sum = abs(begin - r[0]);
printf("\n 横跨磁道数为: %3d", abs(begin - r[0]));//依次输出横跨的磁道数
for (int i = 1; i < num; i++) {
k = abs(r[i - 1] - r[i]);
printf(" %3d", k);
sum += k;
}
printf("\n 横跨的总磁道数: %3d", sum);
printf("\n 平均寻道时间: %.2f\n", 1.0 * sum / num);
}
void SCAN1() { //扫描算法
int c = 0, b = begin;
for (int i = 0; i < num; i++) //SSTF时re[]已改变
re[i] = request[i];
printf("\n扫描算法:\n SCAN调度: %3d", begin);
for (int i = 0; i < num - 1; i++) {
for (int j = 0; j < num - i - 1; j++) {
if (re[j] > re[j + 1]) {
re[j] = re[j] + re[j + 1];
re[j + 1] = re[j] - re[j + 1];
re[j] = re[j] - re[j + 1];
}
}
}
for (int i = 0; i < num; i++)
if (re[i] > b) {
printf(" %3d", re[i]);
r[c++] = re[i];
}
for (int i = num - 1; i >= 0; i--)
if (re[i] < b) {
printf(" %3d", re[i]);
r[c++] = re[i];
}
sum = abs(begin - r[0]);
printf("\n 横跨磁道数为: %3d", abs(begin - r[0]));
for (int i = 1; i < num; i++) {
k = abs(r[i - 1] - r[i]);
printf(" %3d", k);
sum += k;
}
printf("\n 横跨的总磁道数: %3d", sum);
printf("\n 平均寻道时间: %.2f\n", 1.0 * sum / num);
}
void SCAN2() { //扫描算法
int c = 0, b = begin;
for (int i = 0; i < num; i++) //SSTF时re[]已改变
re[i] = request[i];
printf("\n扫描算法:\n SCAN调度: %3d", begin);
for (int i = 0; i < num - 1; i++) {
for (int j = 0; j < num - i - 1; j++) {
if (re[j] < re[j + 1]) {
re[j] = re[j] + re[j + 1];
re[j + 1] = re[j] - re[j + 1];
re[j] = re[j] - re[j + 1];
}
}
}
for (int i = 0; i < num; i++)
if (re[i] < b) {
printf(" %3d", re[i]);
r[c++] = re[i];
}
for (int i = num - 1; i >= 0; i--)
if (re[i] > b) {
printf(" %3d", re[i]);
r[c++] = re[i];
}
sum = abs(begin - r[0]);
printf("\n 横跨磁道数为: %3d", abs(begin - r[0]));
for (int i = 1; i < num; i++) {
k = abs(r[i - 1] - r[i]);
printf(" %3d", k);
sum += k;
}
printf("\n 横跨的总磁道数: %3d", sum);
printf("\n 平均寻道时间: %.2f\n", 1.0 * sum / num);
}
void C_SCAN1() { //循环扫描
int c = 0, b = begin;
printf("\n循环扫描算法:\n CSCAN调度: %3d", begin);
for (int i = 0; i < num - 1; i++) {
for (int j = 0; j < num - i - 1; j++) {
if (re[j] > re[j + 1]) {
re[j] = re[j] + re[j + 1];
re[j + 1] = re[j] - re[j + 1];
re[j] = re[j] - re[j + 1];
}
}
}
for (int i = 0; i < num; i++)
if (re[i] > b) {
printf(" %3d", re[i]);
r[c++] = re[i];
}
for (int i = 0; i < num; i++)
if (re[i] < b) {
printf(" %3d", re[i]);
r[c++] = re[i];
}
sum = abs(begin - r[0]);
printf("\n 横跨磁道数为: %3d", abs(begin - r[0]));
for (int i = 1; i < num; i++) {
k = abs(r[i - 1] - r[i]);
printf(" %3d", k);
sum += k;
}
printf("\n 横跨的总磁道数: %3d", sum);
printf("\n 平均寻道时间: %.2f\n", 1.0 * sum / num);
}
void C_SCAN2() { //循环扫描
int c = 0, b = begin;
printf("\n循环扫描算法:\n CSCAN调度: %3d", begin);
for (int i = 0; i < num - 1; i++) {
for (int j = 0; j < num - i - 1; j++) {
if (re[j] < re[j + 1]) {
re[j] = re[j] + re[j + 1];
re[j + 1] = re[j] - re[j + 1];
re[j] = re[j] - re[j + 1];
}
}
}
for (int i = 0; i < num; i++)
if (re[i] < b) {
printf(" %3d", re[i]);
r[c++] = re[i];
}
for (int i = 0; i < num; i++)
if (re[i] > b) {
printf(" %3d", re[i]);
r[c++] = re[i];
}
sum = abs(begin - r[0]);
printf("\n 横跨磁道数为: %3d", abs(begin - r[0]));
for (int i = 1; i < num; i++) {
k = abs(r[i - 1] - r[i]);
printf(" %3d", k);
sum += k;
}
printf("\n 横跨的总磁道数: %3d", sum);
printf("\n 平均寻道时间: %.2f\n", 1.0 * sum / num);
}
void choose_algorithm() {
printf(" ******************\n *磁盘调度模拟实现*\n");
printf(" ******************\n\n\n");
printf(" *****************************************\n");
printf(" *****************************************\n");
printf(" ********1、先来先服务(FCFS)算法********\n");
printf(" ********2、最短寻道时间(SSTF)算法******\n");
printf(" ********3、扫描(SCAN)算法**************\n");
printf(" ********4、循环扫描(C_SCAN)算法********\n");
printf(" ********0、退出**************************\n");
printf(" *****************************************\n");
printf(" *****************************************\n\n\n");
printf("请选择磁盘调度算法: ");
scanf("%d", &n);
if (n == 1) {
input();
FIFO();
system("pause");
system("cls");
choose_algorithm();
} else if (n == 2) {
input();
SSTF();
system("pause");
system("cls");
choose_algorithm();
} else if (n == 3) {
input();
printf("1、由小到大 \n2、由大到小\n请选择磁头移动的方向:");
scanf("%d", &m);
if (m == 1) {
SCAN1();
system("pause");
system("cls");
choose_algorithm();
} else if (m == 2) {
SCAN2();
system("pause");
system("cls");
choose_algorithm();
} else {
printf("输入操作编号错误,请输入有效操作编号!");
system("pause");
system("cls");
choose_algorithm();
}
} else if (n == 4) {
input();
printf("1、由小到大 \n2、由大到小\n请选择磁头移动的方向:");
scanf("%d", &m);
if (m == 1) {
C_SCAN1();
system("pause");
system("cls");
choose_algorithm();
} else if (m == 2) {
C_SCAN2();
system("pause");
system("cls");
choose_algorithm();
} else {
printf("输入操作编号错误,请输入有效操作编号!");
system("pause");
system("cls");
choose_algorithm();
}
} else if (n == 0) {
exit(0);
} else {
printf("输入操作编号错误,请输入有效操作编号!");
system("pause");
system("cls");
choose_algorithm();
}
}
void input() {
printf("请输入调度磁道数量: ");
scanf("%d", &num);
printf("请输入磁道调度序列: ");
for (int i = 0; i < num; i++) {
scanf("%d", &request[i]);
re[i] = request[i];
}
printf("请输入当前磁道号: ");
scanf("%d", &begin);
return;
}
int main() {
choose_algorithm();
return 0;
}