操作系统——实验4:进程调度的模拟

操作系统实验报告

实验四:进程调度的模拟

一、实验内容

  1. 熟悉进程调度的各种算法;
  2. 对模拟程序给出数据和流程的详细分析,并画出流程图;
  3. 参考模拟程序写出时间片轮转调度算法的程序。

二、实验目的

        通过本实验,加深对进程调度算法原理和过程的理解。

三、实验要求

  1.  对调度算法进行详细分析,在仔细分析的基础上,完全理解主要数据结构和过程的作用,给出主要数据结构的说明及画出主要模块的流程图。
  2.  根据提示信息,把函数写完整,使成为一个可运行程序。
  3.  反复运行程序,观察程序执行的结果,验证分析的正确性,然后给出一次执行的最后运行结果,并由结果计算出周转时间和带权周转时间。

        说明:进程生命周期数据。即CPU-I/O时间序列,它是进程运行过程中进行调度、进入不同队列的依据。如序列:10秒(CPU),500秒(I/O),20秒(CPU),100秒(I/O),30秒(CPU),90秒(I/O),110秒(CPU),60秒(I/O)……,此序列在进程创建时要求自动生成。

 

四、进程的五种基本状态及转换

        进程拥有以下五种状态:

  1. 创建状态:进程在创建时需要申请一个空白PCB,向其中填写控制和管理进程的信息,然后为该进程分配运行时所需的资源,最后将该进程转入就绪状态并插入就绪队列之中。
  2. 就绪状态:进程已处于准备好运行的状态,即已分配到除CPU以外的所需资源,只要分配到CPU就能够立即运行。
  3. 执行状态:进程已获得CPU,其程序正在执行。
  4. 阻塞状态:正在执行的进程由于某事件(I/O请求,申请缓存区失败等)暂时无法继续执行的状态。在满足请求时进入就绪状态等待系统调用。
  5. 终止状态:进程结束,或出现错误,或被系统终止,进入终止状态。

        其相互之间的转换关系如下图1所示:

7bfa46259e8445caa1b41c96c6478055.png

图1 进程基本状态转换图

 

五、进程调度的各种算法说明

5.1 先来先服务(FCFS)调度算法

        从就绪的进程队列中选择一个最先进入该队列的进程,为之分配处理机,使之投入运行。该进程一直运行到完成或发生某事件而阻塞后,进程调度程序才将处理机分配给其他进程。

5.2  短作业优先(SJF)调度算法

        从就绪队列中选择运行时间最短的进程,为之分配处理机。该进程一直运行到完成或发生某事件而阻塞后,进程调度程序才将处理机分配给其他进程。

5.3  优先级调度算法(PSA)

        系统将CPU分配给就绪队列中优先权最高的进程。

(1)根据新进程能否抢占正在执行的进程,可将该调度算法分为:

  • 非抢占式优先权调度算法:运行期间有更高优先权的进程到来也不剥夺CPU;
  • 抢占式优先权调度算法:运行期间有更高优先权的进程到来可以抢占CPU;

(2)根据进程优先级是否可以改变,可将该调度算法分为:

  • 静态优先权调度算法:优先级在创建进程时确定,且在进程的整个运行期间保持不变。确定优先级的主要依据有进程类型、进程对资源的要求、用户要求;
  • 动态优先权调度算法:在运行过程中,根据进程状态的变化动态调整优先级。优先级的主要依据为进程占有CPU时间的长短、就绪进程等待CPU时间的长短。

5.4  时间片轮转调度算法

        系统根据FCFS策略,将所有的就绪进程排成一个就绪队列。每次调度时把CPU分给队首进程,令其执行一个时间片。当该进程的时间片用完,调度程序终止当前进程的执行,并将它送到就绪队列的队尾,等待下次CPU执行。

5.5  多级队列调度算法

        建立多个优先权不同的就绪队列,所有队列的优先权从大到小依次排列,每个队列有自己的调度算法。

5.6  多级反馈队列(MFQ)调度算法

        设置多个就绪队列,并为每个队列赋予不同的优先级,该算法中不同队列中的进程所赋予的执行时间片大小不同,优先权越高的队列中,其进程时间片越小。进程按照队列的优先级调度,且每个队列都采用FCFS算法。

 

六、时间片轮转调度算法

