目录
1.题目内容. 1
1.1设计一:进程调度. 1
1.2设计五:磁盘调度管理. 1
2.题目要求. 1
2.1设计一:进程调度. 1
2.2设计五:磁盘调度管理. 1
3.设计思想. 2
3.1设计一:进程调度. 2
3.2设计五:磁盘调度管理. 3
4.算法分析. 4
4.1设计一:进程调度. 4
4.1.1最高优先级优先算法. 4
4.1.2时间片轮转调度算法. 4
4.2设计五:磁盘调度管理. 5
4.2.1扫描算法SCAN5
4.2.2循环扫描算法CSCAN5
5.核心代码. 5
5.1设计一:进程调度. 5
5.1.1进程控制块结构. 5
5.1.2 create1()、create2()创建进程函数. 5
5.1.3 insert1()、insert2()插入函数. 7
5.1.4最高优先级优先和时间片轮转调度算法. 8
5.2设计五:磁盘调度管理. 10
5.2.1 GetNum()随机生成磁道号序列函数. 10
5.2.2 PaiXu()寻道长度排序函数. 10
5.2.3扫描算法(SCAN)和循环扫描算法(CSCAN)11
6.测试数据或截图. 15
6.1设计一:进程调度. 15
6.1.1最高优先级优先算法. 15
6.1.2时间片轮转调度算法. 17
6.2设计五:磁盘调度管理. 18
6.2.1扫描算法SCAN18
6.2.2循环扫描算法CSCAN19
6.2.3各类算法性能比较. 20
7.心得体会. 20
进程管理是操作系统中的重要功能,用来创建进程、撤消进程、实现进程状态转换,它提供了在可运行的进程之间复用CPU的方法。在进程管理中,进程调度是核心,因为在采用多道程序设计的系统中,往往有若干个进程同时处于就绪状态,当就绪进程个数大于处理器数目时,就必须依照某种策略决定哪些进程优先占用处理器。本设计模拟在单处理器情况下的进程调度,目的是加深对进程调度工作的理解,掌握最高优先级优先算法和时间片轮转调度算法的优缺点。
加深对请求磁盘调度管理实现原理的理解,掌握磁盘调度算法。通过编程实现扫描算法SCAN、循环扫描算法CSCAN两种磁盘调度算法。
设计程序模拟单处理机系统中的进程调度算法,实现最高优先级优先算法、时间片轮转调度算法两种算法。
每个进程由一个进程控制块(PCB)表示。进程控制块可以包含如下信息:进程名、优先数、到达时间、需要运行时间、已用CPU时间、进程状态等。
进程的优先数及需要的运行时间可以事先人为地指定(也可以由随机数产生)。进程的到达时间为进程输入的时间。
进程的运行时间以时间片为单位进行计算。
每个进程的状态可以是就绪W(Wait)、运行R(Run)或完成F(Finish)3中状态之一。
设定开始磁道号寻道范围,依据起始扫描磁道号和最大磁道号数,随机产生要进行寻道的磁道号序列。选择扫描算法SCAN、循环扫描算法CSCAN两种磁盘调度算法,显示该算法的磁道访问顺序,计算出移动的磁道总数和平均寻道总数。并对算法性能进行分析对比。
进程是程序在处理机上的执行过程。进程存在的标识是进程控制块(PCB),所谓系统创建一个进程,就是由系统为某个程序设置一个PCB,用于对该进程进行控制和管理。进程任务完成,由系统收回其PCB,该进程便消亡。每个进程可有三个状态:运行状态、就绪状态和完成状态。因此设计三个链队列,finish为完成队列的头指针,ready为就绪队列的头指针,tail为循环轮转法中的就绪队列的尾指针。因为每一时刻,CPU只能运行一个进程,所以运行队列只有一个run指针指向当前运行的进程。考虑到处理的方便,将它们设为全局变量。
(1)priority()优先调度算法
在prioyity()中,进程每执行一次,优先数减1(自定),CPU时间数加1,进程还需要的时间数减1,。如果进程所需的时间为0,说明进程运行完毕,将其状态变为完成状态“F”,将此PCB插入到完成队列中,程序设计插入在完成队列的头。此时就绪队列不空的话。调用函数firstin(),将就绪队列中的第一个PCB变为运行状态。如果进程没有完成,则将其优先数和就绪队列中第一个PCB的优先数作比较,如果小,则将其变为就绪态,调用insert1()函数按照优先数插入到就绪队列中,调用firstin()函数将就绪队列中的第一个PCB变为运行态投入运行,即将优先级别高的进程投入运行,保证优先数高的优先调用,重复上述过程,直到就绪队列为空,所有进程成为完成态为止。
(2)round()时间片轮转算法
在round()中,采用固定的时间片,时间片数设为2,进程每执行一次,计数器加1,CPU时间片数加1,进程还需等待的时间片减1,如果进程所需要的时间为0,说明进程运行完毕,将其状态变为完成状态“F”,将此PCB插入到完成队列中,程序设计插入在完成队列的头。如果进程没有完成,而计数器等于固定时间片2,即到时间了,则将其变为就绪态插入到就绪队列的队尾,同时计数器清零,等待再次轮转到时调用。此时如果就绪队列不空的话,调用函数firstin(),将就绪队列的第一个PCB变为运行态,重复上述过程,知道就绪队列为空,所有进程成为完成态为止。
(3)main()主函数
用switch()选择语句来实现选用的调度算法,然后输入进程数目,根据选用的调度算法调用相应的函数创建进程和执行调度算法。
(4)create1()、create2()创建进程函数
创建进程函数的功能既创建进程控制块(PCB),并将此PCB链入就绪队列中。
create1()是按照优先数调度算法创建进程,用户输入进程标识符na以及进程所需的时间time,因为是用链表,所以申请空间存放进程PCB信息,进程的初始状态设定为“W”(就绪状态),优先数为随机数。将每个进程的PCB调用函数insert1()按照优先数大小从高到低排到就绪队列中。
create2()是按照时间片轮转调度算法创建进程,用户输入进程标识符na以及进程所需的时间time,申请空间存放进程PCB的信息,进程的初始状态设为“W”(就绪状态),时间片数设为2,统计运行所用的时间片的计数器变量count初值为0,调用函数insret2()将每个进程PCB按照输入的先后顺序插入到就绪队列的末尾。就绪队列创建好后,将队列中的第一个PCB变为运行态“R”,将run指针指向它,ready指针后移,作为就绪队列的新头指针,然后调用算法,注意每一时刻只能有一个进程处于运行态。
(5)insert1()、insert2()插入函数
这两个函数完成就绪队列的建立和管理。
insert1()的功能是将还未完成且优先数小于其他进程的PCB按进程优先数的顺序插入到就绪队列中,这个函数主要考虑插入位置。
insert2()是轮转法用的函数,功能是将执行了一个时间片且还未完成的进程的PCB插入到就绪队列的对尾,这个算法比较简单,只需改变尾指针。
设定开始磁道号寻道范围,依据起始扫描磁道号和最大磁道号数,随机产生要进行寻道的磁道号序列。选择磁盘调度算法,显示该算法的磁道访问顺序,计算出移动的磁道总数和平均寻道总数。
(1)SCAN()扫描算法
在SCAN()中,调用GetNum函数产生随机磁道号序列。通过控制while语句的执行,使一定要使当前磁道向内向外都要扫描到。用for()循环寻找与当前磁道号最短寻道的时间的磁道号。用if(DiscLine[h]>=Han)判断磁道的移动方向,即是由里向外还是由外向里,然后扫面,保证所有磁道都扫描到。
(2)CSCAN()循环扫描算法
在CSCAN()中,首先规定磁头由内向外单方向移动。
调用GetNum函数产生随机磁道号序列,然后先从初始磁道号开始,由内向外搜索离当前磁道最近的磁道号。当磁头移到最外的磁道并访问后,磁头立即返回到最里的欲访问的磁道,亦即将最小磁道号紧接着最大磁道号构成循环,进行循环扫描。
(3)PaiXu()排序函数
使用这个函数功能完成对两个算法的移动磁道距离比较和排序。在SCAN()和CSCAN()算法中就定义其排序号为2和3。
采用冒泡排序法对移动距离大小排序,排完后则执行每个算法的排序。
(4)GetNum()随机生成磁道号序列函数
srand函数在stdlib.h头文件中,time函数在time.h头文件中。srand一般与rand函数配合生成随机数据。srand(time(NULL)),可以为rand函数提供不同的种子值,进而产生不同的随机数序列。srand((unsigned)(time(NULL)))产生随机数。再通过for循环调用rand函数输出成随机序列。
(5)main()主函数
用switch()选择语句来实现选用的调度算法,然后输入初始磁道号和范围,根据选用的调度算法调用相应的函数执行调度算法。
就绪进程获得CPU后都只能运行一个时间片,用已占用CPU时间加1来表示。
如果运行一个时间片后,进程的已占用CPU时间已达到所需要的运行时间,则撤销该进程,如果运行一个时间片后进程的已占用CPU时间还未达到所需要的运行时间,也即进程还需要继续运行,此时应将进程的优先数减1(即降低一级),然后把它插入就绪队列等待CPU。
每进行一次调度程序都打印一次运行进程、就绪队列以及各个进程的PCB,以便进行检查。
重复以上过程,直到所有进程都完成为止。
时间片轮转算法是将所有进程按先来先服务的规则排成一个队列,把CPU分配给就绪队列的队首进程,并规定它执行一定时间,称此时间间隔为时间片。当时间片完时,剥夺该进程的执行,将它送至就绪队列的末尾,并把处理机分配给就绪队列的新队首进程,同样也让它执行一个时间片。这样,就绪队列中的所有进程均可获得一个时间片的处理机运行,知道所有进程全部执行完毕。
SCAN算法不仅考虑到欲访问的磁道与当前磁道间的距离,更优先考虑的是磁头当前的移动方向。例如,当磁头正在自里向外移动时,SCAN算法所考虑的下一个访问对象,应是其欲访问的磁道既在当前磁道之外,又是距离最近的。这样自里向外地访问,直至再无更外的磁道需要访问时,才将磁臂换向为自外向里移动。
CSCAN算法规定磁头单向移动,例如,只是自里向外移动,当磁头移到最外的磁道并访问后,磁头立即返回到最里的欲访问的磁道,亦即将最小磁道号紧接着最大磁道号构成循环,进行循环扫描。
typedef struct node
{
charname[10]; //进程标识符
intprio; //进程优先数
intround; //进程时间轮转时间片
intcputime; //进程占用CPU时间
intneedtime; //进程到完成还要的时间
intcount; //计数器
charstate; //进程的状态
structnode *next; //链指针
intcometime;
}PCB;
void create1(char alg) //优先数创建初始PCB信息
{
PCB*p;
inti,time;
intd;
charna[10];
ready=NULL;//就绪队列头指针
finish=NULL; //完成队列头指针
run=NULL;//运行队列头指针
printf("请输入进程的名字和运行所需要的时间和到达时间\n"); //输入进程标识和所需时间创建PCB
for(i=1;i<=N;i++)
{
p=(PCB*)malloc(sizeof(PCB));
scanf("%s",na);
scanf("%d",&time);
scanf("%d",&d);
strcpy(p->name,na);
p->cputime=0;
p->needtime=time;
p->cometime=d;
p->state='W';
p->prio=50-time;
if(ready!=NULL)//就绪队列不空则调用插入函数插入
insert1(p);
else
{
p->next=ready;//创建就绪队列的第一个PCB
ready=p;
}
}
voidclrscr(void);
printf(" 最高优先级优先算法模拟输出结果:\n");
printf("*******************************************************************\n");
prt(alg); //输出进程PCB信息
run=ready;//将就绪队列的第一个进程投入运行
ready=ready->next;
run->state='R';
}
void create2(char alg) //轮转法创建进程PCB
{
PCB*p;
intd;
inti,time;
charna[10];
ready=NULL;
finish=NULL;
run=NULL;
printf("请输入进程的名字和运行所需要的时间和到达时间\n");
for(i=1;i<=N;i++)
{
p=(PCB*)malloc(sizeof(PCB));
scanf("%s",na);
scanf("%d",&time);
scanf("%d",&d);
strcpy(p->name,na);
p->cputime=0;
p->needtime=time;
p->cometime=d;
p->count=0;//计数器
p->state='W';
p->round=2; //时间片
if(ready!=NULL)
insert2(p);
else
{
p->next=ready;
ready=p;
tail=p;
}
}
voidclrscr(void);
printf(" 时间片轮转法模拟输出结果:\n");
printf("********************************************************************\n");
prt(alg); //输出进程PCB信息
run=ready; //将就绪队列的第一个进程投入运行
ready=ready->next;
run->state='R';
}
//优先数的插入算法
void insert1(PCB *q)
{
PCB*p1,*s,*r;
intb;
s=q; //待插入的PCB指针
p1=ready;//就绪队列头指针
r=p1;//r做p1的前驱指针
b=1;
while((p1!=NULL)&&b) //根据优先数确定插入位置
if(p1->prio>=s->prio)
{
r=p1;
p1=p1->next;
}
else
b=0;
if(r!=p1) //如果条件成立说明插入在r与p1之间
{
r->next=s;
s->next=p1;
}
else
{
s->next=p1; //否则插入在就绪队列的头
ready=s;
}
}
//轮转法插入算法
void insert2(PCB *p2)
{
tail->next=p2; //将新的PCB插入在当前就绪队列的尾
tail=p2;
p2->next=NULL;
}
//最高优先级优先算法
void priority(char alg)
{
while(run!=NULL) //当运行队列不空时,有进程正在运行
{
run->cputime=run->cputime+1;
run->needtime=run->needtime-1;
run->prio=run->prio-1; //每运行一次优先数降低1个单位
if(run->needtime==0) //如所需时间为0将其插入完成队列
{
run->next=finish;
finish=run;
run->state='F'; //置状态为完成态
run=NULL; //运行队列头指针为空
if(ready!=NULL) //如果就绪队列不空
firstin(); //将就绪对列的第一个进程投入运行
}
else //没有运行完同时优先数不是最大,则将其变为就绪态插入到就绪队列
if((ready!=NULL)&&(run->prio
{
run->state='W';
insert1(run);
firstin(); //将就绪队列的第一个进程投入运行
}
prt(alg); //输出进程PCB信息
}
}
//时间片轮转调度算法
void roundrun(char alg)
{
while(run!=NULL)
{
run->cputime=run->cputime+1;
run->needtime=run->needtime-1;
run->count=run->count+1;
if(run->needtime==0) //运行完将其变为完成态,插入完成队列
{
run->next=finish;
finish=run;
run->state='F';
run=NULL;
if(ready!=NULL)
firstin(); //就绪对列不空,将第一个进程投入运行
}
else
if(run->count==run->round) //如果时间片到
{
run->count=0; //计数器置0/
if(ready!=NULL) //如就绪队列不空
{
run->state='W'; //将进程插入到就绪队列中等待轮转
insert2(run);
firstin(); //将就绪对列的第一个进程投入运行
}
}
prt(alg);//输出进程信息
}
}
void GetNum(intDiscL[])
{
intj;
srand((unsigned)(time(NULL)));
for (j=0; j<10; j++)
{
DiscL[j]=rand()%Limit; //设定开始磁道号寻道范围为0~200
printf("%d ",DiscL[j]);
}
printf("\n");
}
void PaiXu()
{
inti,j,Temp;
for(i=0;i<4;i++)
{
for(j=0;j<3;j++)
{
if(Best[j][1]>Best[j+1][1]) //如果前一个算法的移动磁道距离大于后一个移动磁道数,执行下面语句
{
Temp=Best[j+1][1]; //从这起下三行执行冒泡法将移动距离大小排序,排完后则执行每个算法的排序
Best[j+1][1]=Best[j][1];
Best[j][1]=Temp;
Temp=Best[j+1][0]; //将每个算法的序号用冒泡法排序
Best[j+1][0]=Best[j][0];
Best[j][0]=Temp;
}
}
}
}
//扫描算法(SCAN)
int SCAN(int Han,int x,int y)
{
intj,n,k,h,m,All;
intt=0;
intTemp;
intMin;
intOrder;
intDiscLine[10]; //声明准备要生成的随机磁道号序列的数组
printf("\n随机磁道号序列为:");
GetNum(DiscLine);
Order=1;
k=y;
m=2; //控制while语句的执行,即是一定要使当前磁道向内向外都要扫描到
All=0; //统计全部的磁道数变量
printf("\n按照SCAN算法磁道的访问顺序为:");
for(j=x;j<=y;j++) //寻找与当前磁道号最短寻道的时间的磁道号
{
if(DiscLine[j]>Han) //如果第一个随机生成的磁道号大于当前的磁道号,执行下一句
Temp=DiscLine[j]-Han; //求出临时的移动距离
else
Temp=Han-DiscLine[j]; //求出临时的移动距离
if(Temp { Min=Temp; //Temp临时值赋予Min h=j; //把最近当前磁道号的数组下标赋予h } } All=All+Min; printf("%d",DiscLine[h]); if(DiscLine[h]>=Han) { //判断磁道的移动方向,即是由里向外还是由外向里 Order=0; t=1; } Han=DiscLine[h]; DelInq(DiscLine,h,k); //每个磁道数向前移动一位 k--; while(m>0) { if(Order==1) //order是判断磁盘扫描的方向标签,order是1的话,磁道向内移动 { for(j=x;j<=y;j++) { h=-1; Min=64000; for(n=x;n<=k;n++) //判断离当前磁道最近的磁道号 { if(DiscLine[n]<=Han) { Temp=Han-DiscLine[n]; if(Temp { Min=Temp; //Temp临时值赋予Min h=n; //把最近当前磁道号的数组下标赋予h } } } if(h!=-1) { All=All+Min; //叠加移动距离 printf("%d",DiscLine[h]); Han=DiscLine[h];//最近的磁道号作为当前磁道 DelInq(DiscLine,h,k); k--; } } Order=0; //当完成向内的移动,order赋予0,执行else语句,使磁道向外移动 m--; //向内完成一次,m减一次,保证while循环执行两次 } else //order是0的话,磁道向外移动 { for(j=x;j<=y;j++) { h=-1; Min=64000; for(n=x;n<=k;n++) //判断离当前磁道最近的磁道号 { if(DiscLine[n]>=Han) { Temp=DiscLine[n]-Han; if(Temp { Min=Temp; //Temp临时值赋予Min h=n; //把最近当前磁道号的数组下标赋予h } } } if(h!=-1) { All=All+Min; //叠加移动距离 printf("%d",DiscLine[h]); Han=DiscLine[h]; //最近的磁道号作为当前磁道 DelInq(DiscLine,h,k); k--; } } Order=1; //当完成向外的移动,order赋予1,执行else语句,使磁道向内移动 m--; //向内完成一次,m减一次,保证while循环执行两次 } } NAll=NAll+All; if((y-x)>5) { Best[Jage][1]=All;//Best[][1]存放移动磁道数 Best[Jage][0]=2;//Best[][0]存放算法的序号为:2 Jage++;//排序序号加1 Aver=((float)All)/10;//求平均寻道次数 printf("\n移动磁道数:< %d > ",All); printf("\n平均寻道长度:*%0.2f* ",Aver); } if(t==1) printf("\n磁道由内向外移动"); else printf("\n磁道由外向内移动"); return(Han); } //循环扫描算法(CSCAN) void CSCAN(int Han) { printf("\n注:规定磁头由内向外单方向移动。"); intj,h,n,Temp,m,k,All,Last,i; intMin; inttmp=0; m=2; k=9; All=0; //统计全部的磁道数变量 intDiscLine[10]; //声明准备要生成的随机磁道号序列的数组 printf("\n随机磁道号序列为:"); GetNum(DiscLine); Last=Han; printf("\n按照CSCAN算法磁道的访问顺序为:"); while(k>=0) { for(j=0;j<=9;j++) //从当前磁道号开始,由内向外搜索离当前磁道最近的磁道号 { h=-1; Min=64000; for(n=0;n<=k;n++) { if(DiscLine[n]>=Han) { Temp=DiscLine[n]-Han; if(Temp { Min=Temp; h=n; } } } if(h!=-1) { All=All+Min; //统计一共移动的距离 printf("%d",DiscLine[h]); Han=DiscLine[h]; Last=DiscLine[h]; DelInq(DiscLine,h,k); k--; } } if(k>=0) { tmp=DiscLine[0]; for(i=0;i { if(tmp>DiscLine[i]) tmp=DiscLine[i]; } Han=tmp;//把最小的磁道号赋给Han Temp=Last-tmp;//求出最大磁道号和最小磁道号的距离差 All=All+Temp; } } Best[Jage][1]=All;//Best[][1]存放移动磁道数 Best[Jage][0]=3;//Best[][0]存放算法的序号为:3 Jage++;//排序序号加1 Aver=((float)All)/10;//求平均寻道次数 printf("\n移动磁道数:< %d > ",All); printf("\n平均寻道长度:*%0.2f* ",Aver); } 图6-1最高优先级优先算法 图6-2时间片轮转调度算法 图6-3扫描算法SCAN 图6-4循环扫描算法CSCAN 图6-5各类算法性能比较 这次为期三周的实习,真真正正让我体会到了代码的无限乐趣! 原本打算用这学期新学的java来实现进程调度和磁盘调度,经过不断地磨合修改代码,从课本学习对照,上网查资料,第一周已经过了四天,而我得出了我的进程调度的简化版本,真的很满足当时。可是后来发现磁盘调度在java上有很多地方不熟悉,而且陆陆续续出了很多的问题却找不到解决办法,只好还是从新用更熟悉的c语言来打。在磁盘调度的随机数产生随机序列上遇到了问题,后来使用stdlib.h和time.h头文件中的srand函数和rand函数的配合与time函数解决了这个问题。刚开始先从先来先服务(FCFS)算法开始,陆陆续续攻克很多难关完成了进程调度和磁盘调度等等的问题。最终把界面优化美化后看起来很舒服。 在实习期间,通过和舍友的讨论和上网学习,真正的学到了很多东西。从刚开始的为了完成任务而学习变成了因为兴趣而学习,养成了很好的学习习惯。这次实习令人很满足,也很令人回味。
6.测试数据或截图
6.1设计一:进程调度
6.1.1最高优先级优先算法
6.1.2时间片轮转调度算法
6.2设计五:磁盘调度管理
6.2.1扫描算法SCAN
6.2.2循环扫描算法CSCAN
6.2.3各类算法性能比较
7.心得体会