基于优先级抢占和周期调度的进程调度算法的模拟程序设计

来源:互联网 发布:coc皮卡更新后升级数据 编辑:程序博客网 时间:2024/05/22 12:39

        这是我们系统级编程SSD6的大作业,问题是一个基于优先级抢占和周期调度的进程调度算法的模拟程序。虽然并不太熟悉,没有读过如Linux,Windows,等等操作系统的书籍,在不断的讨论和查阅中也简单地向操作系统底层进程调度小小的探索了一番。还是有一点收获的。在这个问题中,我们完成了设计文档,编码,以及测试(设计了51个功能上的测试用例)。相比之前做过的作业和项目,这回尤其考虑到了数据结构的设计,以及操作系统进程调度的知识,更加富有专业性。因此想与大家一同分享我们这个模拟的进程调度算法的解决过程。也希望各路大牛大神能多多指教。

代码共享:https://github.com/fanxiang090909/OS-Process-Scheduler

问题回顾:

设计一个基于优先级抢占和周期调度的进程调度算法。

进程有两个主要属性:优先级、周期。进程有两种调度模式:优先级抢占和周期调度。

1、优先级抢占:高优先级进程抢占低优先级进程。

2、周期调度:每隔“周期时间”使进程进入“就绪状态”。

Scheduler函数,每隔一定的时间(假设1ms)执行一次,在scheduler中执行RunProcess运行一个进程,执行SuspendProcess挂起一个进程。

需注意的问题:

1、记录每一个新建进程(pcbList),考虑数量越界问题;

2、完善错误判断。



《设计文档——摘要》


一.     总体设计

1.     进程状态

进程共分为运行态、就绪态、阻塞态

New ->Suspend 进程创建进入阻塞态

Suspend ->Ready 接触阻塞态,通过Scheduler_ResumeProcess进入就绪态

Ready ->Running 从就绪态,通过Scheduler_StartProcess进入运行态,

每次前一进程时间片运行结束之后,从就绪队列中选取优先级最高的进程运行

Running -> Ready就绪队列中选取优先级最高的进程运行的同时,低优先级的进程回到就绪状态

Running -> Suspend 被抢占的进程被阻塞,另外等待外部I/O或某些人为交互的进程被挂起,Scheduler_SuspendProcess

 

2.     优先级抢占调度

基于优先级的抢占式调度:

(1)每个任务赋予唯一的一个优先级iPriority;(有些操作系统可以动态地改变任务的优先级,称为动态优先级,这里不做考虑);本系统采用静态优先级调度,在进程创建后优先级不改变。系统中规定进程共有为1-32个优先级别。

(2)假如有几个任务同时处于就绪状态,优先级最高的那个将被运行;

(3)只要有一个优先级更高的任务就绪,它就可以中断当前优先级较低的任务的执行;

3. 周期调度

周期调度是实时调度的一种表现:

根据截止时间的要求将实时任务划分为:

(1)硬实时任务,系统必须满足对截止时间的要求否则可能出现难以预测的结果。

(2)软实时系统,它也联系着一个截止时间,但并不严格,若错过了任务的截止时间,对系统产生的影响不会太大。软实时只能提供统计意义上的实时。

例如,有的应用要求系统在95%的情况下都会确保在规定的时间内完成某个动作,而不一定要求100%。在许多情况下,这样的“软性”正确率已经可以达到用户期望的水平。比如,用户在操作DVD播放机时,只要98%的情况都能正常播放,用户可能就满意了;而发射卫星、控制核反应堆的应用系统,这些系统的实时性必须达到100%,是绝对不允许出现意外。

根据需求,本系统采用软实时系统。

每个实时周期进程的周期通过进程周期iPeriod来表现,若iPeriod == -1为非周期进程

以下为固定优先级的软实时系统运行示例。A进程与B进行具有不同的时间片。

当A进程的优先级高时,在每个周期内先完整运行A的时间片,将B进程时间片划分为小块,在CPU空闲时运行B进程;