6.1  算法描述

        将进程按照FCFS原则进行排序放入就绪队列中。CPU每次调用就绪队列中的队首进程,当CPU执行完一个时间片后,当即进行切换。若此前的进程仍未完成运算,则继续进入就绪队列队尾;若完成运算且后续没有运行需求,则进程结束;若完成运算但后续仍有运行需求,则进程进入阻塞队列队尾,等待I/O操作。当阻塞队列中的进程完成I/O操作,若后续仍有运行需求,则继续进入就绪队列队尾;若没有需求则进程结束。其整体算法思想如下图所示:

f9e1987f0243493a9ed6ac78717f9159.png

图2 时间片轮转调度算法示意图

6.2  进程的数据结构说明

struct ProcStruct {
    int  p_pid;
    char p_state;
    int  p_rserial[10];
    int  p_pos;
    int  p_starttime;
    int  p_endtime;
    int  p_cputime;
    int  p_iotime;
    int  p_next;
} proc[10];

1 进程数据结构说明表

属性

含义

p_pid

进程的标识号

p_state

进程的状态

(C:运行,R:就绪,W:阻塞,B:后备,F:完成)

p_rserial[10]

模拟进程执行的CPU和I/O时间数据序列,间隔存储,第0项存储随后序列的长度(项数),以便知晓啥时该进程执行结束

p_pos

当前进程运行到的位置,用来记忆执行到序列中的哪项

p_starttime

进程的建立时间

p_endtime

进程的运行结束时间

p_cputime

当前运行时间段进程剩余的需要运行时间

p_iotime

当前I/O时间段进程剩余的I/O时间

p_next

进程控制块的链接指针

6.3  算法实现流程

        在轮转算法中,时间片的大小设置对系统性能有很大的影响。若时间片小,有利于短进程,因为它能在该时间片内完成;但时间片过小,会频繁地执行进程调度和进程上下文的切换,增加系统的开销。反之,若时间片过长,会使算法退化为FCFS算法。在本次实验中,算法采用后者情形,使每次运行都能在一个时间片内完成计算。其算法流程图如下图3所示:

5868343905e744b398439a8be8ec1d68.png

图3 时间片轮转调度算法流程图

七、代码实现

#include <iostream>
#include <cstdlib>
#include <fstream>
#include <sysinfoapi.h>
#include <conio.h>
#include <synchapi.h>

using namespace std;

//全局变量
int   RunPoint;       // 运行进程指针,-1时为没有运行进程
int   WaitPoint;      // 阻塞队列指针,-1时为没有阻塞进程
int   ReadyPoint;     // 就绪队列指针,-1时为没有就绪进程
long  ClockNumber;    // 系统时钟
int   ProcNumber;     // 系统中模拟产生的进程总数
int   FinishedProc;   // 系统中目前已执行完毕的进程总数

//进程信息结构
struct ProcStruct {
    int  p_pid;          // 进程的标识号
    char p_state;        // 进程的状态,C--运行  R--就绪  W--组塞  B--后备  F--完成
    int  p_rserial[10];  // 模拟的进程执行的CPU和I/O时间数据序列,间隔存储,第0项存储随后序列的长度(项数),以便知晓啥时该进程执行结束
    int  p_pos;          // 当前进程运行到的位置,用来记忆执行到序列中的哪项
    int  p_starttime;    // 进程建立时间
    int  p_endtime;      // 进程运行结束时间
    int  p_cputime;      // 当前运行时间段进程剩余的需要运行时间
    int  p_iotime;       // 当前I/O时间段进程剩余的I/O时间
    int  p_next;         // 进程控制块的链接指针
} proc[10];

//函数
void Create_ProcInfo();         // 随机创建进程
void DisData();                 // 展示所创建的进程序列
void Scheduler_FF();            // 进程调度函数
void NewReadyProc();            // 判断当前是否有新进程到达
void Cpu_Sched();               // CPU调度模拟, 每次只执行一个CPU时间片p=1
void IO_Sched();                // I/O调度模拟
void Display_ProcInfo();        // 显示系统当前状态
void Read_Process_Info();       // 从磁盘读取最后一次生成的进程信息的文件,执行调度,以重现调度情况
void DisResult();               // 显示运行结果
void NextRunProcess();          // 选择下一个运行的进程


