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

实验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://www.mzph.cn/news/697822.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot指定外部环境配置

nohup java -Xms256m -Xmx512m -Dfile.encodingUTF-8 -jar /usr/local/xxxx.jar --spring.profiles.activeprod > system.log 2>&1 & --spring.profiles.activeprod修改的是多环境配置中内部application.properties里的spring.profiles.active值 -Dspring.config…

消息队列MQ 保证消息不丢失(消息可靠性)

文章目录 概述RabbitMQ 怎么避免消息丢失&#xff08;可靠传输&#xff09;RocketMQ 怎么确保消息不丢失Kafka 怎么保证消息不丢失activeMQ 怎么避免消息丢失MQ 宕机了消息是否会丢失线上服务宕机时&#xff0c;如何保证数据100%不丢失吗&#xff1f;消息队列消息持久化 概述 …

思伟老友记 | 携手并进17年 中泰公司的稳步发展和企业传承

17年携手并进 合作共赢 2023年是中泰&#xff08;福建&#xff09;混凝土发展有限公司携手思伟软件的第17年。在这关键的17年间&#xff0c;我们共同经历了一个行业的兴盛发展&#xff0c;也相互见证了彼此的荣耀成长。中泰从泉州惠安洛阳江边一个简单的搅拌站&#xff0c;到如…

h-table(表格列表组件的全封装)

文章目录 概要h-table的封装过程查询组件封装 h-highForm结果页右侧工具栏封装RightToolbar结果页列表组件h-table结果页vue页面使用js文件有需要的请私信博主&#xff0c;还请麻烦给个关注&#xff0c;博主不定期更新组件封装&#xff0c;或许能够有所帮助&#xff01;&#x…

如何做代币分析:以 SOL 币为例

作者&#xff1a;lesleyfootprint.network 编译&#xff1a;cicifootprint.network 数据源&#xff1a;Solana Token Dashboard &#xff08;仅包括以太坊数据&#xff09; 在加密货币和数字资产领域&#xff0c;代币分析起着至关重要的作用。代币分析指的是深入研究与代币…

springmvc基于springboot 的音乐播放系统 _7sdu8

这就意味着音乐播放系统的设计可以比其他系统更为出色的能力&#xff0c;可以更高效的完成最新的ymj排行榜、ymj音乐资讯等功能。 此系统设计主要采用的是JAVA语言来进行开发&#xff0c;JSP技术、采用SSM框架技术&#xff0c;框架分为三层&#xff0c;分别是控制层Controller&…

vue3 vuex

目录 Vuex 是什么 什么是“状态管理模式”&#xff1f; 什么情况下我应该使用 Vuex&#xff1f; 使用方法&#xff1a; 提交载荷&#xff08;Payload&#xff09; 对象风格的提交方式 使用常量替代 Mutation 事件类型 Mutation 必须是同步函数 在组件中提交 Mutation …

redis GEO 类型原理及命令详解

目录 前言 一、GeoHash 的编码方法 二、Redis 操作GEO类型 前言 我们有一个需求是用户搜索附近的店铺&#xff0c;就是所谓的位置信息服务&#xff08;Location-Based Service&#xff0c;LBS&#xff09;的应用。这样的相关服务我们每天都在接触&#xff0c;用滴滴打车&am…

使用ENV工具编译RT-Thread【详细过程讲解:从下载到编译、设置】

感兴趣的宝子&#xff0c;可以点个赞收藏&#xff0c;便于后期有需要的时候能快速找到~~ ENV编译编译RT-Thread工程的详细过程讲解 ENV简介ENV的下载设置ENV使用ENV编译RT-Thread工程◆ 打开ENV◆ 输入打包命令◆ 查看并打开工程文件◆ 使用menuconfig 对生成项目的RT-Thread配…

【Git企业实战开发】Git常用开发流操作总结

【Git企业实战开发】Git常用开发流操作总结 大家好 我是寸铁&#x1f44a; 总结了一篇Git常用开发流操作总结的文章✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 现在刚做项目的伙伴&#xff0c;可能你之前学过git&#xff0c;但是一实战发现不熟悉 没关系&#xff0c;看寸铁这篇…

