【操作系统】处理机调度算法

实验3 处理机管理

一、实验目的

在多道程序或多任务系统中,系统中同时处于就绪态的进程有若干个,即能运行的进程数远远大于处理机个数。为了使系统中的各个进程能有条不紊的运行,必须按照某种调度策略,选择一个进程占用处理机。

本次实验要求设计并实现一个模拟单处理机调度的算法,以加深对处理机调度基本概念和基本算法的理解。

二、实验内容

1、设计一个按时间片轮转法实现处理机调度的程序(难度系数0.95)

       (1)假设系统中有N个进程,每个进程由一个进程控制块(PCB)来标识,进程控制块结构如下图所示。

进程名

到达时间

估计运行时间

进程状态

……

链接指针

图1 进程控制块PCB结构图

 

       进程名:即进程标识。

       到达时间:进程创建时的系统时间或由用户制定。

估计运行时间:可由设计者指定一个时间值。

       进程状态:这里假定进程有两种状态:就绪状态和完成状态。就绪状态用“R”表示,完成状态用“C”表示。假定进程创建后就处于就绪状态,运行结束时,就被置成完成状态。

链接指针:按照进程到达系统的时间将处于就绪状态的进程连接成一个就绪队列。指针指出下一个到达的进程控制块首地址。最后一个进程的链指针为NULL。

       (2)按照进程到达的先后顺序排成一个循环队列。

       (3)执行处理机调度时,首先选择队首的第一个进程运行。

       (4)由于本实验是模拟实验,所以对被选中进程并不实际启动运行,而只是执行:

                  估计运行时间减1,用这个操作来模拟进程的一次运行。

       (5)进程运行一次后,以后的调度则将当前指针依次下移一个位置,指向下一个进程。同时还应判断该进程的剩余运行时间是否为0,若不为0,则等待下一轮的运行;若该进程的剩余运行为0,则将该进程的状态置为完成状态,并退出循环队列。

       (6)若就绪队列不空,则重复上述的步骤(4)和(5)直到所有进程都运行完为止。

       (7)在所设计的调度程序中,应包含显示或打印语句,以便显示或打印每次选中进程的名称及运行一次后队列的变化情况。

       (8)计算每个进程的周转时间、带权周转时间以及进程平均周转时间。

2、设计一个按优先级调度的算法实现处理机调度(难度系数1.0)

(1) 系统有N个进程,每个进程用一个进程控制块来代表,进程控制块的结构如下图所示。

进程名

到达时间

估计运行时间

进程优先级

进程状态

……

链接指针

图2 进程控制块PCB结构

进程的优先级、要求运行时间和估计运行时间由用户程序任意设定。调度时,总是选择优先级最高的进程运行。

(2)为了调度方便,设计一个指针指向就绪队列中的第一个进程。另外再设一个当前运行进程指针,指向当前正运行的进程。

(3)处理机每个时间片调度1次,总是选择已经到达队列的优先级最高的进程运行。为了采用动态优先级调度,进程每运行一次,其优先级就减1。由于本实验是模拟实验,所以对选中进程并不实际启动运行,而只是执行:

优先级减1和估计运行时间减1,用这两个操作来模拟进程的一次运行。

(4)进程运行一次后,若剩余时间不为0,且其优先级低于就绪队列的其他进程优先级,则选择一个高优先级进程抢占CPU;若剩余时间为0,把它的状态改为完成状态“C”,并撤出就绪队列。

(5)若就绪队列不空,则重复上述的步骤(3)和(4),直到所有进程成为完成状态。

(6)在所设计的调度程序中,应包含显示或打印语句,以便显示或打印每次选中进程的名称及运行一次后队列的变化情况。

       (7)计算每个进程的周转时间、带权周转时间以及进程平均周转时间。

三、实验结果和分析

(1)程序中使用的数据结构说明;

【1】实验内容1使用的数据结构说明:

如上图所示,进程控制块使用结构体进行构建,包含以下几个属性:进程名字、进程状态、就绪队列中的累计排队序号、进程到达时间、进程服务时间、进程完成时间、进程剩余时间。其中,进程名字使用字符串变量,其他属性均使用整形变量。