//主函数
int main() {
    char ch;
    RunPoint = -1;      // 运行进程指针,-1时为没有运行进程
    WaitPoint = -1;     // 阻塞队列指针,-1时为没有阻塞进程
    ReadyPoint = -1;    // 就绪队列指针,-1时为没有就绪进程
    ClockNumber = 0;    // 系统时钟
    ProcNumber = 0;     // 当前系统中的进程总数

    while (true) {
        printf("***********************************\n");
        printf("     1: 建立进程调度数据序列 \n") ;
        printf("     2: 读进程信息,执行调度算法\n") ;
        printf("***********************************\n");
        printf( "Enter your choice (1 ~ 2): ");

        do {
            cin >> ch; // 如果输入信息不正确,继续输入
        } while (ch != '1' && ch != '2');

        if (ch == '1') {
            Create_ProcInfo();      // 选择1
        } else if (ch == '2'){
            Scheduler_FF();    // 选择2
        }  else {
            break;
        }
    }
    return 0;
}

/* 随机创建进程:
   随机生成5~10个进程和对应的1~10个CPU--I/O时间数据序列
   CPU和I/O的时间数据值在5~15之间  */
void Create_ProcInfo() {
    srand(GetTickCount());   // 初始化随机数队列的"种子"
    ProcNumber=((float) rand() / 32767) * 5 + 5;  // 随机产生5~10个进程

    for(int i = 0; i < ProcNumber; i++) {
        // 生成进程的CPU--I/O时间数据序列
        proc[i].p_pid = ((float) rand() / 32767) * 1000;  // 初始化随机的进程ID号
        proc[i].p_state = 'B';   // 初始状态都为后备状态
        proc[i].p_rserial[0] = ((float) rand() / 32767) * 9 + 1;  // 随机生成时间序列长度1~10
        for (int j = 1; j <= proc[i].p_rserial[0]; j++) {
            proc[i].p_rserial[j] = ((float) rand() / 32767)  * 10 + 5; // 随机生成CPU和I/O时间数据序列5~15
        }
        proc[i].p_pos = 1;
        proc[i].p_starttime = ((float) rand() / 32767)  * 19 + 1;    // 随机生成开始时间,取值范围1~20ms
        proc[i].p_endtime = 0;
        proc[i].p_cputime = proc[i].p_rserial[1];
        proc[i].p_iotime = proc[i].p_rserial[2];
        proc[i].p_next = -1;
    }
    printf("\n---------------------------\n    建立了%2d 个进程数据序列\n\n", ProcNumber);
    DisData();  // 该函数为在屏幕上打印所创建的进程的具体信息,Dis是Display的缩写.
}


/* 展示所创建的进程序列,并将其写入磁盘的Process_Info.txt文件中 */
void DisData() {
    ofstream  outFile;
    outFile.open("Process_Info.txt") ;  //创建并打开Process_Info.txt文件
    for(int i=0; i<ProcNumber; i++) {
        // 写到txt文件中
        outFile << "ID=" << proc[i].p_pid
                << "(len=" << proc[i].p_rserial[0]
                << ",start=" << proc[i].p_starttime
                << "):\t";
        for (int j = 1; j <= proc[i].p_rserial[0]; j++) {
            outFile << "\t" << proc[i].p_rserial[j];
        }
        outFile << endl;

        // 打印到屏幕上
        cout << "ID=" << proc[i].p_pid
             << "(len=" << proc[i].p_rserial[0]
             << ",start=" << proc[i].p_starttime
             << "):\t";
        for (int j = 1; j <= proc[i].p_rserial[0]; ++j) {
            cout << "\t" << proc[i].p_rserial[j];
        }
        cout << endl;
    }
    outFile.close();  // 写入txt文件的流被冲刷,保存到磁盘上
}


//调度模拟算法
void Scheduler_FF()  {
    //用户没创建进程,从磁盘读取上次创建的进程信息,赋值给相应变量
    if(ProcNumber == 0) {
        Read_Process_Info();
    }
    ClockNumber = 0;  // 时钟开始计时, 开始调度模拟

    // 执行算法
    while(FinishedProc < ProcNumber) {
        ClockNumber++;    // 时钟前进1个单位
        NewReadyProc();   // 判别新进程是否到达
        Cpu_Sched();      // CPU调度
        IO_Sched();       // IO调度
        Display_ProcInfo(); //显示当前状态
    }
    DisResult();
}


