实验二、进程调度模拟实验

一、实验目的:

本实验模拟在单处理机环境下的处理机调度,帮助理解进程调度的概念,深入了解进程控制块的功能,以及进程的创建、撤销和进程各个状态间的转换过程。

二、实验内容:

  1. 进程调度算法:采用最高优先数优先的调度算法、先来先服务算法、SJF和多级反馈调度算法。
  2. 每个进程有一个进程控制块(PCB)表示。进程控制块可以包含如下信息:进程名、优先数、到达时间、需要运行时间、已用CPU时间、进程状态等等。进程的优先数及需要的运行时间可以事先人为输入(也可以由随机数产生)。进程的到达时间为进程输入的时间。 进程的运行时间以时间片为单位进行计算。
  3. 就绪进程获得CPU后都只能运行一个时间片。用已占用CPU时间加1来表示。如果运行一个时间片后,进程的已占用CPU时间已达到所需要的运行时间,则撤消该进程,如果运行一个时间片后进程的已占用CPU时间还未达所需要的运行时间,也就是进程还需要继续运行,此时应将进程的优先数减1(即降低一级),然后把它插入就绪队列等待CPU。
  4. 每个进程的状态可以是就绪W(Wait)、运行R(Run)、或完成F(Finish)三种状态之一。

    三、实验要求:
  5. 每进行一次调度程序都打印一次运行进程、就绪队列、以及各个进程的PCB,以便进行检查。
  6. 对同一组进程的各种调度算法分别计算平均周转时间和平均带权周转时间。

第一步

关于头文件:

    #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<<"个的进程名、优先级、到达时间、运行时间(空格隔开,进程直接回车隔开)\n注意:进程的到达时间必须递增,首个的到达时间为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\t",zpcb[0]->pname);
printf("就绪队列:");
for(int i=1; zpcb[i]; i++)
if(time>=zpcb[i]->atime) printf("%c ",zpcb[i]->pname);//输出到达时间到了系统时间的进程,为就绪队列
printf("\n");
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\t", zpcb[0]->pname);
printf("就绪队列:");//输出就绪队列,判断是否完成
for(int i=1; zpcb[i]; i++)if(time>= zpcb[i]->atime)printf("%c ", zpcb[i]->pname);
printf("\n");
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\t",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<<"正在运行的进程:"<<'\t'<<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的优先级进行执行,判断是否完成,丢入下级。

最后输出情况

基本的思想我都已经实现,但是会有一些问题,希望写出代码的同志评论区走一走,最后我们的代码就编完了,希望多级反馈能够有一个更好的代码,我的代码就不附上了。。。

完结撒花!!!

OS_进程调度:C++实现的更多相关文章

  1. 《Linux内核设计与实现》读书笔记 第四章 进程调度

    第四章进程调度 进程调度程序可看做在可运行太进程之间分配有限的处理器时间资源的内核子系统.调度程序是多任务操作系统的基础.通过调度程序的合理调度,系统资源才能最大限度地发挥作用,多进程才会有并发执行的 ...

  2. Linux2.6内核--进程调度理论

    从1991年Linux的第1版到后来的2.4内核系列,Linux的调度程序都相当简陋,设计近乎原始,见0.11版内核进程调度.当然它很容易理解,但是它在众多可运行进程或者多处理器的环境下都难以胜任. ...

  3. Linux0.11内核--进程调度分析之2.调度

    [版权所有,转载请注明出处.出处:http://www.cnblogs.com/joey-hua/p/5596830.html ] 上一篇说到进程调度归根结底是调用timer_interrupt函数, ...

  4. Linux0.11内核--进程调度分析之1.初始化

    [版权所有,转载请注明出处.出处:http://www.cnblogs.com/joey-hua/p/5596746.html ] 首先看main.c里的初始化函数main函数里面有个函数是对进程调度 ...

  5. linux 内核学习之八 进程调度过程分析

    一  关于进程的补充 进程调度的时机 中断处理过程(包括时钟中断.I/O中断.系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule() ...

  6. Linux内核分析——理解进程调度时机跟踪分析进程调度与进程切换的过程

    20135125陈智威 +原创作品转载请注明出处 +<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验 ...

  7. <Operating System>进程调度

    在多道程序环境下,进程数目往往多于处理机数目,致使它们争用处理机.这就要求系统能按某种算法,动态地把处理机分配给就绪队列中的一个进程,使之执行.分配处理机的任务是由进程调度程序完成的. 三级调度 一个 ...

  8. Linux内核分析之理解进程调度时机跟踪分析进程调度与进程切换的过程

    一.原理分析 1.调度时机 背景不同类型的进程有不同的调度需求第一种分类I/O-bond:频繁的进行I/O:通常会花费很多时间等待I/O操作的完成CPU-bound:计算密集型:需要大量的CPU时间进 ...

  9. 操作系统开发系列—13.i.进程调度 ●

    上面的三个进程都是延迟相同的时间,让我们修改一下,尝试让它们延迟不同的时间. void TestA() { int i = 0; while (1) { disp_str("A." ...

随机推荐

  1. css3新选择

    官方解释: [attribute^=value],a[src^="https"],选择其 src 属性值以 "https" 开头的每个 <a> 元素 ...

  2. Python的自定义属性访问跟描述器以及ORM模型的简单介绍

    一 . 自定义属性访问 1.__getattr__ 作用:当我们访问属性的时候,如果属性不存在(出现AttrError),该方法会被触发. 2.__getattribute__ 作用:访问属性的时候, ...

  3. MySQL/MariaDB随笔一

    1.yum 安装后先跑一下系统自带的安全脚本,否则数据库很不安全,任何人都可以登录 [root@xixi ~]# mysql_secure_installation NOTE: RUNNING ALL ...

  4. eatwhatApp开发实战(九)

    之前我们为app在item项上添加了点击出现修改对话框,对店名进行修改的功能,其中我们会发现我们点击item和点击item上的按钮会有点击冲突.这次我们来修正下这个问题,同时介绍item项的长按点击O ...

  5. [Python番外]001.用Sublime开发Python

    用Sublime开发Python 准备 安装Package Control插件 安装Python插件 Python环境配置 修改快捷键 准备 安装Python 详见 Python准备 下载Sublim ...

  6. Elasticsearch系列---生产集群部署(上)

    概要 本篇开始介绍Elasticsearch生产集群的搭建及相关参数的配置. ES集群的硬件特性 我们从开始编程就接触过各种各样的组件,而每种功能的组件,对硬件要求的特性都不太相同,有的需要很强的CP ...

  7. GitHub+jsDelivr+PicGo 打造稳定快速、高效免费图床

    标题: GitHub+jsDelivr+PicGo 打造稳定快速.高效免费图床 作者: 梦幻之心星 347369787@QQ.com 标签: [GitHub, 图床] 目录: 图床 日期: 2019- ...

  8. Java的四种权限修饰符

    private:仅对本类可见 缺省(不需修饰符):对本包可见 protected:对本包及所有子类可见 public:对所有类可见 修饰符: * 权限修饰符:private,默认的,protected ...

  9. UPX的使用

    UPX是一个通用可执行文件压缩器,由于其具有: 压缩率高:压缩效果优于zip/gzip: 解压速度快:在奔腾133上即可达到大约10MB/秒: 压缩的可执行文件没有额外的内存开销: 安全:可以列表,检 ...

  10. [前端开发]form-data和x-www-form-urlencoded的区别

    在后台开发时,之前做了文件的上传,用的是form-data,但并不知其区别.今天遇到了req.body为空的情况,切换成了x-www-form-urlencoded解决 form-data 就是htt ...