如上图所示,在执行RR算法之前,首先利用字符串存放当前的就绪队列,利用数组list存放在就绪队列中的进程的编号,利用数组waitnumber存放进程在就绪队列中的累计排队序号。然后,通过对数组waitnumber进行从小到大的排序,同时对应给数组list进行排序。最后,在数组list中得到按照等待顺序递增的进程序列,并将进程名字按序写入ready中。

【2】实验内容2使用的数据结构说明:

如上图所示,进程控制块使用结构体进行构建,包含以下几个属性:进程名字、进程状态、进程到达时间、进程服务时间、进程完成时间、进程剩余时间、进程优先级、进程上一次变为就绪态的时间。其中,进程名字使用字符串变量,其他属性均使用整形变量。此外,进程上一次变为就绪态的时间初始化为进程到达时间,如果进程后续有被CPU调用,则更新为被CPU调用后的时间。

如上图所示,在执行PSA算法之前,首先利用数组存放当前的就绪队列所对应的进程编号,并调用sortlist()函数进行当前时间的就绪队列计算。

(2)程序流程图和带有注释的源程序;

【1】实验内容1的流程图:如(4)处的总结1所示,并主要包含以下四个内容:

1)整体的程序流程图;

2)进程初始化的流程图;

3)按照到达时间排序进程的流程图;

4)时间片轮转算法的流程图。

【2】实验内容2的流程图:如(4)处的总结2所示,并主要包含以下四个内容:

1)整体的程序流程图;

2)进程初始化的流程图;

3)就绪队列进行排序的流程图;

4)优先级调度算法的流程图。

【3】实验内容1 & 实验内容2的带有注释的源程序:

实验内容1:exp3-1.cpp

#include <iostream>

#include <stdio.h>

#include <iomanip>

#include <string.h>

#include <stdlib.h>

using namespace std;

//pcb的结构体设置

typedef struct{

    string name;        //进程名字

    int flag;           //是否完成,状态检测(1是完成,0是未完成)

    int number;         //就绪队列中的顺序

    int arrivetime;     //到达时间

    int runtime;        //服务时间

    int completetime;   //完成时间

    int remaintime;     //剩余时间

}PCB;

//进程指标 (全局)

PCB p[100];             //可输入的进程数量

int timelen=1;          //时间片长度

int total;              //进程总数(由main中的用户输入)

//就绪队列指标 (全局)

int current=0;          //当前应该执行的进程编号

int head=0;             //就绪队列中的队首

//进程初始化

void initialize(){

    for(int i=0;i<total;i++){

        cout<<"请输入第"<<i+1<<"个进程的名字、到达时间、服务时间:";

        cin>>p[i].name>>p[i].arrivetime>>p[i].runtime;

       

        //其他属性的初始化

        p[i].remaintime=p[i].runtime;   //初始剩余时间 = 服务时间

        p[i].flag=0;                    //完成状态 = 未完成

        p[i].number=0;                  //就绪队列中的顺序 = 0

    }

}

//按照到达时间排序进程(冒泡排序)

void sortarrive(){

    for(int i=0;i<total-1;i++){

        for(int j=0;j<total-i-1;j++){

            //到达时间从小到大排序

            if(p[j].arrivetime>p[j+1].arrivetime){

                //如果前一个的时间大于后一个,则交换两个进程的顺序

                PCB t=p[j+1];

                p[j+1]=p[j];

                p[j]=t;

            }

        }

    }

}

//时间片轮转