/* 判断当前是否有新进程到达,有则放入就绪队列 */
void NewReadyProc() {
    for (int i = 0; i < ProcNumber; i++) {
        if (proc[i].p_starttime == ClockNumber) {
            // 进程进入时间达到系统时间,ClockNumber是当前的系统时间
            proc[i].p_state = 'R';   //    进程状态修改为就绪
            proc[i].p_next = -1;     // 该进行即将要挂在队列末尾,它肯定是尾巴,后面没人的,所以先设置next=-1

            //当前就绪队列无进程
            if (ReadyPoint == -1) {
                ReadyPoint = i;
            } else  {
                //就绪队列有进程,放入队列尾
                int n = ReadyPoint;
                while (proc[n].p_next != -1){
                    n = proc[n].p_next;  //找到原来队伍中的尾巴
                }
                proc[n].p_next = i;   //挂在这个尾巴后面
            }
        }
    }
}


//CPU调度模拟, 每次只执行一个CPU时间片p=1
void Cpu_Sched() {
    int n;

    // 没有进程在CPU上执行
    if (RunPoint == -1) {
        NextRunProcess();
        return;
    }

    proc[RunPoint].p_cputime--;      // 进程CPU执行时间减少1个时钟单位

    if (proc[RunPoint].p_cputime > 0) return;  // 还需要CPU时间,下次继续,这次就返回了

    //如果不满足以上>0的条件,就意味着=0,就不会自动返回,接着做以下事情
    // 进程完成本次CPU后的处理
    if (proc[RunPoint].p_rserial[0] == proc[RunPoint].p_pos) {
        //进程全部序列执行完成
        FinishedProc++;
        proc[RunPoint].p_state = 'F';
        proc[RunPoint].p_endtime = ClockNumber;
        RunPoint = -1;  //无进程执行
        NextRunProcess(); //找分派程序去,接着做下一个
    } else {
        //进行IO操作,进入阻塞队列
        proc[RunPoint].p_pos++;
        proc[RunPoint].p_state ='W';
        proc[RunPoint].p_iotime = proc[RunPoint].p_rserial[proc[RunPoint].p_pos];
        proc[n].p_next = -1;  //标记下,就自己一个进程,没带尾巴一起来;否则,当p_next不为-1时,后面的那一串都是被阻塞者
        n = WaitPoint;
        if(n == -1) //是阻塞队列第一个I/O进程
            WaitPoint = RunPoint;
        else {
            do {
                //放入阻塞队列第尾
                if(proc[n].p_next == -1) {
                    proc[n].p_next = RunPoint;
                    break;
                }
                n = proc[n].p_next;
            } while(n != -1) ;
        }
        RunPoint = -1;
        NextRunProcess();
    }
    return;
}


// I/O调度模拟, 与CPU调度操作类似。排在队首的进程得到一个时间片服务
void IO_Sched(){
    int n,m;

    // 没有等待I/O的进程,直接返回
    if (WaitPoint == -1) return;

    proc[WaitPoint].p_iotime--;  // 进行1个时钟的I/O时间

    if (proc[WaitPoint].p_iotime > 0) return; // 还没有完成本次I/O

    // 进程完成本次I/O的处理
    if (proc[WaitPoint].p_rserial[0] == proc[WaitPoint].p_pos) {
        //进程全部任务执行完成
        FinishedProc++;
        proc[WaitPoint].p_endtime = ClockNumber;
        proc[WaitPoint].p_state ='F';

        if(proc[WaitPoint].p_next == -1) {
            WaitPoint = -1;
            return;
        } else {
            //调度下一个进程进行I/O操作
            n = proc[WaitPoint].p_next;
            proc[WaitPoint].p_next = -1;
            WaitPoint = n;
            proc[WaitPoint].p_iotime = proc[WaitPoint].p_rserial[proc[WaitPoint].p_pos] ;
            return ;
        }
    } else {
        //进行下次CPU操作,进就绪队列
        m = WaitPoint;
        WaitPoint = proc[WaitPoint].p_next;
        proc[m].p_pos++;
        proc[m].p_state ='R'; //进程状态为就绪
        proc[m].p_next = -1;

        n = ReadyPoint;
        if(n == -1) {
            //是就绪队列的第一个进程
            ReadyPoint = m;
            return;
        } else {
            do {
                if(proc[n].p_next == -1) {
                    proc[n].p_next = m;
                    break ;
                }
                n = proc[n].p_next;
            } while(n != -1);
        }
    }
    return ;
}