当B进程的优先级高时,在每个周期内先完整运行B的时间片, 在CPU空闲时运行A进程;由于可能会出现错过运行的情况,因此为软实时。

 

 

typedef structKPCB

{

   IntiProcessID;

   intiPeriod;    //进程周期(毫秒),-1为非周期进程

   intiPriority;   //优先级。1-32

}KPCB;

 

4.      时间片

时间片即CPU分配给各个程序的时间,每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间,使各个程序从表面上看是同时进行的。

如果在时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。

如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。而不会造成CPU资源浪费。

按道理来讲,时间片的计算应与静态优先级相关:静态优先级越高,时间片越长。时间片的计算有相应的公式。

这里我们简化时间片的计算,规定在没有抢占的情况下:

(1) 对于普通优先级进程:

优先级1-8的进程时间片为5ms

优先级9-16的进程时间片为10ms

优先级17-24的进程时间片为15ms

优先级25-32的进程时间片为20ms

当有抢占的情况时,优先级高的进程会抢占优先级低的进程。

(2) 对于周期进程,时间片设定依据其执行周期

定义如果period / 3小于time_slice的话,将time_slice定义为period / 3;

否则如果这个进程依然有iPriority属性,按普通优先级time_slice计算,防止时间片过长。

当然无论在哪种情况下time_slice一定大于等于1。

 

5.      数据结构

(1)   阻塞态:普通数组suspendList

每次执行检查Scheduler函数时检查阻塞态队列中是否含有仍在等待的进程,将不在等待的进程进入就绪态。本系统中几乎所有进程(除人为外部调用Scheuler_SuspendProcess函数挂起进程的)都不需要额外等待,可直接进入就绪状态。

(2)   就绪态:

周期调度进程:periodList数组

普通优先级调度进程:两个优先级队列activePQ和expiredPQ

除了周期调度进程以外,已运行时间片的进程需要为仍未执行时间片的进程留出CPU,为了保证进程运行,设置两个优先级队列:activePQ和expiredPQ。

activePQ装有还没跑完时间片的活跃任务;expiredPQ装有跑完时间片的任务。当activePQ中没有任务了,就简单地将activePQ与expiredPQ的指针交换过来。

真正代码实现时,为节约存储空间,进程用结构体数组存储,数据结构顺序表或队列均存储数组下标,即都是int型数组:

挂起和周期进程就绪态使用一个顺序表(数组实现),因为这两个状态的进程数量相加必然小于等于最大进程数。声明如下:

int suspendPeriodList[MAX_PROCESSES];

定义被挂起的进程顺序表为suspendPeriodList的逆向使用:

int * suspendList =&suspendPeriodList[MAX_PROCESSES - 1];

定义就绪态进程中的周期进程顺序表为suspendPeriodList的正向使用:

int * periodList = &suspendPeriodList[0];

定义普通优先级队列的就绪态存储在两个优先级队列中,分别用activatePQ与expiredPQ轮流指向:

int PQA[MAX_PROCESSES];

int PQB[MAX_PROCESSES];

程序初始时未被执行的优先级队列,初始指向PQA:

int * activatePQ = PQA;

刚执行完的优先级队列,初始指向PQB:

int * expiredPQ = PQB;

 

6.      资源互斥访问

由于是多线程程序,针对定义的数据结构,再定义三个临界区:

CRITICAL_SECTION pcbListCS;

CRITICAL_SECTION suspendPeriodListCS;

CRITICAL_SECTION activeExpiredPQCS;

 

二.     函数详细设计

(1)     基本数据结构顺序表,优先级队列操作函数

a. 进程结构体数组操作

intpcb_insert(int period,intpriority,int time_slice);

插入新的进程到pcbList中,返回processid号

 

intpcb_delete(int processid);

从pcbList中删除进程,即把标志位pcbListTimeCount置-1

 

b. 计算时间片

intcount_timeslice(int period,int priority);

时间片计算函数,在创建进程和时间片用完重新计时时使用

 

c. 对于顺序表的操作