void timeprocess(){

    int time=p[0].arrivetime;           //系统开始的时间是最早就绪进程的到达时间

    int complete=0;                     //已经完成的进程个数

    int n=0;                            //记录while循环次数的变量

   

    //所有进程执行的过程

    while(complete<total){

        for(int i=0;i<total;i++){

            //当前这个进程和cur相同,且状态为未完成

            if(p[i].number==current && p[i].flag==0){

                //print current cycle

                cout<<endl<<"当前的时钟周期是:T"<<current<<endl;

               

                //process ready list

                string ready="";

                int list[100];

                int waitnumber[100];

                int cnt=0;

                for(int k=0;k<total;k++){

                    //大于当前current的,都是在就绪队列里面的进程

                    if(p[k].number>current && p[k].flag==0){

                        list[cnt]=k;

                        waitnumber[cnt]=p[k].number;

                        cnt++;

                    }

                }

                cout<<"就绪队列里面一共有:"<<cnt<<"个进程,";

               

                //按照waitnumber排序 (冒泡排序)

                for (int k = 0; k < cnt - 1; k++) {

                    for (int kk = 0; kk < cnt - 1 - k; kk++) {

                        if (waitnumber[kk] > waitnumber[kk + 1]) {

                            int tempfig = waitnumber[kk + 1];

                            waitnumber[kk + 1] = waitnumber[kk];

                            waitnumber[kk] = tempfig;

                            int templist = list[kk + 1];

                            list[kk + 1] = list[kk];

                            list[kk] = templist;

                        }

                    }

                }//不知道为啥死循环了。。调好了

               

               

                //按照waitnumber的大小进行push

                for(int k=0;k<cnt;k++){

                    int id=list[k];

                    ready+=p[id].name;

                    ready+=" ";

                }

                //print ready

                if(ready==""){

                    cout<<"就绪队列为空"<<endl;

                }

                else{

                    cout<<"当前的就绪队列为:"<<ready<<endl;

                }

               

                //时间片工作

                cout<<"判断时间片ing,当前应该执行进程"<<p[i].name<<endl;

                //current进程能在当前时间片运行完

                if(p[i].remaintime<=timelen){

                    time+=p[i].remaintime;      //系统时间,增加该进程的剩余时间

                    p[i].flag=1;                //状态变为完成

                    p[i].completetime=time;     //此进程的完成时间为当前系统时间

                    p[i].remaintime=0;          //剩余时间,设置为0

                     

                    complete++;     //系统中完成的进程数+=1

                    current++;      //cur+=1

                   

                    //检查是否有新的到达进程

                    for(int j=i+1;j<total;j++){

                        //有新的到达进程

                        if(p[j].arrivetime<=time && p[j].number==0){

                            head++;             //就绪队列个数+=1

                            p[j].number=head;   //这个进程的就绪编号为head

                        }

                    }

                }

                //current进程不能在当前时间片运行完

                else{

                    time+=timelen;

                    p[i].remaintime-=timelen;

                    current++;

                   

                    //检查是否有新的到达进程

                    for(int j=i+1;j<total;j++){

                        if(p[j].arrivetime<=time && p[j].number==0){

                            head++;

                            p[j].number=head;

                        }

                    }

                   

                    //重新给这个进程加入就绪队列

                    head++;             //刚刚执行的这个进程继续进入就绪队列

                    p[i].number=head;   //更改就绪编号

                }

               

            }

        }

        n++;    //检测while循环的次数

    }

    cout<<"执行完毕,总共花费时钟周期:T"<<current<<endl;

}

void show(){

    double x=0;     //总周转时间         周转时间 = 完成时间 - 到达时间

    double y=0;     //总带权周转时间   带权周转时间 = 周转时间 / 服务时间

   

    //输出每个进程的基本信息

    for(int i=0;i<total;i++){

        double px=(double)p[i].completetime-p[i].arrivetime;    //当前进程的周转时间

        double py=px/(double)p[i].runtime;                      //当前进程的带权周转时间

        cout<<"进程名字:"<<p[i].name;

        cout<<"\t"<<"周转时间:"<<px;

        cout<<"\t"<<"带权周转时间:"<<py;

        cout<<"\t"<<"到达时间:"<<p[i].arrivetime;

        cout<<"\t"<<"服务时间:"<<p[i].runtime;

        cout<<"\t"<<"完成时间:"<<p[i].completetime;

        cout<<endl;

        x+=px;

        y+=py;

    }

    double ret1=x/total;    //平均周转时间

    double ret2=y/total;    //平均带权周转时间

    cout<<"平均周转时间:"<<ret1<<endl;

    cout<<"平均带权周转时间:"<<ret2<<endl;

}

/*

//RR

测试用例1

5

p1 0 3

p2 2 6

p3 4 4

p4 6 5

p5 8 2

//优先级

测试用例2

4

p1 0 2 2

p2 0 2 3

p3 0 4 1

p4 0 3 4

进程执行的顺序应该是:p4 p2 p4 p1 p2 p4 p3 p1 p3 p3 p3

7.5

2.8125

*/

int main(){

    cout<<"请输入进程总数:";

    cin>>total;         //用户输入进程的总数

    initialize();       //初始化

    sortarrive();       //到达时间处理

    timeprocess();      //时间片处理

    cout<<endl;

    show();             //输出最终的结果

    return 0;

}