//显示系统当前状态
void  Display_ProcInfo(){
    printf("\n当前系统模拟%d个进程的运行    时钟:%ld", ProcNumber, ClockNumber);
    printf("\n就绪指针 = %d, 运行指针 = %d, 阻塞指针 = %d\n", ReadyPoint, RunPoint, WaitPoint);

    printf("\n......... Running Process .........\n");
    if (RunPoint != -1) {
        // Print 当前运行的进程的信息
        printf("No.%d  ID:%d(%d,%2d)\t总CPU时间=%2d,剩余CPU时间=%2d\n",
               RunPoint, proc[RunPoint].p_pid, proc[RunPoint].p_rserial[0], proc[RunPoint].p_starttime,
               proc[RunPoint].p_rserial[proc[RunPoint].p_pos], proc[RunPoint].p_cputime);
    } else {
        printf("    No Process Running !\n");
    }

    int n = ReadyPoint;
    printf("\n......... Ready Process .........\n");
    if (n != -1) {
        while (n != -1) {
            // 显示就绪进程信息
            printf("No.%d  ID:%d(%d,%2d),第%2d个/总时间=%2d   serial:",
                   n, proc[n].p_pid, proc[n].p_rserial[0], proc[n].p_starttime,
                   proc[n].p_pos, proc[n].p_rserial[proc[n].p_pos]);
            for (int i = 1; i <= proc[n].p_rserial[0]; ++i) {
                printf("%4d", proc[n].p_rserial[i]);
            }
            printf("\n");
            n = proc[n].p_next;
        }
    } else {
        printf("    No Process Ready !\n");
    }

    n = WaitPoint;
    printf("\n......... Waiting Process .........\n");
    if (n != -1) {
        while (n != -1) {
            // 显示阻塞进程信息
            printf("No.%d  ID:%d(%d,%2d),I/O执行到序列中的第%2d个,总I/O时间=%2d/  剩余I/O时间=%2d   serial:",
                   n, proc[n].p_pid, proc[n].p_rserial[0], proc[n].p_starttime,
                   proc[n].p_pos, proc[n].p_rserial[proc[n].p_pos], proc[n].p_iotime);
            for (int i = 1; i <= proc[n].p_rserial[0]; ++i) {
                printf("%4d", proc[n].p_rserial[i]);
            }
            printf("\n");
            n = proc[n].p_next;
        }
    } else {
        printf("    No Process Waiting !\n");
    }

    printf("\n=================== 后备进程 ====================\n");
    for (int i = 0; i < ProcNumber; i++) {
        if (proc[i].p_state == 'B') {
            printf("No.%d  ID:%d(%d,%2d)", i, proc[i].p_pid, proc[i].p_rserial[0], proc[i].p_starttime);
            for (int j = 1; j <= proc[i].p_rserial[0]; ++j) {
                printf("%4d", proc[i].p_rserial[j]);
            }
            printf("\n");
        }
    }

    printf("\n================ 已经完成的进程 =================\n");
    for (int i = 0; i < ProcNumber; i++) {
        if (proc[i].p_state == 'F') {
            printf("No.%d  ID:%d(%d,%2d),Endtime=%d  serial:",
                   i, proc[i].p_pid, proc[i].p_rserial[0],
                   proc[i].p_starttime, proc[i].p_endtime);
            for (int j = 1; j <= proc[i].p_rserial[0]; ++j) {
                printf("%4d", proc[i].p_rserial[j]);
            }
            printf("\n");
        }
    }
    printf("\n");
}