【Linux】普通用户sudo失败怎么办

普通用户&#xff0c;sudo失败报错怎么办 问题分析如何解决成功 问题分析 新建的普通用户sudo失败 sudo提权&#xff0c;是以root的身份执行命令。 当我们用sudo提升权限的时候&#xff0c;这里有个问题&#xff0c;Linux会提示我们输入当前普通用户的密码——这就有点不好。…

【Linux取经路】基础I/O之重定向的实现原理

文章目录 一、再来理解重定向1.1 输出重定向效果演示1.2 重定向的原理1.3 dup21.4 输入重定向效果演示1.5 输入重定向代码实现 二、再来理解标准输出和标准错误2.1 同时对标准输出和标准错误进行重定向2.2 将标准输出和标准错误重定向到同一个文件 三、再看一切皆文件四、结语 …

Elasticsearch从入门到精通-01认识Elasticsearch

Elasticsearch从入门到精通-01认识Elasticsearch &#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是程序员行走的鱼 &#x1f342;博主从本篇正式开始ES学习&#xff0c;希望小伙伴可以一起探讨 &#x1f4d6; 本篇主要介绍和大家一块简单认识下ES并了解ES中的主要角色…

SpringBoot集成Mqtt发送消息

1. MQTT简介 MQTT是一种物联网消息协议&#xff0c;为Message Queuing Telemetry Transport的缩写&#xff0c;即消息队列传输探测&#xff0c;协议基于发布订阅模式进行通信&#xff0c;有开销低、带宽小、轻量的特点&#xff0c;通常应用在物联网数据采集、移动应用、智能硬…

H5获取手机相机或相册图片两种方式-Android通过webview传递多张照片给H5

需求目的&#xff1a; 手机机通过webView展示H5网页&#xff0c;在特殊场景下&#xff0c;需要使用相机拍照或者从相册获取照片&#xff0c;上传后台。 完整流程效果&#xff1a; 如下图 一、H5界面样例代码 使用html文件格式&#xff0c;文件直接打开就可以展示布局&#…

【每日一题】2583. 二叉树中的第 K 大层和-2024.2.23

题目: 2583. 二叉树中的第 K 大层和 给你一棵二叉树的根节点 root 和一个正整数 k 。 树中的 层和 是指 同一层 上节点值的总和。 返回树中第 k 大的层和(不一定不同)。如果树少于 k 层,则返回 -1 。 注意,如果两个节点与根节点的距离相同,则认为它们在同一层。 示…

canvas水波纹效果,jquery鼠标水波纹插件

canvas水波纹效果&#xff0c;jquery鼠标水波纹插件 效果展示 jQuery水波纹效果&#xff0c;canvas水波纹插件 HTML代码片段 <div class"scroll04wrap"><h3>发展历程</h3><div class"scroll04"><p>不要回头&#xff0c;一…

前端工程Bem架构及其封装

文章目录 简介语法在vue3项目中引用sass创建bem.scss文件修改vite.config.tsvue文件中使用结果 这是我学习记录的笔记&#xff0c;如有不正&#xff0c;欢迎补充 简介 首先认识一下什么是bem架构&#xff1f;BEM的意思就是块&#xff08;block&#xff09;、元素&#xff08;e…

【DDD】学习笔记-发布者—订阅者模式

在领域设计模型中引入了领域事件&#xff0c;并不意味着就采用了领域事件建模范式&#xff0c;此时的领域事件仅仅作为一种架构或设计模式而已&#xff0c;属于领域设计模型的设计要素。在领域设计建模阶段&#xff0c;如何选择和设计领域事件&#xff0c;存在不同的模式&#…

nginx-ingress-controller组件中Nginx的版本升级

参考链接&#xff1a;https://blog.csdn.net/qq_22824481/article/details/133761302 https://blog.csdn.net/mengfanshaoxia/article/details/127155020 https://blog.csdn.net/weixin_39961559/article/details/87935873 概要 业务区k…