实验内容2:exp3-2.cpp

#include <iostream>

#include <stdio.h>

#include <iomanip>

#include <string.h>

#include <stdlib.h>

#include <vector>

using namespace std;

//pcb的结构体设置

typedef struct{

    string name;        //进程名字

    int flag;           //是否完成,状态检测(1是完成,0是未完成)

    int arrivetime;     //到达时间

    int runtime;        //服务时间

    int completetime;   //完成时间

    int remaintime;     //剩余时间

    int prior;          //优先级

    int lasttime;       //上一次运行的时间

}PCB;

//进程指标 (全局)

PCB p[100];             //可输入的进程数量

int timelen=1;          //时间片长度

int total;              //进程总数(由main中的用户输入)

//进程初始化

void initialize(){

    for(int i=0;i<total;i++){

        cout<<"请输入第"<<i+1<<"个进程的名字、到达时间、服务时间、优先级:";

        cin>>p[i].name>>p[i].arrivetime>>p[i].runtime>>p[i].prior;

       

        //其他属性的初始化

        p[i].remaintime=p[i].runtime;   //初始剩余时间 = 服务时间

        p[i].flag=0;                    //完成状态 = 未完成

        p[i].lasttime=p[i].arrivetime;  //上一次运行的时间

    }

}

//初始化就绪队列

void sortlist(vector<int> &wait,int cycle){

    //遍历已经到来且未完成的进程

    for(int i=0;i<total;i++){

        if(p[i].arrivetime<=cycle && p[i].flag==0){

            wait.push_back(i);

        }

    }

   

    int n=wait.size();

    //按照prior划分(选择排序)

    for(int i=0;i<n-1;i++){

        int id=i;   //max to process

        for(int j=i+1;j<n;j++){

            int curprocess=wait[j];     //遍历到的后面的进程序号

            int maxprocess=wait[id];    //当前优先级最大的进程序号

            if(p[maxprocess].prior<p[curprocess].prior){

                //如果后面进程的prior更大,则更新优先级最大的进程序号

                id=j;

            }

        }

        if(id!=i){

            //如果有更新,则交换两个pcb的顺序

            int t=wait[id];

            wait[id]=wait[i];

            wait[i]=t;

        }

    }

   

    //相同prior按照lasttime划分

    int i=0;    //从队首开始分析

    while(i<n-1){

        int pos=i;  //记录离当前最远,且和当前进程优先级相同的进程

        int nowid=wait[i];

        for(int j=1+i;j<n;j++){

            int processid=wait[j];

            if(p[processid].prior==p[nowid].prior){

                pos=j;  //后面的进程和现在的进程优先级相同,更新最远记录

            }

        }

        if(pos!=i){

            //sort between wait[i] and wait[pos] according to lasttime(选择排序)

            for(int a=i;a<pos;a++){

                int aid=a;

                for(int b=a+1;b<=pos;b++){

                    int cur=wait[b];

                    int min=wait[aid];

                    if(p[min].lasttime>p[cur].lasttime){

                        //如果后面的lasttime更小

                        aid=b;

                    }

                }

                if(aid!=a){

                    //如果有更新,则交换两个pcb的顺序

                    int tt=wait[aid];

                    wait[aid]=wait[a];

                    wait[a]=tt;

                }

            }

        }

        i=pos+1;    //移动到下一个还没有判断过优先级是否相等的pcb

    }

}