intlist_insert(int * start,int& length,int direction,intpos,int i);

direction: 对于定义顺序表的正向0或逆向1,

比如suspendList和periodList共同使用suspendPeriodList数组空间。suspendList逆向1,periodList正向0

将元素i插入到顺序表pos位置之后

 

intlist_delete(int * start,int& length,int direction,inti);

将元素i在顺序表中删除

 

intlist_get(int * start,int& length,int direction,inti);

得到顺序表的中值为i的元素,返回其在顺序表中的位置

 

d. 对优先级队列的操作

intPQ_insert(int * pq,int& length,int i);

堆插入,把i插入到pq堆实现的优先级队列中

 

intPQ_removemax(int * pq,int& length);

堆顶删除

 

intPQ_removei(int * pq,int& length,int i);

删除堆中的元素(在挂起进程Scheduler_SuspendProcess中会用到),i是堆中的元素

 

voidPQ_swap(int & i,int& j);

优先级调整之结点交换函数

 

intPQ_compare(int i,int j);

优先级比较函数:比较pcbList数组中下标i和j的进程优先级

返回值:-3进程不存在无法比较;-2不具有可比性;0相等;-1(i优先级小于j);1(i优先级大于j)

 

(2)     调度函数

int Scheduler_CreateProcess(int iPriority,intiPeriod);

创建进程,进入Suspend状态:

null -> suspend

初始化进程结构体;

分配一个iProcessID插入到pcbList数组中;

并初始化进入suspendList。

 

int Scheduler_SuspendProcess(int iProcessID);

挂起进程,进程状态从其他的状态进入Suspend状态:

running -> suspend; ready -> suspend

找到iProcessID的位置,是running,还是在周期进程就绪periodList,或是在普通优先级进程就绪队列中;

相应位置退出

赋给suspendList。

 

int Scheduler_StartProcess(int iProcessID);

开始进程:

running -> ready 的同时 ready -> running

从就绪态到运行态,就是在就绪队列activatePQ中把队列头部元素出队列;

判断iProcessID是什么进程:是running,还是在周期进程就绪periodList,或是在普通优先级进程就绪队列中;

如果它是周期进程

然后判断前一时刻正在运行的进程:

Ø  如果是周期进程且未运行完其时间片,重新计算剩余时间,继续将其进入就绪态;

Ø  如果是周期进程且未运行完其时间片,继续将其进入就绪态;

Ø  如果是普通优先级进程已运行完它的时间片,重新计算剩余时间,把进程插入到expiredPQ中;

Ø  如果是普通优先级进程,且该进程还未运行完它的时间片,依然进入就绪态activatePQ中。

执行SuspendProcess函数打印一句话,把iProcessID加入到running态。执行RunProcess函数。

 

int Scheduler_StopProcess(int iProcessID);

杀死进程:

running -> null; ready -> null; suspend -> null

通过iProcessID号找到进程

去掉就绪态(activatePQ或expiredPQ)中的该进程;

将pcbList中的该进程作用域附默认值0。

 

int Scheduler_ResumeProcess(int iProcessID);

重新开始进程,进程进入ready态,

suspend -> ready

将iProcessID的进程从挂起进程状态中删除,插入就绪态。

判断是否为非周期进程

Ø  若是周期进程,插入priorityList,数组尾插入

Ø  若是普通优先级队列,插入优先级activatePQ队列

 

int Scheduler_ProcessExit(int iProcessID);

进程退出,不引发调度。

 

int Scheduler();

每一毫秒执行一次,调度运行或挂起进程(实际这个Scheduler中不应该挂起进程,顶多是running -> ready):

       ///////////////此处加入调度算法//////////////////////

a.      如果有进程正在运行,记录剩余时间减一。

对于periodList中每一个非正在执行的进程,记录时间片剩余时间(timeCount - 1);

b.      isScheduler标识是否已经执行调度,0表示还未执行。

每次调度执行Scheduler_StartProcess之后,这一毫秒的调度,就不能再继续调度执行Scheduler_StartProcess。

