实验二、进程调度模拟实验
一、实验目的:
本实验模拟在单处理机环境下的处理机调度,帮助理解进程调度的概念,深入了解进程控制块的功能,以及进程的创建、撤销和进程各个状态间的转换过程。
二、实验内容:
- 进程调度算法:采用最高优先数优先的调度算法、先来先服务算法、SJF和多级反馈调度算法。
- 每个进程有一个进程控制块(PCB)表示。进程控制块可以包含如下信息:进程名、优先数、到达时间、需要运行时间、已用CPU时间、进程状态等等。进程的优先数及需要的运行时间可以事先人为输入(也可以由随机数产生)。进程的到达时间为进程输入的时间。 进程的运行时间以时间片为单位进行计算。
- 就绪进程获得CPU后都只能运行一个时间片。用已占用CPU时间加1来表示。如果运行一个时间片后,进程的已占用CPU时间已达到所需要的运行时间,则撤消该进程,如果运行一个时间片后进程的已占用CPU时间还未达所需要的运行时间,也就是进程还需要继续运行,此时应将进程的优先数减1(即降低一级),然后把它插入就绪队列等待CPU。
- 每个进程的状态可以是就绪W(Wait)、运行R(Run)、或完成F(Finish)三种状态之一。
三、实验要求:- 每进行一次调度程序都打印一次运行进程、就绪队列、以及各个进程的PCB,以便进行检查。
- 对同一组进程的各种调度算法分别计算平均周转时间和平均带权周转时间。
第一步
关于头文件:
#include<stdio.h>
#include<cstdlib>
#include<iostream>
#define N 3
我们这里默认最大允许三个进程,您也可以自己改成5,这边建议用3就好,程序还有一些不为人知的秘密
命名空间这可以学习一下c++,与本算法无太大关系所以在此不讲述
using std::cout;
using std::cin;
using std::endl;
接下来我们开始我们最初的结构的定义:
进程pcb的定义
因为是进程调度,所以一定涉及到了进程pcb的调用,跟书一样的定义,我们直接给出
typedef struct pcb{
char pname;//进程名
int n;//优先级
int atime;//进程到达时间
int rtime;//运行时间
int utime;//已用时间
char status;//进程的状态,w等待,r运行
pcb* next;
}pcb,*PCB;
PCB zpcb[N];//装载着每个pcb的指针数组
int ctime;//当前运行时间用于先来先服务
int num;//处理机要处理的进程的个数
float time1,time2;//1为平均周转:完成减去到达,2为带权周转:平均除以运行
PCB head=(PCB)malloc(sizeof(pcb));
zpcb是一个指针数组你也可以想象成进程队列,这是根据算法的各个形式进行增删的,time1是平均周转时间,用完成减去到达就可以,time2是平均带权周转时间,用time1除以进程运行时间即可,这里可以看后面各个调度算法的应用,很容易理解。
假设我们的调度算法都实现了,首先我们需要定义我们的参数,创建pcb,装载pcb数组,进程个数等等####
那么我们首先定义一个方法,createProcess
在创建之前我们需要一个思考,我们的进程的初始状态应该都置于wait,我们可以将所有的进程放置在我们之前的head队列中,方便后续的使用。我们需要输入我们的进程名等pcb结构中需要的值,最后用一个队列进行装载,这里只给出一些提示,读者自己创造。
int createProcess(){
cout<<"请输入您需要运行的进程数目(max:3)";
cin>>num;
cout<<"输入 "<<num<<"个的进程名、优先级、到达时间、运行时间(空格隔开,进程直接回车隔开)
注意:进程的到达时间必须递增,首个的到达时间为0"<<endl;
PCB p = head;
for(int i=0;i<num;i++){
希望读者可以自己去实现,最后我们需要告诉我们的调度算法用户一共创建了几个进程,所以num应该作为返回值进行一个返回,这里其实有一个bug,我的到达时间是我自己控制的,这是一种纯粹的理想状态。希望有人后来居上。
既然已经创建好了我们的进程,接下来是一种关于使用进程调度算法了,我们可以设置一个while循环,让用户去选择,while中可以搭配swtich语句这种简单的留给读者####
在这里面你肯定会这样想,我们是不会想调用一个算法就退出,我们是想再调度几个,用原来的输入,所以我们如果已经动了我们的head,那么这里面的值都要一个归零,就会很麻烦,这里你会想起来我们的zpcb数组,它可以代替我们的head,这里我们需要一个clean函数,让使用时间归零,让状态至为w,这个clean叫进程状态初始化。在此给出代码
void clean(){//清除状态,使用其他的调度算法
cout<<"进程信息初始化:"<<endl;
PCB pc=head->next;
int i;
while(pc)
{
i++;
pc->utime=0;
pc->status='W';
pc=pc->next;
}
}
第一个调度算法:PSA高优先数###
在每次传参我们只需要传入我们main中的num,其余的为全局变量,
这里直接给出定义
void PSA(int num){
//int n=num;
PCB pc=head->next;
int time,pr[N];//pr数组记录了各个进程的优先级
time1=0;
time2=0;
首先我们将n暂替我们的num,pc指针指向我们的首进程,pr数组用于记录优先级,time在前期可以充当一个临时的变量,后期可以充当我们的系统时间,两个平均**时间我们可以让它们进行一个清零。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
第一步我们得让我们的pr数组和我们的zpcb数组记录下进程的优先级,和clone整个进程,这就是我们上面说的作用,关于zpcb数组,一个参与调度的进程集合,ok话不多说我们给出代码:
for(time=0; time<N; time++)
if(pc)
{
zpcb[time]=pc;
pr[time]=pc->n;
pc=pc->next;
}
接下来我们找一找有没有与我们进程同时到达的进程,又要使用我们的工具变量time,定义一个for,这里我们的zpcb已经有了我们所有的进程,所以我们直接zpcb[0]->atime==zpcb[time]->atime,看看我们的for循环运行与否,如果我们的time大于了1,说明在此刻已经有进程同时到达了,这时候我们要去比较它们的优先级决定谁位于对首,后面的运行中我们肯定是不止一次要进行一个比较优先级的操作,我们为什么不将这个封装成函数呢,既然如此,干就完事了。
sort函数:比较优先级,在同时为第一时间到达的进程中将优先级高的放置与首,进程优先级n越大就说明优先级越高
void sort(int time)
{
int i=1;
while(zpcb[i]&&time>=zpcb[i]->atime)i++;
for(int j=0; j<i-1; j++)
for(int k=j+1; k<i; k++)if(zpcb[j]->n<zpcb[k]->n)
{
PCB c=zpcb[j];
zpcb[j]=zpcb[k];
zpcb[k]=c;
}
}
这里直接给出代码,因为太简单,就用了一个排序默认你们都可以看懂
接下来我们回到我们的psa调度算法,这里给出了之后我们的工具变量time可以做回本职工作了,将第一个的到达时间设置为time,也就是zpcb[0]—>atime;
接下来开始我们的调度,我们首先用一个大while,条件是num>0,
我们需要考虑几种情况,第一种情况,//如果第二个进程还未到达,第一个进程就开始运行//优先级减一,运行时间和总的时间加1,状态置为run
第二种情况,第二个进程到达了
这里是第一种情况
大致思想:首先我们让第一个进程的utime++ 系统时间++ 在这个同时让我们的优先级-- 因为已经进行了一次对cpu的使用,状态置为r 这时候我们找一找有没有到达的进程,比较优先级,输出正在运行的进程,在这个同时我们也要将优先级最高且到达的进程至于第一个,输出一下我们的就绪队列,也就是已经到达的进程,已经就绪的进程,看看有没有完成的进程,如果有就状态置为f计算两个时间,将zpc中的第一个进程覆盖掉因为已经调度完了,如果没有完成,我们就让它置为等待,进行新的判断。
这里我们还有一个问题就是我们的进程重新的进行了排序输出了就绪队列,我们同时也需要输出当前所有进程的状态,因为并不是所有的进程都是就绪了,因此这里需要输出,在后面我们进行了换位也好,调度完成了都是需要使用,这里就需要一个show,展示当前所有进程的状态,这里直接给出
void show(bool n)
{
for(PCB p=head->next;p!=NULL;p=p->next)
{
if(n)
cout<<"进程名:"<<p->pname<<" 优先级数:"<<p->n<<"到达时间:"<<p->atime<<" 运行时间:"<<p->rtime<<" 已用运行时间:"<<p->utime<<" 状态:"<<p->status<<endl;
else cout<<"进程名:"<<p->pname<<" 到达时间:"<<p->atime<<" 运行时间:"<<p->rtime<<" 已用运行时间:"<<p->utime<<" 状态:"<<p->status<<endl;
}
cout<<"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"<<endl;
}
因为大致思想描述可能需要代码参考,也在这附上我的代码
if(zpcb[1]&&time<zpcb[1]->atime||!zpcb[1])//如果第二个进程还未到达,第一个进程就开始运行
//优先级减一,运行时间和总的时间加1,状态置为run
{
zpcb[0]->utime++;
time++;
if(zpcb[0]->n!=0)zpcb[0]->n--;
zpcb[0]->status='R';
int i;
for(i=1; zpcb[i]; i++)if(time>=zpcb[i]->atime);
else break;//找看有没有比第二个进程先到达的进程
for(int j=1; j<i-1; j++)//如果优先级比第二个的高就换位
for(int k=j+1; k<i; k++)if(zpcb[j]->n<zpcb[k]->n)
{
PCB t=zpcb[j];
zpcb[j]=zpcb[k];
zpcb[k]=t;
}
printf("正在运行的进程:%c ",zpcb[0]->pname);
printf("就绪队列:");
for(int i=1; zpcb[i]; i++)
if(time>=zpcb[i]->atime) printf("%c ",zpcb[i]->pname);//输出到达时间到了系统时间的进程,为就绪队列
printf("
");
show(true);
if(zpcb[0]->utime==zpcb[0]->rtime)//如果当前进程运行完了状态为F,平均和带权根据公式,将后一个覆盖前一个
{//再去查看有与系统时间time相同的到达时间或者在之前到达,比较优先级
zpcb[0]->status='F';
time1+=time-zpcb[0]->atime;
time2+=(double)(time-zpcb[0]->atime)/zpcb[0]->utime;
int i;
for( i=1; zpcb[i]; i++)zpcb[i-1]=zpcb[i];
zpcb[i-1]=NULL;
num--;
sort(time);
}
else//如果没运行完就等待,根据优先级再度确定下一个要运行的进程
{
zpcb[0]->status='W';
if( zpcb[1]!=NULL) sort(time);
}
}
希望读者能够自主的编码,
第二个情况是第二个进程到达了,比较到达了之后的优先级分为大于和小于第一个进程,后面与前面一样
else//第二个进程到达了
{
if( zpcb[1]&& zpcb[0]->n>= zpcb[1]->n||! zpcb[1])//如果第二个进程也到达了,且优先级小于第一个进程
{
zpcb[0]->utime++;
time++;
if( zpcb[0]->n!=0) zpcb[0]->n--;
zpcb[0]->status='R';
}
else//如果优先级比第一个进程高,那么第一个进程等待,将其换位置,运行
{
zpcb[0]->status='W';
sort(time);
zpcb[0]->utime++;
time++;
if( zpcb[0]->n!=0) zpcb[0]->n--;
zpcb[0]->status='R';
}
int i;
for(i=1; zpcb[i]; i++)if(time>= zpcb[i]->atime);
else break;
for(int j=1; j<i-1; j++)
for(int k=j+1; k<i; k++)if( zpcb[j]->n< zpcb[k]->n)
{
PCB t= zpcb[j];
zpcb[j]= zpcb[k];
zpcb[k]=t;
}//与上一个一样,下一个进程到达了,比较优先级
printf("正在运行的进程:%c ", zpcb[0]->pname);
printf("就绪队列:");//输出就绪队列,判断是否完成
for(int i=1; zpcb[i]; i++)if(time>= zpcb[i]->atime)printf("%c ", zpcb[i]->pname);
printf("
");
show(true);
if( zpcb[0]->utime== zpcb[0]->rtime)
{
zpcb[0]->status='F';
time1+=time- zpcb[0]->atime;
time2+=(double)(time- zpcb[0]->atime)/ zpcb[0]->utime;
int i;
for( i=1; zpcb[i]; i++) zpcb[i-1]= zpcb[i];
zpcb[i-1]=NULL;
num--;
sort(time);
}
else
{
zpcb[0]->status='W';
if( zpcb[1]!=NULL)sort(time);
}
}
}
最后大while结束,输出最终的进程的信息和两个时间time1 time2 清楚状态,输出进程队列的初始队列,等待下一轮新的选择。PSA到此结束
接下来是FCFS先来先服务
基本思想:在这已经没有优先级的概念了,定义好pc指针和time,(这里我们没有改变其他的,可以直接用head队列,也就是pc。)记得初始化两个时间,我们直接一个大while进行使用,让系统时间和utime进行++就好,每进行一次输出就绪队列,发现有完成了也是覆盖,最后输出最终情况,清零,展示原始状态,这里比较的简单我就不贴代码了,读者自行编码。
短进程优先
//短进程优先
//当做了几个之后,其实是差不多的,这里的优先级是作业的使用时间
//即utime,刚开始第一个我们判断是否存在且第二个是否到达
//然后分情况进行处理,跟前面的代码有重合,其实可以封装一下算时间的等等,
//
基本思想:这里跟前面一样进行初始化,用zpcb数组,一个while,接下来想想会有几种情况,第一种后面的进程还没来,第二种后面的进程来了。
假如没来,直接让它完成,不用++直接+rtime。输出各个队列,覆盖,状态置为f,两个time,num--
如果来了,比较谁的rtime小,置于第一个,接下来是一样的操作,就不叙述了,这里附上代码,留给读者进行参考,很简单希望自己编码。
void SJF(int num){
int n = num;
PCB pc=head->next;
int time=0;
time1=time2=0;
for(time=0;time<N;time++)if(pc){
zpcb[time]=pc;
pc=pc->next;
}
time = zpcb[0]->atime;
while(num>0){
if(zpcb[1]&&time<zpcb[1]->atime||!zpcb[1]){
zpcb[0]->utime+=zpcb[0]->rtime;
time+=zpcb[0]->rtime;
zpcb[0]->status='R';
printf("正在运行的进程:%c ",zpcb[0]->pname);
printf("就绪队列:");
for(int i = 1;zpcb[i]&&time>zpcb[i]->atime;i++)
cout<<zpcb[i]->pname;
cout<<endl;
show(false);
if(zpcb[0]->utime==zpcb[0]->rtime){
zpcb[0]->status='F';
time1+=time-zpcb[0]->atime;
time2+=(float)(time-zpcb[0]->atime)/zpcb[0]->utime;
int i;
for(i = 1; zpcb[i];i++)zpcb[i-1]=zpcb[i];
zpcb[i-1]=NULL;
num--;
}
}
else{
int i;
for(i=1;zpcb[i];i++)if(time>=zpcb[i]->atime);
else break;
for(int j=0;j<i-1;j++)
for(int k = j+1;k<i;k++)if(zpcb[j]->rtime>zpcb[k]->rtime){
PCB t=zpcb[j];
zpcb[j]=zpcb[k];
zpcb[k]=t;
}
zpcb[0]->utime+=zpcb[0]->rtime;
time+=zpcb[0]->rtime;
zpcb[0]->status='R';
cout<<"正在运行的进程:"<<' '<<zpcb[0]->pname;
printf("就绪队列:");
for(i= 1;zpcb[i]&&time>zpcb[i]->atime;i++)
cout<<zpcb[i]->pname;
cout<<endl;
show(false);
if(zpcb[0]->utime==zpcb[0]->rtime){
zpcb[0]->status='F';
time1+=time-zpcb[0]->atime;
time2+=(float)(time-zpcb[0]->atime)/zpcb[0]->utime;
num--;
}
}
}
cout<<"最终的进程信息:"<<endl;
show(false);
clean();
cout<<"平均周转时间:"<<(float)(time1/n)<<" 平均带权周转时间:"<<(float)(time2/n)<<endl<<endl;
show(false);
}
多级反馈MFQ 这个算法我的代码有问题在此只叙述思想
关于初始化这里可以给出:
void MFQ(int num){
int n=num;
PCB head1[N],head2[N],head3[N],pc=head->next; //三个优先级队列
int time=pc->atime;
time1=0;
time2=0;
for(int i=0; i<N; i++)head1[i]=head2[i]=head3[i]=NULL;//每个就绪清空
head1[0]=pc;//放入第一个
pc=pc->next;
接下来就是while分情况,后续没到达,后续到达。
后续没到达也需要分情况,首先就是看看优先级最高到最低,从h0(head0)到h3中哪一个是有进程的,让它进行一个使用,并输出其他的两个队列的进程,如果三个都没有,我们将pc指向的进程进行一个放入h1中,看看情况如何,最后展示当前进程的一个情况。。。看当前的进程队列0-2中是否有已经完成进程,有就状态置为f,覆盖,num--,计算时间12,没有就投放下一级队列如果head3中就将其放到最后。
第二种情况中,我们需要判断pc是不是结束标志,如果不是加入head1后,继续运行一个时间片,输出情况,判断是否执行完,如果没有后续进程,将每个队列中的进程由0-2的优先级进行执行,判断是否完成,丢入下级。
最后输出情况
基本的思想我都已经实现,但是会有一些问题,希望写出代码的同志评论区走一走,最后我们的代码就编完了,希望多级反馈能够有一个更好的代码,我的代码就不附上了。。。
完结撒花!!!