void psa(){

    //系统开始时间 (初始化)

    int starttime=p[0].arrivetime;

    for(int i=1;i<total;i++){

        if(starttime>p[i].arrivetime){

            starttime=p[i].arrivetime;  //更新进程最早到达的时间

        }

    }

   

    int cycle=0;        //执行的周期数

    int complete=0;     //完成的进程数

   

    //执行所有进程

    while(complete<total){

        //wait list

        vector<int> wait;

        sortlist(wait,cycle);

        cout<<endl<<"在执行该时间片前,T"<<cycle<<"的就绪队列为:";

        for(int i=0;i<wait.size();i++){

            int proid=wait[i];

            cout<<p[proid].name<<" ";

        }

        cout<<endl;

       

        //执行wait[0](位于就绪队列队首的时间片)

        int curpro=wait[0];

        cout<<"判断时间片ing,当前应该执行进程:"<<p[curpro].name<<endl;

       

        //判断当前时间片内,curpro是否能执行完毕

        if(p[curpro].remaintime<=timelen){  

            //can

            p[curpro].flag=1;                           //修改进程状态,且flag==1之后,就不需要管prior

            p[curpro].completetime=starttime+cycle+1;   //修改完成时间

            p[curpro].remaintime=0;                     //修改剩余时间

            complete++;                                 //完成的进程数 += 1

            cout<<"在这个时间片内,已经执行完进程:"<<p[curpro].name<<endl;

        }

        else{  

            //cannot

            p[curpro].remaintime-=timelen;  //修改剩余时间

            p[curpro].prior--;              //修改优先级

            p[curpro].lasttime=cycle+1;     //修改上次使用时间

        }

       

        cycle++;    //时间周期 += 1

        //complete++;   //temporary break while to debug

    }

    cout<<endl<<"执行完毕,总共花费时钟周期:T"<<cycle<<endl;

}

void show(){

    double x=0;     //总周转时间         周转时间 = 完成时间 - 到达时间

    double y=0;     //总带权周转时间   带权周转时间 = 周转时间 / 服务时间

   

    //输出每个进程的基本信息

    for(int i=0;i<total;i++){

        double px=(double)p[i].completetime-p[i].arrivetime;    //当前进程的周转时间

        double py=px/(double)p[i].runtime;                      //当前进程的带权周转时间

        cout<<"进程名字:"<<p[i].name;

        cout<<"\t"<<"周转时间:"<<px;

        cout<<"\t"<<"带权周转时间:"<<py;

        cout<<endl;

        cout<<"\t"<<"到达时间:"<<p[i].arrivetime;

        cout<<"\t"<<"服务时间:"<<p[i].runtime;

        cout<<"\t"<<"完成时间:"<<p[i].completetime;

        cout<<endl;

        x+=px;

        y+=py;

    }

    double ret1=x/total;    //平均周转时间

    double ret2=y/total;    //平均带权周转时间

    cout<<"平均周转时间:"<<ret1<<endl;

    cout<<"平均带权周转时间:"<<ret2<<endl;

}

/*

//优先级

测试用例2

4

p1 0 2 2

p2 0 2 3

p3 0 4 1

p4 0 3 4

进程执行的顺序应该是:p4 p2 p4 p1 p2 p4 p3 p1 p3 p3 p3

                      0  1  2  3  4  5  6  7  8  9  10

7.5

2.8125

*/

int main(){

    cout<<"请输入进程总数:";

    cin>>total;         //用户输入进程的总数

    initialize();       //初始化

    psa();              //psa process

    cout<<endl;

    show();             //输出最终的结果

    return 0;

}

 

(3)执行程序名,并打印程序运行时的初值和运行结果,其中包括:

    ①各进程控制块的初始状态;

【实验内容1】

在本实验内容中,测试用例的内容如下表所示。

进程总数

5

进程名字

进程到达时间

进程服务时间

P1

0

3

P2

2

6

P3

4

4

P4

6

5

P5

8

2

 如下图所示,在初始化函数initialize()中,在初始化各个进程的各个属性之后,将打印各个进程的基本信息(完成时间除外)。

测试用例的结果如下图所示。

由上图可知,进程初始化成功,各个进程控制块的初始状态符合预期。

【实验内容2】

在本实验内容中,测试用例的内容如下表所示。

进程总数

4

进程名字

进程到达时间

进程服务时间

进程优先级

P1

0

2

2

P2

0

2

3

P3

0

4

1

P4

0

3

4

如下图所示,在初始化函数initialize()中,在初始化各个进程的各个属性之后,将打印各个进程的基本信息(完成时间除外)。

测试用例的结果如下图所示。

由上图可知,进程初始化成功,各个进程控制块的初始状态符合预期。

    ②选中运行进程的名字、运行后各进程控制块状态以及每次调度时,就绪队列的进程排列顺序;

【实验内容1】

在RR算法执行过程中,程序会输出当前的时间、就绪队列的结果、当前时间片选中运行进程的名字、当前时间片运行后执行完毕的进程,并在算法执行完毕后输出系统总花费的时间。程序输出的结果如下图所示。

