程优先级调度算法的分析及模拟实现.docx
《程优先级调度算法的分析及模拟实现.docx》由会员分享,可在线阅读,更多相关《程优先级调度算法的分析及模拟实现.docx(13页珍藏版)》请在冰豆网上搜索。
程优先级调度算法的分析及模拟实现
进程优先级调度算法的分析及模拟实现
摘要:
在操作系统中,进程调度是处理机管理的核心内容,是计算机工作的灵魂。
优先级算法是进程调度的重要算法,分为静态和动态优先级算法,它是解决多个进程无法在一个CPU环境下顺利运行的方法之一。
本论文首先详细分析了优先级调度方算法的原理,并用C++语言实现对多个进程采用抢占式优先级算法的进程调度,模拟动态优先级,逐一将多个进程运行,并且显示出当前时间片下的进程名、优先级运行状态。
关键词:
优先级;进程控制块;进程调度;优先级调度算法;动态优先级调度算法
1.绪论
在计算机系统中,中央处理机CPU是最重要的资源。
每一个提交给计算机的任务都必须使用CPU。
处理机管理的主要任务是对其时间进行分配,也就是按照一定的策略将CPU运行的时间分配给各个用户以满足用户的要求,同时要考虑到充分利用CPU来提高它的效率。
在实时系统里,要使重要紧急的进程被优先调度运行,系统就必须有基于进程优先级的实时调度策略。
进程调度的功能就是按一定策略、动态地把处理机分配给处于就绪队列中的某一进程并使之执行。
根据不同的系统设计目标,可有多种选择进程的策略,每一项任务的执行都必须经过进程调度算法来执行。
进程调度在操作系统中起着非常重要的作用。
2.进程调度
2.1进程调度算法的概念
进程调度算法又称调度策略,它是指在就绪状态的进程中按照什么原则选择一个进程,并把处理机分配给该进程使用。
在多道程序环境下,进程数目往往多于处理机数目,致使他们争用处理机。
这就要求系统能按照某种算法动态地把处理机分配给就绪队列中的一个进程,使之执行。
分配处理机的任务是由进程调度程序完成的。
在进程调度中采用哪种调度算法取决于系统要求达到的目标。
常用的进程调度算法有:
2.1.1先来先服务(FCFS)算法
先来先服务FCFS(First-ComeFirst-Service)算法又称先进先出算法,它是最简单的进程调度算法。
其基本思想是按照各进程进入就绪队列的先后顺序来调度,并分配给处理机执行,即先进入就绪队列的进程,先分配处理机运行。
先来先服务调度算法通常采用非剥夺调度方式。
一旦一个进程占有了处理机,它就一直运行下去,直列该进程完成工作或者因等待某事件而不能继续续运行时才释放处理机。
先来先服务算法如图1.1所示:
图1-1先来先服务算法
优点:
对每个进程来说都是公平的。
对于长作业来说比较有利,但对于短作业来说需要等待很长的时间。
缺点:
FIFO算法从表面上看对所有作业都是公平的,但实际上当一个长作业排在就绪队列前面时,就会使排在其后的许多短作业等待很长的时间。
所以,这种算法不利于短作业,致使短作业的等待时间可能要远远超出它运行的时间。
2.1.2短进程优先调度算法
短进程优先调度即最短CPU执行期优先调度,每次调度时,从就绪队列中找出下一个CPU执行期最短的进程优先调度。
最短CPU执行期优先调度算法的实现过程与先来先服务算法基本一样,只是短进程优先调度算法按照进程要求的运行的时间来调度,运行时间短的进程优先调度。
优点:
对于CPU执行期短的进程来说是有益,但对于CPU执行期长的进程等待要很长的时间。
缺点:
该调度算法的难点在于预测进程的下一个CPU执行期是很不容易的。
2.1.3时间片轮转法RR
时间片轮转调度算法RR(RounRobin)的基本思想是:
把就绪态进程按照先进先出的顺序排成一个环状结构的就绪队列,给队列中的每个进程分配一个时间片。
时间片可以是固定大小的,也可以是根据优先级的不同给以不同的时间片。
当一个进程占用处理机运行完—个时间片时,调度程序便将其插至就绪队列末尾,重新把处理机分配给队列的下一个进程。
如此重复,使就绪队列中的所有进程依次轮流占用处理机运行。
在时间片轮转RR(RounRobin)调度算法中,每个进程按先进先出的原则进入就绪队列,每次调度是用执行指针指向就绪队列队首进程的PCB表,并将其摘下,重布现场,给以时间片让其运行。
显然。
时间片轮转法使用的是剥夺调度方式。
优点:
简单、公平。
缺点:
若时间片取得太长,对于短的交互请求,响应时间太慢,容易引起用户不满;若时间片取得太小,进程切换多,管理开销大,很多CPU时间就被浪费了。
2.1.4优先级调度算法
1.进程优先级
进程优先级是用以描述进程使用处理机的优先级别的整数,是优先级调度算法中进行调度的依据。
优先级可以处理成不变的,称为静态优先级,也可处理成可变的,称为动态优先级。
通常优先级的高低可以由用户自定义,优先数越高,优先级越高,或优先数越高优先级越低。
2.优先级调度算法的基本思想
按某种原则对就绪队列中的每个进程赋予一个优先级,进程调度时则根据进程的优先级确定选择顺序,即把处理机分配给就绪队列中优先级高的进程。
由于进程的优先级别通常用数字表示,所以又称为进程的优先数。
有些操作系统中规定优先数愈小,其优先级愈高,本设计研究的是优先数愈高,优先级愈高的情况。
优先数调度算法一般可以采用可抢占调度方式或非抢占调度方式。
所谓可抢占式,即就绪队列中一旦有优先级高于当前执行进程优先级的进程存在时,便立即发生进程调度,转让处理机。
而非抢占式则意味着:
即使存在优先级更高的进程,当前进程仍然继续占有处理机,直到该进程自己因调用原语操作或等待I/O进入阻塞、睡眠状态,或时间片用完才重新发生调度让出处理机。
本程序研究的是可抢占调度方式。
在采用非抢占调度方式时,只是在一个进程运行结束后才根据进程的优先级来选择下一个进程。
而采用可抢占调度方式时,一旦出现优先级较高的紧迫的或重要的进程时,就剥夺当前运行状态进程使用处理机的权利。
而把处理机分配给紧急进程,保证它们能够优先占用处理机运行。
3.优先级调度算法分为静态优先级调度和动态优先级调度两种算法。
静态优先级调度算法
在静态优先级调度算法中,优先级是在进程创建时确立,确定后运行期间保持不变。
确立依据有:
进程的类型、进程对资源的需求、用户申请的优先级;创建进程时,根据进程的情况或要求赋予进程一个优先级,进程运行过程中优先级不再改变。
每次调度时,就绪队列中优先级最高的进程被率先调度,同级的采用先来先服务(FCFS)。
优点:
简单
缺点:
不能动态反映进程特点,系统调度性能差
动态优先级调度算法
在动态优先级调度算法中,在创建进程时赋予进程一个优先级,进程在运行中优先级根据某种规则或者某种算法在发生变化。
每次调度时,仍然是从就绪队列中选择优先级最高的进程率先调度,同级的采用先来先服务(FCFS)。
优先数的确定:
进程运行时被赋予一个优先数。
运行中根据进程等待时间的长短、执行时间的多少、输入与输出信息量的大小等通过计算得到新的优先数。
优先数计算公式如下:
优先数=固定的时间片—用户定义的优先数(随级产生的优先数)
固定的时间片:
用户自己定义,从开始到结束始终不变。
优先数:
用户定义,每执行一次CPU执行期,优先数都要变化。
总之,可以通过对取不同值来体现系统对哪一类进程采取优先政策。
3.优先级调度算法中的工作机制
3.1进程调度的时机
(1)正在执行的进程运行完毕;
(2)正在执行的进程调用阻塞原语将自己阻塞起来进入等待状态;
(3)正在执行的进程调用了P原语操作,从而因资源不足而被阻塞;或调用了V原语操作激活了等待资源的进程队列;
(4)执行中的进程提出I/O请求后被阻塞;
(5)在分时系统中时间片已经用完;
(6)CPU方式是可剥夺时,就绪队列中的某个进程约优先级变得高于当前运行进程的优先级;
3.2进程调度的过程
(1)进程调度所依赖的数据结构通常是调度队列,由于调度的原因不同,在单处理器系统中设置了多种等待队列;
(2)只有就绪队列中的进程能够获得处理器而最终运行,其他队列中的进程从队列中调度出来后,必须进入就绪队列才能分配处理器;
3.3优先级调度算法的基本思想:
当实时进程被创建进入任务队列时,给进程指定一个优先级,然后系统从任务队列中查找出优先级最高的进程,并交给处理机执行。
采用最高优先数优先的调度算法(即把处理机分配给优先数最高的进程)和先来先服务算法。
本程序采用动态优先级调度算法。
进程的优先数随运行时间的改变而改变,进程刚被创建时,插入就绪队列中,等待被处理机选中,进程每运行一个时间片,优先数值减3,进程还需要运行的时间减1;运行的时间加1,然后调用priority-cal()函数,,按照每个进程的优先数的高低来决定进程在每个时间片的状态,并通过输出函数显示。
每个用来标识进程的进程控制块PCB用结构来描述。
每个进程有一个进程控制块(PCB)表示。
进程控制块是描述进程动态变化过程以及对进程进行控制与管理而设的数据结构。
进程与PCB表一一对应,它是进程是否存在的唯一标志。
进程控制块可以包含如下信息:
进程名、优先数、到达时间、需要运行时间、已用CPU时间、进程状态等等。
本程序中进程的优先数及需要的运行时间可以事先人为地指定(也可以由随机数产生),优先数越大,优先级越高,优先数可以先取值为98,进程每执行一次,优先数减3。
每个进程的状态可以是就绪、运行、或完成(Finish)三种状态之一。
进程的到达时间为进程输入的时间。
进程的运行时间以时间片为单位进行计算。
就绪进程获得CPU后都只能运行一个时间片。
用已占用CPU时间加1来表示。
如果运行一个时间片后,进程的已占用CPU时间已达到所需要的运行时间,则撤消该进程,如果运行一个时间片后进程的已占用CPU时间还未达所需要的运行时间,也就是进程还需要继续运行,此时应将进程的优先数减1(即降低一级)。
然后把它插入就绪队列等待CPU。
进程的运行时间以时间片单位进行计算。
对于遇到优先数一致的情况,采用FCFS的策略解决。
重复以上过程,直到所要进程都完成为止。
4进程调度算法的设计方法
4.1程序设计流程图如下:
图4-1程序设计流程图
4.2程序实现
用宏命令定义常量P_NUM和p_TIME,其中P_NUM表示进程个数的常量,p_TIME表示一个固定的时间片,首先创建进程,用指针q指向结构体数组变量pcb,并在内存中分配地址空间,初始的cputime为0,当创建了P_NUM个进程时,判断那个进程首先被调度,用priority_cal()来实现,在调度前,用finish()函数判断该进程是否已经被撤销即已经完成,若已完成,不用调度;否则找优先级高的进程,p_TIME与进程还需要CPU的时间(needtime)的差值(q->priority=P_TIME-q->needtime)表示进程q的优先级,如果tp(即下个进程)的优先数小于q进程的优先数且未处于完成状态(tppriority&&q->process!
=finish),即进程的优先级高,把q的优先级赋给tp,q赋给t,q的下一个指针赋给q(q=q->next),则用进程调度函数priority_cal()优先调度q,使它处于运行状态,t的优先数减3(t->priority-=3),还需要占用CPU的时间减1(t->needtime--),接着继续执行下一个进程,依次循环。
(1)PCB结构
用结构体变量定义进程控制块的优先级,进程需要占用CPU的时间(cputime),运行后还需要CPU的时间,进程的状态,及指向pcb结构体变量的指针。
具体代码如下:
structpcb
{
charname[4];//进程名存放在字符数组
intpriority;//进程的优先数
intcputime;//进程运行时占用的CPU时间
intneedtime;//进程运行时还需要的CPU时间
stateprocess;//进程的状态
pcb*next;
};
其中用枚举变量定义进程所处状态,具体实现如下:
enumstate
{
ready,//就绪状态,ready=0
execute,//运行状态,execute=1
finish,//结束状态,finish=2
};
(2)创建进程
设计一个函数get_process()来创建一个进程,并把其置于就绪队列中,等待被处理机调度,其中q是指向就绪队列的指针,并给它分配地址空间,q始终指向创建的新进程,t始终指向创建的最后一个进程。
进程的优先级用固定的时间片与它所需要的cpu时间的差表示,具体代码实现如下:
pcb*get_process()//创建进程的函数
{
pcb*q;//指向就绪队列的指针,始终指向新的进程
pcb*t;//始终指向创建的最后一个进程
pcb*p;//指向pcb结构体的指针
inti=0;//初始进程数为0
cout<<"inputnameandtime"<while(i{
q=(structpcb*)malloc(sizeof(pcb));//给指针q分配地址空间
cin>>q->name;//输入进程的名字
cin>>q->needtime;//输入进程还需要的CPU时间
q->cputime=0;//初始时进程运行的CPU时间为0
q->priority=P_TIME-q->needtime;//q的优先级为固定的时间片与q还需要执行的CPU时间之差
q->process=ready;//刚创建时q为就绪状态
q->next=NULL;//就绪队列只有q一个进程
if(i==0)//进程数为0
{
p=q;
t=q;
}//,进程q、进程p、进程t指向同一个地址空间
else
{
t->next=q;//q始终指向新创建的进程
t=q;
}//进程数不为0,t的next指针指向q
i++;//进程数加1
}//while
returnp;
}
(3)进程调度
处于就绪队列中的进程,究竟哪个进程先被处理机调度,进程调度函数priority_cal()来实现,首先在就绪队列中选择优先级高的进程,用finish(pcb*q)函数判断进程是否已经完成,若已经完成,不需调度,否则,找优先级最高的进程使它处于运行状态,用cpuexe(pcb*q)函数来实现。
优先调度,分配CPU,实现的代码如下:
voidpriority_cal()
{
pcb*p;//指向结构体pcb的指针
p=get_process();//用来调度get_process()
intcpu=0;//刚开始创建时未占用CPU,cpu=0
while(!
process_finish(p))//当进程没有结束时执行此循环语句
{
cpu++;//进程每执行一次,cpu的时间片自动加1
cout<<"cputime:
"<cpuexe(p);//调用cpuexe(p)函数
display(p);//显示所有的进程
}
printf("Allprocesshavefinished,pressanykeytoexit");
getch();
}
在上面的调度函数中,调用了一个函数finish(pcb*q),当进程还需要占用CPU的时间为0时,表示该进程已经完成,接着在执行下一个进程。
实现代码如下:
intprocess_finish(pcb*q)
{
intb1=1;
while(b1&&q)
{
if(b1=1&&q->needtime==0;)
q=q->next;//q的下一个指针赋给q
else
b1=0;
returnb1;
}
接着找优先级最高的进程使它处于运行状态,用cpuexe(pcb*q)函数实现此功能,先用条件语句判断该进程是否为结束,若已运行结束,没必要找,否则找优先级高的进程,优先数大的优先,每运行一次优先数要减3,cputime的时间要加1,还需要占用cpu的时间减1,实现过程如下:
voidcpuexe(pcb*q)
{
pcb*t=q;
inttp=0;
while(q)
{
if(q->process!
=finish)
{
q->process=ready;
if(q->needtime==0)
{
q->process=finish;
}
}
if(tppriority&&q->process!
=finish)
{
tp=q->priority;
t=q;
}
q=q->next;
}//若进程q的优先级大于tp的优先级且进程没有完成,则把优先数高的进程赋给tp,q赋给t,接着把q的下一个进程赋给q。
if(t->needtime!
=0)//进程还未执行完毕,还需占用CPU
{
t->priority-=3;//进程的优先数减3
t->needtime--;//还需要的时间减1
t->process=execute;//进程处于运行状态
t->cputime++;//进程已经运行的CPU时间加1
}
}
(4)显示输出
最后需要一个输出函数把各个进程控制块的所有信息显示出来,display()函数可实现此功能,代码如下:
voiddisplay(pcb*p)
{
cout<<"name"<<""<<"cputime"<<""<<"needtime"<<""<<"priority"<<""<<"state"<while(p)
{
cout<name;
cout<<"";
cout<cputime;
cout<<"";
cout<needtime;
cout<<"";
cout<priority;
cout<<"";
switch(p->process)
{
caseready:
cout<<"ready"<caseexecute:
cout<<"execute"<casefinish:
cout<<"finish"<}
p=p->next;
}
}
5.测试结果
进行调试、运行操作后,屏幕上出现了以下的输出界面:
(1)输入“1”,按回车键,则屏幕上出现了:
请输入进程名和时间:
输入5个进程(程序中定义了5个进程):
进程名(name)和进程需要的运行的CPU时间,如下图5-1所示:
图5-1程序输入界面
表示进程aa需要运行的时间为3;bb需要运行的时间为6;进程cc需要运行的时间为8;进程dd需要运行的时间为10;进程ee需要运行的时间为9。
(2)输入“2”后直接退出
(3)输入了进程名和需要运行的时间后,当cputime=1时,输出界面如下图5-2所示:
图5-2程序输出结果
进程的优先数为q->priority=P_TIME-q->needtime,在cputime=1的时间片,进程aa的优先数最大,首先占用CPU,同时aa进程的cputime加1,needtime减1,优先数再减3。
其它进程在就绪队列中继续等待。
(4)在第二个时间片,即cputime=2时,比较进程的优先数,aa的优先数和bb的优先数一样大,遵循先来先服务的原则,aa进程先到,继续占用CPU,同时cputime加1,needtime减1,优先数减3,其它进程控制块内容不发生变化。
输出界面如下图5-3所示:
图5-3程序输出结果
(5)当cputime=3,进程bb的优先数最大,优先级高,被分配CPU,处于运行状态,同时cputime加1,needtime减1,优先数减3;其它进程不变。
输出界面如下图5-4所示:
图5-4程序输出结果
(6)当cputime=4,进程cc的优先数最大,优先级高,首先给分配CPU,
cputime加1,needtime减1,优先数减3;其它进程不变。
输出界面如下图5-5所示:
图5-5程序输出结果
(7)依照上面的结果,程序共要输出36个cputime的时间片,所有的进程全部结束。
6.总结
在调式程序的过程中,出现了一些问题:
问题1:
在voiddisplay_menu()的显示中,必须输两次“1”或“2”,调试结果才会出来,原因出在主函数的输出函数中多输了一个空格,所以必须输入两次k才行,如下:
voidmain()
{
display_menu();
intk;
scanf("%d",&k);
switch(k)
{
case1:
priority_cal();break;
case2:
break;
display_menu();
scanf("%d",&k);
}
}
问题2:
在编写程序时,给tp和q赋值如下:
tp->priority==q->priority,导致类型不匹配,“->”必须指向结构体变量或类,而非整型变量,不能直接赋植。
只需改为tp==q->priority,同类型的才可以。
本程序主要实现了优先级高的进程在就绪队列中首先被调度的策略,优先数越高的进程优先级越高。
使我对进程有了更深的理解,程序的设计也使我很好地对C++语言有了更深一步的掌握,但也存在着一定的缺陷,没有涉及到进程在运行的过程中优先级高的进程抢占CPU时,优先级相同的因为需要时间短而被优先调度的现象,以后有待进一步提高。
参考文献:
[1]孙雅如,房鼎益.丁剑峰计算机操作系统.[J]西安电子科技大学出版,2003.7.
[2]张红光等编UNIX操作系统教程[J].机械工业出版社,2006.01.
[3]王万森.计算机操作系统原理[M].高等教育出版社,2001.
[4](美)Stallings,W.操作系统--内核与设计原理[M].电子工业出版社,2001.
[5]窦延平数据结构与算法C++[M].上海交通大学出版社,2005.5.
[6]张晓莉罗文劼刘振鹏许百成.数据结构与算法[M].机械工业出版社,2003.1.
[7]MauriceJ.Bach著陈葆珏等译.操作系统设计.机械工业出版社,2000
[8]汤子瀛哲凤屏汤小丹编著.计算机操作系统[M].西安电子科技大学出版社,1996.
[9]WilliamStallings.操作系统——精髓与设计原理[J].电子工业出版社,2006.2.