//从磁盘读取最后一次生成的进程信息的文件,执行调度,以重现调度情况。
//否则,都是随机生成的,每次都不一样,难以重现当时场景。
void Read_Process_Info() {
    ifstream inFile;     // 定义打开文件的文件流
    char ch;
    int i,j,k,tmp;

    inFile.open("Process_Info.txt") ;  //打开上次写的txt进行信息文件流

    i = 0;
    while(inFile) {
        inFile.get(ch);

        for(j = 0; j < 3; j++)  inFile.get(ch);    //每一行前3个字符都是ID=,扔掉
        inFile >> proc[i].p_pid;

        for(j = 0; j < 5; j++)  inFile.get(ch);    //继续读(len=,扔掉5个字符
        inFile >> proc[i].p_rserial[0];         //读序列项数

        for(j = 0; j < 7; j++)  inFile.get(ch);     //继续读start=,扔掉7个字符
        inFile >> proc[i].p_starttime;

        for(j = 0 ; j < 2; j++)  inFile.get(ch);    //继续读,扔掉2个字符

        for(k = 1; k <= proc[i].p_rserial[0]; k++) {
            inFile >> tmp;
            proc[i].p_rserial[k]=tmp;
        }

        proc[i].p_state = 'B';
        proc[i].p_pos = 1;
        proc[i].p_endtime = 0;
        proc[i].p_next = -1;
        proc[i].p_cputime = proc[i].p_rserial[1];
        proc[i].p_iotime = proc[i].p_rserial[2];
        i++;    //本行结束,一个进程信息读完,序号+1, 准备 next process
    }

    ProcNumber = i-1;     //给ProcNumber赋值,i最后有++,回位下
    inFile.close();       //完工后关闭文件
}


// 显示运行结果
void DisResult(){
    printf("\n=================== 运行结果 ====================\n");
    printf("标识号---时间序列---建立时间---结束时间---周转时间---带权周转时间\n");
    for (int i = 0; i < ProcNumber; i++) {
        int schedule_time = proc[i].p_endtime - proc[i].p_starttime;
        int runtime = 0;
        for (int j = 1; j <= proc[i].p_rserial[0]; j += 2) {
            runtime += proc[i].p_rserial[j];
        }
        printf("ID=%d     %d      %d      %d      %d      %.2f\n", proc[i].p_pid, proc[i].p_rserial[0],
        proc[i].p_starttime, proc[i].p_endtime, schedule_time, schedule_time * 1.0 / runtime);
    }
    printf("\n");
}


void NextRunProcess(){
    if (ReadyPoint == -1) {
        //就绪队列没有等待的进程,结束所有
        RunPoint = -1;
        return;
    }
    proc[ReadyPoint].p_state = 'C';
    RunPoint = ReadyPoint;
    proc[ReadyPoint].p_cputime = proc[ReadyPoint].p_rserial[proc[ReadyPoint].p_pos] ;
    ReadyPoint = proc[ReadyPoint].p_next;
    proc[RunPoint].p_next = -1;
}

 

八、运行结果验证及分析

8.1  运行结果展示

(1)建立进程调度数据序列

11c84b82c7a345218bae3789a4d0405b.png

(2)读进程信息,执行调度算法

初始状态:

d338ff8b90e54c94a0c165a7d24964ff.png


时钟4:

dd49803531684620be810d4550ad46ab.png

        进程4在第4s创建完成,CPU调用进程4开始运行。


时钟8:

5aa3678d39074e03ac3e6372b39c6d8f.png

        进程0在第8s创建完成,进入就绪队列。而此前没有其他进程创建,故在前段时间内进程4一直占用CPU。


第9s:

ba313f08a08d4c71b45ebe099e50a801.png

        进程4的CPU段运行完成,但后续仍有I/O请求操作,故进入阻塞队列,等待I/O操作完成。CPU调度就绪队列的队首元素进程0执行。


第16s:

b22b497d4a754cdf8ec56e66e08706a0.png

        进程1、进程3分别在第15s和第16s创建完成,先后进入就绪队列队尾。


第17s:

6a0101c5a4ad44ee8e26eb904b126c6b.png

        进程4的I/O操作执行完毕,从阻塞队列进入到就绪队列队尾。


以此算法继续执行,最终所有进程在第95s运行结束:

0f299cf411b34cac8fbea975946276e1.png

(3)周转时间和带权周转时间运算

通过计算各进程结束时间与开始时间的差值,求得各进程的周转时间,再除以其各自对应的进程的CPU时钟数,得到各进程的带权周转时间。

c0344b49409a4b69a6035166bbb8da8c.png

8.2  运行结果验证

(1)将题给进程信息存入txt文件

