操作系统课程设计磁盘调度算法Word格式.docx
《操作系统课程设计磁盘调度算法Word格式.docx》由会员分享,可在线阅读,更多相关《操作系统课程设计磁盘调度算法Word格式.docx(31页珍藏版)》请在冰豆网上搜索。
设备的动态分配算法与进程调度相似,也是基于一定的分配策略的。
常用的分配策略有先请求先分配、优先级高者先分配等策略。
在多道程序系统中,低效率通常是由于磁盘类旋转设备使用不当造成的。
操作系统中,对磁盘的访问要求来自多方面,常常需要排队。
这时,对众多的访问要求按一定的次序响应,会直接影响磁盘的工作效率,进而影响系统的性能。
访问磁盘的时间因子由3部分构成,它们是查找(查找磁道)时间、等待(旋转等待扇区)时间和数据传输时间,其中查找时间是决定因素。
因此,磁盘调度算法先考虑优化查找策略,需要时再优化旋转等待策略。
平均寻道长度(L)为所有磁道所需移动距离之和除以总的所需访问的磁道数(N),即:
L=(M1+M2+……+Mi+……+MN)/N
其中Mi为所需访问的磁道号所需移动的磁道数。
启动磁盘执行输入输出操作时,要把移动臂移动到指定的柱面,再等待指定扇区的旋转到磁头位置下,然后让指定的磁头进行读写,完成信息传送。
因此,执行一次输入输出所花的时间有:
寻找时间——磁头在移动臂带动下移动到指定柱面所花的时间。
延迟时间——指定扇区旋转到磁头下所需的时间。
传送时间——由磁头进程读写完成信息传送的时间。
其中传送信息所花的时间,是在硬件设计就固定的。
而寻找时间和延迟时间是与信息在磁盘上的位置有关。
为了减少移动臂进行移动花费的时间,每个文件的信息不是按盘面上的磁道顺序存放满一个盘面后,再放到下一个盘面上。
而是按柱面存放,同一柱面上的各磁道被放满信息后,再放到下一个柱面上。
所以各磁盘的编号按柱面顺序(从0号柱面开始),每个柱面按磁道顺序,每个磁道又按扇区顺序进行排序。
2.2算法描述
1.先来先服务算法(FCFS)
先来先服务(FCFS)调度:
按先来后到次序服务,未作优化。
最简单的移臂调度算法是“先来先服务”调度算法,这个算法实际上不考虑访问者要求访问的物理位置,而只是考虑访问者提出访问请求的先后次序。
例如,如果现在读写磁头正在50号柱面上执行输出操作,而等待访问者依次要访问的柱面为130、199、32、159、15、148、61、99,那么,当50号柱面上的操作结束后,移动臂将按请求的先后次序先移到130号柱面,最后到达99号柱面。
采用先来先服务算法决定等待访问者执行输入输出操作的次序时,移动臂来回地移动。
先来先服务算法花费的寻找时间较长,所以执行输入输出操作的总时间也很长。
2.短寻道时间优先算法(SSTF)
最短寻找时间优先调度算法总是从等待访问者中挑选寻找时间最短的那个请求先执行的,而不管访问者到来的先后次序。
现在仍利用同一个例子来讨论,现在当50号柱面的操作结束后,应该先处理61号柱面的请求,然后到达32号柱面执行操作,随后处理15号柱面请求,后继操作的次序应该是99、130、148、159、199。
采用最短寻找时间优先算法决定等待访问者执行操作的次序时,读写磁头总共移动了200多个柱面的距离,与先来先服务、算法比较,大幅度地减少了寻找时间,因而缩短了为各访问者请求服务的平均时间,也就提高了系统效率。
但最短查找时间优先(SSTF)调度,FCFS会引起读写头在盘面上的大范围移动,SSTF查找距离磁头最短(也就是查找时间最短)的请求作为下一次服务的对象。
SSTF查找模式有高度局部化的倾向,会推迟一些请求的服务,甚至引起无限拖延(又称饥饿)。
3.扫描算法(SCAN)
SCAN算法又称电梯调度算法。
SCAN算法是磁头前进方向上的最短查找时间优先算法,它排除了磁头在盘面局部位置上的往复移动,SCAN算法在很大程度上消除了SSTF算法的不公平性,但仍有利于对中间磁道的请求。
“电梯调度”算法是从移动臂当前位置开始沿着臂的移动方向去选择离当前移动臂最近的那个柱访问者,如果沿臂的移动方向无请求访问时,就改变臂的移动方向再选择。
这好比乘电梯,如果电梯已向上运动到4层时,依次有3位乘客陈生、伍生、张生在等候乘电梯。
他们的要求是:
陈生在2层等待去10层;
伍生在5层等待去底层;
张生在8层等待15层。
由于电梯目前运动方向是向上,所以电梯的形成是先把乘客张生从8层带到15层,然后电梯换成下行方向,把乘客伍生从5层带到底层,电梯最后再调换方向,把乘客陈生从2层送到10层。
我们仍用前述的同一例子来讨论采用“电梯调度”算法的情况。
由于磁盘移动臂的初始方向有两个,而该算法是与移动臂方向有关,所以分成两种情况来讨论。
〈1〉.移动臂由里向外移动
开始时,,在50号柱面执行操作的读写磁头的移动臂方向是由里向外,趋向32号柱面的位置,因此,当访问50号柱面的操作结束后,沿臂移动方向最近的柱面是32号柱面。
所以应先为32号柱面的访问者服务,然后是为15号柱面的访问者服务。
之后,由于在向外移方向已无访问等待者,故改变移动臂的方向,由外向里依次为各访问者服务。
在这种情况下为等待访问者服务的次序是61、99、130、148、159、199。
〈2〉.移动臂由外向里移动
开始时,正在50号柱面执行操作的读写磁头的移动臂是由外向里(即向柱面号增大的内圈方向)趋向61号柱面的位置,因此,当访问50号柱面的操作结束后,沿臂移动方向最近的柱面是61号柱面。
所以,应先为61号柱面服务,然后按移动臂由外向里移动的方向,依次为99、130、148、159、199柱面的访问者服务。
当201号柱面的操作结束后,向里移动的方向已经无访问等待者,所以改变移动臂的前进方向,由里向外依次为32、15柱面的访问者服务。
“电梯调度”与“最短寻找时间优先”都是要尽量减少移动臂时所花的时间。
所不同的是:
“最短寻找时间优先”不考虑臂的移动方向,总是选择离当前读写磁头最近的那个柱面,这种选择可能导致移动臂来回改变移动方向;
“电梯调度”是沿着臂的移动方向去选择离当前读写词头最近的哪个柱面的访问者,仅当沿移动臂的前进移动方向无访问等待者时,才改变移动臂的前进方向。
由于移动臂改变方向是机械动作,速度相对较慢,所以,电梯调度算法是一种简单、使用且高效的调度算法。
但是,“电梯调度”算法在实现时,不仅要记住读写磁头的当前位置,还必须记住移动臂的当前前进方向。
4.循环扫描算法(CSCAN)
单项扫描调度算法的基本思想是,不考虑访问者等待的先后次序,总是从0号柱面开始向里道扫描,按照各自所要访问的柱面位置的次序去选择访问者。
在移动臂到达最后一个柱面后,立即快速返回到0号柱面,返回时不为任何的访问者等待服务。
在返回到0号柱面后,再次进行扫描。
由于该例中已假定读写的当前位置在50号柱面,所以,指示了从50号柱面继续向里扫描,依次为61、99、130、148、159、199各柱面的访问者服务,此时移动臂已经是最内的柱面,于是立即返回到0号柱面,重新扫描,依次为15、32号柱面的访问者服务。
除了“先来先服务”调度算法外,其余三种调度算法都是根据欲访问的柱面位置来继续调度的。
在调度过程中可能有新的请求访问者加入。
在这些新的请求访问者加入时,如果读写已经超过了它们所要访问的柱面位置,则只能在以后的调度中被选择执行。
在多道程序设计系统中,在等待访问磁盘的若干访问者请求中,可能要求访问的柱面号相同,但在同一柱面上的不同磁道,或访问同一柱面中同一磁道上的不同扇区。
所以,在进行移动调度时,在按照某种短法把移动臂定位到某个柱面后,应该在等待访问这个柱面的各个访问者的输入输出操作都完成之后,再改变移动臂的位置。
5.FSCAN算法
FSCAN算法实质上是N步SCAN算法的简化,即FSCAN只将磁盘请求队列分成两个子队列。
一个是由当前所有请求磁盘I/O的进程形成的队列,由磁盘调度按SCAN算法进行处理。
在扫描期间,将新出现的所有请求磁盘I/O的进程,放入另一个等待处理的请求队列。
这样,所有的新请求都将被推迟到下一次扫描时处理。
如何进行FSCAN的模拟问题?
是否可以通过再设置一个定时器,产生随机时间间隔的随机磁盘请求?
再将这些随机磁盘请求纳入新的队列。
TEST发现以上方法不是很妥当,由于模拟是相对动态的模拟,而非动态模拟,如果按以上的方法实行,各模块都需要动,特别是随机定时器需要设置3个,分别进行动态模拟控制,
这违背了模拟的初衷,程序设计的意义不大。
所以采用的是产生一个随机扫描时间模拟确定扫描期间的磁盘请求个数,对上一界面传递过来的请求队列拆分成两个队列。
intnum=rand()%call_num;
//产生一个随机扫描时间模拟确定扫描期间的磁盘请求个数。
三、开发环境
VC++6.0MFC
四、重要算法和设计思路描述
◆4.1各算法的流程图
●FCFS流程图:
●SSTF流程图
●SCAN流程图
●CSCAN流程图
●FSCAN流程图
◆三个模块的流程图
〈1〉.参数输入模块
〈2〉.算法模块
五、数据结构
主要使用的数据结构:
1.CString和CStringArray
`2.符号整型数组
MFC中CString和STL中string的互换问题?
技巧]:
通过char*作为中介.
CString->
std:
:
string例子:
CStringstrMfc=“test“;
stringstrStl;
strStl=strMfc.GetBuffer(0);
string->
CString例子:
CStringstrMfc;
stringstrStl=“test“;
strMfc=strStl.c_str();
初始化对话框类DSchedulingDlg定义的数据变量如下:
UINTcount_auto;
//记录磁盘产生的磁盘调度请求的个数,初始为0
UINTdisk_call_auto[1000];
//放置模拟产生的磁盘调度请求序列
UINTm_nTimer;
//定时器的标识
UINT*disk_call;
//磁盘请求的数组,向ALG传递的头指针
CDSchedulingDlg(CWnd*pParent=NULL);
//standardconstructor
算法对话框类DAlg定义的数据变量如下:
//DialogData
//{{AFX_DATA(CDSchedulingDlg)
enum{IDD=IDD_DSCHEDULING_DIALOG};
UINTm_now;
CStringm_disk_calling;
//}}AFX_DATA
UINT*disk_schedule;
//磁盘请求序列头指针
UINTnow;
//磁盘当前指向的磁道号
UINTcall_num;
//磁盘请求的个数
CDAlg(CWnd*pParent=NULL);
//{{AFX_DATA(CDAlg)
enum{IDD=IDD_DIALOG_ALG};
CStringm_disk_schedule;
//控件-》磁盘调度的序列
floatm_time;
//控件-》磁盘调度平均寻道时间
UINTm_sum;
六、程序实现---程序清单
程序实现的界面如图:
“模拟”按钮:
voidCDSchedulingDlg:
OnAutoStart()
{
//TODO:
Addyourcontrolnotificationhandlercodehere
m_nTimer=SetTimer(1,500,NULL);
//设置一个500MS响应时间的定时器
}
“停止模拟”按钮:
OnAutoStop()
KillTimer(m_nTimer);
//将定时器关闭
“SAVE”按钮:
OnSave()
CStringbuf;
UpdateData();
buf.Format("
%d"
+m_disk_calling,m_now);
CFileDialogfileDlg(FALSE);
fileDlg.m_ofn.lpstrFilter="
TextFiles(*.txt)\0*.txt\0AllFiles(*.*)\0*.*\0\0"
;
fileDlg.m_ofn.lpstrDefExt="
txt"
if(IDOK==fileDlg.DoModal())
{
CFilefile(fileDlg.GetFileName(),CFile:
modeCreate|CFile:
modeWrite);
file.Write(buf,buf.GetLength());
file.Close();
}
“OPEN”按钮:
OnOpen()
CFileDialogfileDlg(TRUE);
modeRead);
char*pBuf;
DWORDdwFileLen;
dwFileLen=file.GetLength();
pBuf=newchar[dwFileLen+1];
pBuf[dwFileLen]=0;
file.Read(pBuf,dwFileLen);
//MessageBox(pBuf);
CStringstr;
for(inti=0;
pBuf[i]!
=0;
i++)
str+=pBuf[i];
CStringArrayszResult;
intsubStringLen=SplitString(str,'
'
szResult);
if(subStringLen==2){
m_now=atoi(szResult.GetAt(0));
m_disk_calling=szResult.GetAt
(1);
}
else{
MessageBox("
打开文件格式错误,请确认后打开"
);
UpdateData(false);
“NEXT”按钮:
OnNext()
//CStringteststr=m_disk_calling.SpanExcluding("
.-:
"
//MessageBox(teststr);
CStringArrayszResult;
SplitString(m_disk_calling,'
-'
intn=szResult.GetSize();
//得到子字符串的个数
disk_call=newUINT[n];
//disk_call数组中存放磁道的请求序列
for(inti=0;
i<
n;
disk_call[i]=atoi(szResult.GetAt(i));
//到此部分完成输入的转化
UpdateData(false);
ShowWindow(SW_HIDE);
CDAlgDAlg;
//将当前磁道号,磁盘请求序列数组首地址和它的数目传递到CDAlg类中
DAlg.now=m_now;
DAlg.disk_call=disk_call;
DAlg.call_num=n;
DAlg.DoModal();
ShowWindow(SW_SHOW);
“退出系统”按钮:
OnCancel()
CDialog:
OnCancel();
//此函数将按算法排序后的无符号整型数组转化为CString
/*
result:
转化后的字符串
separator:
分隔符
disk_schedule:
无符号整型数组头指针
num:
数组的长度
*/
ChangeToString(CString&
result,charseparator,UINT*disk_schedule,UINTnum)
CStringtemp;
for(UINTi=0;
num;
temp.Format("
%d"
disk_schedule[i]);
result+=temp;
if(i<
num-1)
result+=separator;
//szText:
待分解字符串
//separator:
分割符
//szResult:
存放分解后的字符串
//返回值:
分解后的字符串个数
intSplitString(CString&
szText,charseparator,CStringArray&
szResult)
CStringszTemp;
intnCount=0;
i<
szText.GetLength();
i++)
if(szText.GetAt(i)!
=separator)
szTemp+=CString(szText.GetAt(i));
else
if(!
szTemp.IsEmpty())
szResult.SetAtGrow(nCount,szTemp);
nCount++;
//记数加1
szTemp.Empty();
//将临时字符串清空
if(i==szText.GetLength()-1&
&
!
szTemp.IsEmpty())//到字符串末尾的特殊情况
returnnCount;
DAlg类方法:
voidCDAlg:
//此函数用来计算平均寻道时间
now:
当前磁盘号
返回值是平均寻道时间
UINTTime(UINT*disk_schedule,UINTnum,UINTnow){
UINTsum=0;
sum+=abs(disk_schedule[i]-now);
now=disk_schedule[i];
returnsum;
//冒泡排序法
head:
voidSort(UINT*head,UINTnum)
UINTtemp;
num-1;
for(UINTj=0;
j<
num-i-1;
j++)
if(head[j]>
head[j+1])
{
temp=head[j];
head[j]=head[j+1];
head[j+1]=temp;
“FCFS”按钮:
OnFcfs()
m_d