c.       最先处理调度周期进程。

       依次遍历检查periodList数组中的所有元素,看其是否具有执行条件;

       对于periodList队首(数组头有个元素)有进程的情况下,且isScheduler还为0,接下来查看:

Ø  若当前没有进程正在运行;

Ø  若该周期进程期限已到,且正在运行的进程为周期进程,且它已完成时间片;

Ø  若该周期进程期限已到,且正在运行的进程为非周期进程(周期进程优先级比普通优先级进程优先级更高,可抢占);

       以上三种情况满足其中之一时,通过调用Scheduler_StartProcess执行periodList队首的元素, isScheduler置1。

d.      处理全部周期进程结束后isScheduler还为0,开始处理普通优先级进程。

       如果尚未引发Scheduler_Start函数(isScheduler还为0),继续调度普通优先级进程就绪态中的最大优先级进程;

       考察普通优先级进程队列activatePQ中有最大优先级的进程。(若普通优先级进程中没有待运行的进程(正在运行的进程时间片已执行完),但是有已执行完的进程,交换activatePQ和expiredPQ指针及长度值,考察普通进程队列activatePQ中有最大优先级的进程):

Ø  若当前没有进程正在运行,

Ø  若正在运行的已完成时间片

Ø  若该进程相比正在运行的进程有更高的优先级(正在运行的进程一定是普通优先级进程),可抢占

       以上三种情况满足其中之一时,通过调用Scheduler_StartProcess执行activatePQ队首的元素, isScheduler置1。

e.       处理普通优先级进程结束后isScheduler还为0,说明执行正在运行的函数

如果尚未引发Scheduler_Start函数(isScheduler还为0),且有正在运行的进程。如果当前运行进程的时间片已执行完,重新计算它时间片,继续调度自己。

       ///////////////调度算法结束//////////////////////////

 

 

 

三.     测试用例设计

测试用例详见《测试用例.xls

 

【对设计文档v1.0的校正】

1.     Scheduler_ResumeProcess和Scheduler_StartProcess名称对换

由于原有代码注释含义不清楚,导致函数名称理解稍有偏差

Scheduler_ResumeProcess现在理解为从suspend状态转换到ready状态

Scheduler_StartProcess现在理解为运行期间进程状态里running和ready状态的自动调度相互转换。

2.     进程状态转换图的校正

如1所讲,出现一些需要改正的地方,另外Scheduler_StopProcess函数改为为从任何状态中删除,即running-> null; ready -> null; suspend -> null。

3.     时间片取值的校正,之前定义的时间片有的过长,而且对于周期进程来讲,其优先级依据为iPeriod,与iPriority无关。因此增加额外设定的优先级。

4.     调度函数中一开始不再执行Scheduler_ResumeProcess函数。此函数为外部调度时调用。在测试时是测试程序调用,在实际进程调度中是由于外部等待IO等类型的中断引发的Scheduler_SuspendProcess,外部中断结束后再引发Scheduler_ResumeProcess函数。

5.     题目要求Scheduler中调度RunProcess比较容易理解,而SuspendProcess无法理解,因为Scheduler_SuspendProcess的触发是外部引起的,不需要在自身调度函数中执行,让人费劲。因此现在我们的理解是:Scheduler_SuspendProcess是真正意义上的挂起进程,而SuspendProcess只是在执行Scheduler_StartProcess时ready态和running态相互转换事打印一句话,并发真正意义上的挂起。

6.     总体设计中增加了资源互斥访问的设计。

 

代码共享:https://github.com/fanxiang090909/OS-Process-Scheduler


如有转载,请注明出处

【参考】

《关于基于优先级的抢占式调度》

http://blog.csdn.net/zenny_chen/article/details/1889283

《抢占式调度用于周期实时任务》

http://wenku.baidu.com/view/3d266df90242a8956bece409.html

《O(1) Scheduler Notes》

http://fleurer-lee.com/2012/09/23/o1-scheduler-notes.html

原创粉丝点击