在PSA算法执行完毕后,程序会输出各个进程的周转时间和带权周转时间,并同时输出到达时间、服务时间、完成时间,以便核对计算是否正确。在输出完所有进程的信息后,程序会输出系统的平均周转时间和平均带权周转时间。程序输出的结果如下图所示。

核验实验3的ppt中关于RR算法的测试案例后,发现实验结果和理论计算结果一致。

【实验内容2】

在PSA算法执行过程中,程序会输出就绪队列的结果、当前时间片选中运行进程的名字、当前时间片运行后执行完毕的进程,并在算法执行完毕后输出系统总花费的时间。程序输出的结果如下图所示。

在PSA算法执行完毕后,程序会输出各个进程的周转时间和带权周转时间,并同时输出到达时间、服务时间、完成时间,以便核对计算是否正确。在输出完所有进程的信息后,程序会输出系统的平均周转时间和平均带权周转时间。

核验实验3的ppt中关于PSA算法的测试案例后,发现实验结果和理论计算结果一致。

4总结本次实验的心得与体会。

【流程图】

1:实验内容1的流程图

1)整体的程序流程图(main函数)

2)进程初始化的流程图(initialize函数)

3)按照到达时间排序进程的流程图(sortarrive函数)

4)时间片轮转算法的流程图

5)运行结果输出的流程图(show函数)

2:实验内容2的流程图

注:运行结果输出的流程图(show函数)与实验内容1相同,此处不再赘述。

1)整体的程序流程图

2)进程初始化的流程图

3)就绪队列进行排序的流程图

4)优先级调度算法的流程图

【实验总结】

3时间片原则:各进程按时间片运行,当一个时间片用完后,便停止该进程的执行而重新调度,适用于分时系统。

4优先权原则:通常对一些重要的和紧急的进程赋予较高的优先权。当这种进程进入就绪队列时,如果其优先权比正在执行的进程优先权高,便停止正在执行的进程,将处理机分配给优先权高的进程。

5:进程调度方式分为两种——【抢占方式】和【非抢占方式】。抢占方式:把处理机分配给某进程后便让它一直运行下去,直到进程完成或发生某事件而阻塞时,才把处理机分配给另一个进程。非抢占方式:当一个进程正在运行时,系统可以基于某种原则,剥夺已分配给它的处理机,将之分配给其它进程。因此,时间片轮转法(RR)和优先级调度算法(PSA)均是抢占方式,因为这两种算法都并没有一个进程完整地运行。

6:各种调度算法的优点和缺点如下图所示。

FCFS:先来先服务算法。

SJF:最短作业优先算法。

HRRF:最高响应比优先算法。

RR:时间片轮转算法。

7:在本实验中,主要使用了RR和PSA。RR的特点是:公平性高,每个进程都有机会执行;可能导致较高的上下文切换开销,实时性较差。PSA的特点是:能够更好地满足紧急任务的需要,对于重要或紧急的任务更加有效;可能导致低优先级进程长时间等待,甚至发生饥饿现象。

8饥饿现象:在操作系统中是指某一个或多个进程由于种种原因长时间得不到所需的资源,从而无法继续执行的情况。

四、实验指导

       在Linux环境下, 用C语言编写,程序中的数据结构定义和程序主框架如下,供参考。

  

       typedef   struct  pcb          //进程控制块定义

       {    int  pid;           //进程ID

char  pname[N];          //进程名

              int  runtime;                //运行时间

              int  arrivetime;            //到达时间

              char state;                    //进程状态

              int  priority;                //进程优先级

int   finishtime;             //进程结束时间

              struct  pcb  *next;      //链接指针

        ……

}  PCB;

PCB  *head_input;             //就绪队列头指针

PCB  *head_run;                //运行进程指针

unsigned long current;      //记录系统当前时间的变量

……

void  inputprocess( );  //建立进程,输入进程数目N,输入各个进程信息,并建立链表;

void  printreadylist( );  //输出就绪队列

int   readyprocess( ); //检查就绪队列并运行进程

int   runprocess( );  //运行进程的函数;

……

void main()           //主函数

{    //fp=open("result.txt”, “w”);

    current=0;

       inputprocess( );

      readyprocess( );

    ……

       getch( );

       //fclose(fp);

}

void  inputprocess( )  //建立进程,输入进程数目N,输入各个进程信息,并建立链表;

{ // 输入需要运行的进程数量n

  …..

  // 输入进程信息:进程名,运行时间,到达时间,优先级等信息

  …...

}

