实验目的
1) 加深进程概念的理解;
2) 掌握进程状态转变;
3) 掌握进程调度策略。
实验要求
1) 编写程序完成实验内容;
2) 在实验报告中画出程序流程图;
3) 撰写实验报告。
实验内容
1) 设计可用于该实验的进程控制块,进程控制块至少包括进程号、状态和要求服务时间;
2) 动态或静态创建多个进程;
3) 模拟操作系统四种进程调度算法中的任意一种。
4) 调度所创建的进程并显示调度结果。
实验原理
1.进程概念:
(1) 进程是程序的一次执行。
(2) 进程是一个程序及其数据在处理机上顺序执行时所发生的活动。
(3) 进程是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。
2.进程状态:
进程执行时的间断性决定了进程可能具有多种状态。事实上,运行中的进程可能具有以下三种基本状态。
1) 就绪状态
当进程已分配到除CPU以外的所有必要资源后,只要再获得CPU,便可立即执行,进程这时的状态称为就绪状态。在一个系统中处于就绪状态的进程可能有多个,通常将它们排成一个队列,称为就绪队列。
2) 执行状态
进程已获得CPU,其程序正在执行。在单处理机系统中,只有一个进程处于执行状态;在多处理机系统中,则有多个进程处于执行状态。
3) 阻塞状态
正在执行的进程由于发生某事件而暂时无法继续执行时,便放弃处理机而处于暂停状态,亦即进程的执行受到阻塞,把这种暂停状态称为阻塞状态,有时也称为等待状态或封锁状态。致使进程阻塞的典型事件有:请求I/O,申请缓冲空间等。通常将这种处于阻塞状态的进程也排成一个队列。有的系统则根据阻塞原因的不同而把处于阻塞状态的进程排成多个队列。
3.进程调度算法
进程调度的策略通常有先来先服务算法、时间片轮转算法、最高优先权优先调度算法、最短进程优先调度算法等。
3.1 先来先服务算法
3.1.1 算法思想
该算法思想是按照进入就绪队列的先后次序来分配处理机。FCFS采用非剥夺调度方式,即一旦某个进程占有处理机,就一直运行下去,直到该进程完成其工作或因等待某一事件而不能继续执行时才释放处理机。
3.1.2 算法实现原理图
该算法实现原理图如图1所示。
说明:Ready表示就绪队列,Pi表示新进入队列的进程,Finish表示进程运行完毕退出。
3.1.3 算法分析与评价
①该算法原理简单,易于实现。
②各进程平等竞争。
③由于各进程执行的时间不一样,从而导致相对不公平现象的产生。
④该算法有利于长进程,不利于短进程。
⑤该算法很少用来作为主调度策略,常常用作辅助调度算法使用
3.2最高优先权优先调度算法
3.2.1 算法思想
该算法的基本思想是进程优先权高者优先调度,是一种最常用的进程调度算法。该算法的关键是如何确定优先数。通常确定优先数的方法有两种,即静态法和动态法。
静态优先权是在创建进程时确定的,其运行特征是优先数确定之后在整个进行运行期间不再改变。确定静态优先权的依据有进程的类型、进程所使用的资源、进程的估计运行时间等因素。进程所申请的资源越多,估计的运行时间越长,进程的优先权越低。进程类型不同,优先权也不同,如系统进程的优先权高于用户进程的优先权。
动态优先权是指在创建进程时,其运行特征是根据系统资源的使用情况和进程的当前特点确定一个优先权,在进程运行过程中再根据情况的变化调整优先权。动态优先权一般根据进程占有CPU时间的长短、进程等待CPU时间的长短等因素确定。占有处理机的时间越长,则优先权越低;等待时间越长,则优先权越高。
3.3.2 算法分析与评价
①静态优先级调度算法实现较为简单,但不能反映系统以及进程在运行过程中发生的各种变化。而动态优先级法可以满足这个方面的需要。
②动态优先级调度算法的性能一般介于时间片轮转算法和先来先服务算法之间。
3.3时间片轮转调度算法
3.3.1 算法思想
该算法思想是使每个进程在就绪队列中的等待时间与享受服务的时间成比例。即将CPU的处理时间分成固定大小的时间片,如果在执行的一个进程把它分给它的时间片用完了,但任务还没有完成,则它也只能停止下来,释放它所占的CPU资源,然后排在相应的就绪队列的后面去。
3.3.2 算法实现原理图
该算法实现原理图如图2所示
说明:Ready表示就绪队列,Pi表示新进入队列的进程,Finish表示进程运行完毕退出。Not Finish表示分配给某进程的时间片已用完但任务还没有完成,从而插入到Ready队列尾部。
3.3.3 算法分析与评价
①时间片的长度选择比较困难
时间片的长度选择比较困难是因为时间片长度的选择直接关系到系统开销和进程的响应时间。如果时间片长度过短→导致调度程序剥夺处理器的次数增加→进程的上下文切换的次数增加→系统的开销也加重;如果时间片长度过长,长到能使就绪队列中所需要执行时间最长的进程执行完毕→轮转法就变成了FCFS算法→FCFS短处不足就显示出来了。
又因为CPU的整个执行时间=各进程执行时间之和+系统开销(各进程切换所花费的CPU时间之和,假定存储开销忽略不计)。即,。因此,时间片的长短通常需要由以下因素确定:
↙系统的响应时间。
↙就绪队列中的进程数。
↙进程的切换时间。
↙计算机的处理能力,计算机的速度越高,时间片就可越短。
②时间片长度选择的动态性
以上仅仅作了静态分析,通常情况下,就绪队列里地进程个数是不断变化的。因此,每一次的调度都需要计算新一轮的时间片长度,不能用固定的时间片长度来进行所有进程的轮转执行。
③该算法的扩充——多级反馈轮转法
在上面的算法中,未对就绪队列中的进程加以条件分析(即进入就绪队列的因素),由于进入就绪队列的原因不一样,要求占用处理机的紧急程度也不一样。主要因素有:
↙分给该进程的时间片用完,但进程还未完成。
↙分给其时间片未用完,而发生了I/O等请求后由阻塞态转变成就绪态。
↙新的进程进入。
因此,根据紧急程度的不一样,建立多个就绪队列,同时赋予不同的的优先级,优先权高的就绪队列优先执行,同一就绪队列中,优先级相同,按照先来先服务进行调度,运行一个给定的时间片,如果没有执行完成则转入到相应的就绪队列中去(运行一次,优先级降低一个等级,等待一个时间片,则优先级升高一个等级)。其实现原理图如图3所示。
3.4 短进程优先调度算法
3.4.1 算法思想
该算法的基本思想是从就绪队列(内存)中选择一个估计运行时间最短的进程,将处理机分配给它。
3.4.2 算法分析与评价
①该算法能有效降低作业的平均等待时间,提高系统吞吐量。
②对长进程不利,甚至导致长期得不到处理。
③该算法完全未考虑进程的紧迫程度。
④进程的长短通常由某种策略估计提供,不能做到真正的短进程优先。
以下代码在VC++6.0运行通过
#include"stdio.h"
#include"stdlib.h"
#include "dos.h"
#include "conio.h"
#include "time.h"
#include"windows.h"
#define READY 1
#define RUN 2
#define BLOCK 3
typedef struct pcb
{
int num;
struct pcb *next;
int priority;
int cputime;
int state;
}pcb;/*用此结构体来模拟一个进程*/
struct pcb *head;
struct pcb *run;
pcb *creatpc(int n)/*此函数用于创建进程队列*/
{
int i=1;
pcb *head,*p,*q;
srand(time(0));/*随机函数的初始化*/
head=(pcb *)malloc(sizeof(pcb));/*创建一个空表头*/
p=head;
for(i=1;i<=n;i++)/*用循环来创建指定个结点*/
{
q=(pcb *)malloc(sizeof(pcb));
p->next=q;
q->num=i;
q->next=NULL;
q->priority=rand()%10;/*随机产生优先级*/
q->cputime=rand()%10;/*随机产生运行时间*/
q->state=READY;
p=q;
}
return head;/*返回表头指针*/
}
pcb *getmaxpriority(struct pcb *head)/*此函数用来挑选一个优先级最大的进程来执行*/
{
struct pcb *p,*q;
int max;
p=head->next;
max=p->priority;/*初始max为队首结点的优先级*/
q=p;
while(p)
{
if(p->priority>max)/*逐一比较,选出优先级最大的结点*/
{max=p->priority;
q=p;}
p=p->next;
}
return q;
}
void deletepc(struct pcb *head,struct pcb *run)/*此函数用来将运行完的进程删除出进程队列*/
{
struct pcb *q=head;
while(q->next)/*扫描进程队列,找到执行完了的进程*/
{
if(q->next->num==run->num)/*判断是不是已完成的进程*/
{
if(run->next!=NULL)
q->next=run->next;
else q->next=NULL;
free(run);/*释放申请的空间*/
return;
}
q=q->next;
}
}
void control()/*此函数是用来控制各个进程的执行和调度*/
{
struct pcb *p;
/*run=head->next;初始让第一个进程运行*/
run=getmaxpriority(head);
run->state=RUN;
while(run)
{
if(run->cputime>0)
{printf("pcb%d is running.priority=%d,cputime=%d\n",run->num,run->priority,run->cputime);
printf("Ready list:");/*显示整个就绪队列*/
p=head->next;
while(p)
{
if(p!=run)
printf("pcb%d,priority=%d,cputime=%d ",p->num,p->priority,p->cputime);
p=p->next;
}
printf("\n");
Sleep(5);/*模拟进程运行*/
run->cputime--;/*进程需要时间减一*/
run->priority--;/*进程优先级减一*/
run=getmaxpriority(head);/*从进程队列中挑选一个优先级最大的进程来运行*/
run->state=RUN;
}
else
{ printf("pcb%d is finished.\n",run->num);
Sleep(2);//这里的S大写
deletepc(head,run);/*删除该结点*/
if(head->next!=NULL)/*判断进程队列是不是为空*/
{run=head->next;
run->state=RUN;}
else
{printf("All progresses are done.\n");
return;}
}
}
}
void main()
{
int n;
int flag=1;
printf("Enter the number of the progresses:");
scanf("%d",&n);/*输入要创建的进程的数量*/
head=creatpc(n);/*创建进程队列,将链表的表头赋给head指针*/
run=head->next;/*run指针指向正在运行的进程的pcb*/
while(run)
{
printf("num: %d ,priority: %d ,need cputime: %d \n",run->num,run->priority,run->cputime);
run=run->next;
} /*将刚创建的进程队列打印出来*/
while(flag)/*由flag的值判断是否继续执行control()函数*/
{
if(head->next)/*判断进程是否完成*/
control();
else flag=0;
}
getch();
}