e838a6f295ef436f9b3c84733d7c83e5.png

        将实验任务书中的进程信息存入txt文件,如上图所示。因此只需运行程序,对照程序运行结束后的结果与任务书中给出的结果是否一致,即可检验所编写的轮转调度法模拟程序是否正确。

(2)运行程序并对照运行结果

3fa54658713a474795f529eb32cf233a.png

        读取txt文件,修改Read_Process_Info()函数,如上图所示。

        运行程序,得到运行结果,与任务书中情况对照:

时钟2时:

bd47f669efd846b38ac5319f92a1fcae.png

524d9881bf23419ca2cbd43e267f2f17.png


时钟35时:

bce9f79cb5314973ab6962860a95e5c9.png

00f730b5f4ea4ea292a89e98e180933e.png


时钟88时:

d9e3341ba48640e29dab860e8ad44fec.png

10e82b243aa548cdb86f060bdcb06e4f.png


时钟101时:

e486c3338fcd42cf8d06c357cebdd6c9.png

e43ab86c210348ce9cc2e4a64825474b.png


时钟155时:

d1156812588d4ceba10dccabe7b829d4.png

09b1647d6bbd414a9fdc54107e257dd6.png

        对比运行结果和正确答案,两者在各时钟结果完全一致,证明实验程序正确实现了轮转调度算法。

        该情况下的各进程的周转时间和带权周转时间结果如下:

ceac2c82a4644bfea600b54acc3a2e3a.png

 

  • 18
    点赞
  • 110
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
实验目的】 1. 理解进程的概念,熟悉进程的组成; 2. 用高级语言编写和调试一个进程调度程序,以加深对进程调度算法理解。 【实验准备】 1. 几种进程调度算法  短进程优先调度算法  高优先权优先调度算法  先来先服务调度算法  基于时间片的轮转调度算法 2. 进程的组成  进程控制块(PCB)  程序段  数据段 3. 进程的基本状态  就绪W(Wait)  执行R(Run)  阻塞B(Block) 【实验内容】 1. 例题 设计一个有 N个进程共行的进程调度程序。 进程调度算法:采用最高优先数优先的调度算法(即把处理机分配给优先数最高的进程)和先来先服务算法。 每个进程有一个进程控制块(PCB)表示。进程控制块可以包含如下信息:进程名、优先数、到达时间、需要运行时间、已用CPU时间、进程状态等等。进程的优先数及需要的运行时间可以事先人为地指定(也可以由随机数产生)。进程的到达时间为进程输入的时间。进程的运行时间以时间片为单位进行计算。每个进程的状态可以是就绪 W(Wait)、运行R(Run)、或完成F(Finish)三种状态之一。就绪进程获得 CPU后都只能运行一个时间片。用已占用CPU时间加1来表示。如果运行一个时间片后,进程的已占用 CPU时间已达到所需要的运行时间,则撤消该进程,如果运行一个时间片后进程的已占用CPU时间还未达所需要的运行时间,也就是进程还需要继续运行,此时应将进程的优先数减1(即降低一级),然后把它插入就绪队列等待CPU。每进行一次调度程序都打印一次运行进程、就绪队列、以及各个进程的 PCB,以便进行检查。重复以上过程,直到所要进程都完成为止。 4. 实验题目  编写并调试一个模拟进程调度程序,采用“最高优先数优先”调度算法对五个进程进行调度。“最高优先数优先”调度算法的基本思想是把CPU分配给就绪队列优先数最高的进程。静态优先数是在创建进程时确定的,并在整个进程运行期间不再改变。动态优先数是指进程的优先数在创建进程时可以给定一个初始值,并且可以按一定原则修改优先数。例如在进程获得一次CPU后就将其优先数减少1。或者,进程等待的时间超过某一时限时增加其优先数的值,等等。  编写并调试一个模拟进程调度程序,采用“轮转法”调度算法对五个进程进行调度。轮转法可以是简单轮转法、可变时间片轮转法,或多队列轮转法。简单轮转法的基本思想是:所有就绪进程按 FCFS排成一个队列,总是把处理机分配给队首的进程,各进程占用CPU的时间片相同。如果运行进程用完它的时间片后还为完成,就把它送回到就绪队列的末尾,把处理机重新分配给队首的进程。直至所有的进程运行完毕。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阮阮的阮阮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值