int  readyprocess( )      //检查就绪队列并运行进程

{ // 就绪队列为空时,结束运行

  …..

 // 根据调度算法选择一个进程;

runprocess();

//如果是时间片调度算法,则将时间减1

//如果是FCFS调度算法,则该进程运行结束,将当前时间+运行时间

//如果是优先级调度算法,运行时间-1,优先级-1

// 修改进程的状态

 ……

 // 输出进程运行信息

}

五、测试用例

1. 时间片调度算法

2 优先级调度算法

进程       优先级          运行时间       到达时间    结束时间

P1      12            2         0            8

P2      13            2         0            5

P3      11            4         0            11

P4      14            3         0            6

程序执行过程:

每个时间片调度一次,执行完成的进程优先级减1,重新调度

0     1     2     3     4     5     6     7     8     9     10     time

P4­——p2——p4——p1——P2——p4——p3——p1——p3——p3——p3

13    12    12    11  11end   11end 10    10end  9    8     7end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2803031.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

集百家所长的开放世界游戏,艾尔莎H311-PRO带你玩转《幻兽帕鲁》

随着近几年开放世界游戏热潮的兴起&#xff0c;如今这类游戏可以说是像雨后春笋般不断推出&#xff0c;比如《幻兽帕鲁》就是近期非常火热的一个代表&#xff0c;它不仅集合了生存、建造、宠物养成等多种元素&#xff0c;而且可爱的卡通画风格更是老少皆宜。那么&#xff0c;这…

【Spring】IoC容器 控制反转 与 DI依赖注入 配置类实现版本 第四期

文章目录 基于 配置类 方式管理 Bean一、 配置类和扫描注解二、Bean定义组件三、高级特性&#xff1a;Bean注解细节四、高级特性&#xff1a;Import扩展五、基于注解配置类方式整合三层架构组件总结 基于 配置类 方式管理 Bean Spring 完全注解配置&#xff08;Fully Annotatio…

防御保护---内容过滤技术

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 一.防火墙内容过滤技术概述 内容过滤技过检查网络流量中的数据包内容&#xff0c;对不符合预订策略的文件类型/文件的上传下载/承载文件的协议和应用进行过滤和阻止&#xff0c;以确保网络安全。…

CSS浮动与定位

行内元素和块级元素的区别&#xff1a;&#xff08;非常重要&#xff09; 行内元素&#xff1a; 与其他行内元素并排&#xff1b;不能设置宽、高。默认的宽度&#xff0c;就是文字的宽度。 块级元素&#xff1a; 霸占一行&#xff0c;不能与其他任何元素并列&#xff1b;能…

华为算法题 go语言或者ptython

1 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返…

PostgreSQL 实体化视图的使用

上周的教程中&#xff0c;通过 DVD Rental Database 示例&#xff0c;让我们了解了在 PostgreSQL 中创建实体化视图的过程。正如我们所了解的&#xff0c;PostgreSQL 实体化视图提供了一种强大的机制&#xff0c;通过预计算和存储查询结果集为物理表来提高查询性能。接下来的内…

轻松掌握opencv的8种图像变换

文章目录 opencv的8种图像变换1. 图像放大、缩小2. 图像平移3. 图像旋转4. 图像仿射变换5. 图像裁剪6. 图像的位运算&#xff08;AND, OR, XOR&#xff09;7. 图像的分离和融合8. 图像的颜色空间 opencv的8种图像变换 1. 图像放大、缩小 我们先看下原图 import cv2 import ma…

HTTP基本概念-HTTP 常见的状态码有哪些?

资料来源 : 小林coding 小林官方网站 : 小林coding (xiaolincoding.com) HTTP 常见的状态码有哪些? 1xx 类状态码属于提示信息&#xff0c;是协议处理中的一种中间状态&#xff0c;实际用到的比较少。 2xx 类状态码表示服务器成功处理了客户端的请求&#xff0c;也是我们最愿…

【stm32】hal库-双通道ADC采集

【stm32】hal库-双通道ADC采集 CubeMX图形化配置 程序编写 /* USER CODE BEGIN PV */ #define BATCH_DATA_LEN 1 uint32_t dmaDataBuffer[BATCH_DATA_LEN]; /* USER CODE END PV *//* USER CODE BEGIN 2 */lcd_init();lcd_show_str(10, 10, 24, "Demo14_4:ADC1 ADC2 S…

vue后台管理添加水印简单方式watermark-package

详情参考:https://www.npmjs.com/package/watermark-package 示例方法 <el-button type"primary" click"AddWatermark">添加水印</el-button><el-button type"primary" click"RemoveWatermark">清除水印</el-but…

k8s-heml管理 17

Helm是Kubernetes 应用的包管理工具&#xff0c;主要用来管理 Charts&#xff0c;类似Linux系统的 yum。Helm Chart 是用来封装 Kubernetes 原生应用程序的一系列 YAML 文件。可以在你部署应用的时候自定义应用程序的一些 Metadata&#xff0c;以便于应用程序的分发。 对于应用…

关于el-select值的回显问题 : 框内显示label值还是value值

<el-form-item label"状态" prop""><el-selectv-model"roleForm.state"class"m-2"size"large"style"width: 240px"placeholder"请选择状态"value-key"value"//value-key 与下面的ke…

CleanMyMac X2024告别硬盘空间不足,让您的Mac电脑极速如新

CleanMyMac X支持多种操作系统版本&#xff0c;包括macOS 10.10&#xff08;Yosemite&#xff09;、macOS 10.11&#xff08;El Capitan&#xff09;、macOS 10.12&#xff08;Sierra&#xff09;、macOS 10.13&#xff08;High Sierra&#xff09;、macOS 10.14&#xff08;Mo…

多输入时序预测|GWO-CNN-LSTM|灰狼算法优化的卷积-长短期神经网络时序预测(Matlab)

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、算法介绍&#xff1a; 灰狼优化算法&#xff1a; 卷积神经网络-长短期记忆网络&#xff1a; 四、完整程序下载&#xff1a; 一、程序及算法内容…

解决IntelliJ IDEA 2023版本创建Spring项目时Java只能选择17或21的问题

问题描述&#xff1a; 当使用IntelliJ IDEA2023版本中Spring Initializr新建Spring项目时&#xff0c;即使JDK配置项为1.8&#xff0c;Java配置项仍然只能选17或21. 在JDK为1.8版本情况下&#xff0c;Java选择17或21&#xff0c;点击NEXT按钮&#xff0c;则会弹窗提示SDK不支持…

如何打造品牌的专属记忆点?媒介盒子支招

传播环境不断改变&#xff0c;品牌运营的方式也需要不断更新&#xff0c;碎片化“短平快”营销也许能短暂的占据大众视野&#xff0c;但并非品牌的长效致胜法宝。实现品牌价值长效增长的核心技巧就在于打造品牌的专属记忆点&#xff0c;那么如何通过打造记忆点来破开品牌的同质…

互动游戏团队如何将性能体验优化做到TOP级别

一、背景 随着互动游戏业务 DAU 量级增加&#xff0c;性能和体验重要性也越发重要&#xff0c;好的性能和体验不仅可以增加用户使用体感&#xff0c;也可以增加用户对于互动游戏的使用粘性。 对现状分析&#xff0c;主要存在首屏渲染速度慢、打开页面存在白屏、页面加载过多资…

二叉树中的第K大层和

1.题目 这道题是2024-2-23的签到题&#xff0c;题目难度为中等。 考察知识点为BFS算法&#xff08;树的层序遍历&#xff09; 大根堆&#xff08;优先队列&#xff09;。 题目链接&#xff1a;2583. 二叉树中的第 K 大层和 - 力扣&#xff08;LeetCode&#xff09; 给你一棵…

云服务器发展史

在数字化浪潮的推动下&#xff0c;云服务器作为信息技术领域的一颗璀璨明珠&#xff0c;其发展史是一部科技进步和创新思维的缩影。从最初的概念提出到现如今的广泛应用&#xff0c;云服务器经历了翻天覆地的变化&#xff0c;不仅极大地推动了信息技术的发展&#xff0c;也彻底…

微服务架构师封神之路13-RabbitMQ集群与高可用|RabbitMQ clustering and HA

目录 几个关键技术点 节点间相互验证&#xff0c;.erlang.cookie nodename的唯一性 port冲突与配置 Management UI plugin安装 Queue&#xff08;message&#xff09;replicas Queue leader strategy 配置文件详细 Node 1 Installation path .erlang.cookie rabbit…