FCFS、SJF、HRRN、RR、MRLA算法C++代码实现(先来先服务、短作业优先、高响应比优先、时间片轮转、多级反馈队列)

本文章写于操作系统实验,SJF算法写的有问题,读者可以自己删改(很简单的啦)

除了FCFS算法,其余算法都是基于时钟轮转循环来实现的,时间负责度比较高,但是读者认为这样做比较还原操作系统内核实现,就依旧头铁写的。

在RR和MRLA算法中,几乎每一个if中都有continue,是因为对于时钟增加、队列进出等情况涉及到的参数变动比较多,而且顺序很重要,索性直接写为每个if都有continue,这样便于理解的纠错。

注释写的比较多,大家有什么问题欢迎评论区留言,就直接上代码了。

//
//  main.cpp
//  ProcessScheduling
//
//  Created by BINOFL on 2021/4/30.
//

//模拟实现各种CPU调度算法
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <time.h>
#include <queue>
//#define NULL 0
#define getpch(type) (type*)malloc(sizeof(type))
typedef struct pcb PCB;

using namespace::std;

struct pcb { //定义进程控制块PCB
    int id; // 标识符
    char name[10]; // 名称
    int time_start; // 到达时间
    int time_need; // 服务时间
    int time_left; // 剩余运行时间
    int time_used; // 已使用的CPU时间
    int time_clock; // ⚠️:仅在MRLA中使用
// 在时间片机制中, time_need = time_left + time_used
    char state; // 进程状态
    
    float ratio_response; // 响应比
};

//****************系统函数*****
void Sleep(int n) { // 停顿n秒
    clock_t goal;
    goal = (clock_t)n * CLOCKS_PER_SEC + clock();
    while(goal > clock());
}

char Keygo() { //程序暂停,按任意键继续
    char c;
    printf("按任意键继续.....");
    c = getchar();
    return c;
}

//*********用户函数***********
//------数据设置区域------
int time_unit = 2; //时间片长度
int high_time_unit = 2, mid_time_unit = 4, low_time_unit = 8;
#define MAXNUM 19 //最大进程数量

//int process_num = 5; //实际进程数量

  /* MRLA 数据*/ int process_num = 4; //实际进程数量

//PCB pcbdata[MAXNUM] = {
在程序内设置进程数据,不必手工输入,数据来自课本P76图3-4
//    {1000, "A", 0, 4, 4, 0, 0, 'R', 0.0},
//    {1001, "B", 1, 3, 3, 0, 0, 'R', 0.0},
//    {1002, "C", 2, 5, 5, 0, 0, 'R', 0.0},
//    {1003, "D", 3, 2, 2, 0, 0, 'R', 0.0},
//    {1004, "E", 4, 4, 4, 0, 0, 'R', 0.0},
//};

/* MRLA 数据*/PCB pcbdata[MAXNUM] = {
    {1000, "A", 0, 7, 7, 0, 0, 'R', 0.0},
    {1001, "B", 5, 4, 4, 0, 0, 'R', 0.0},
    {1002, "C", 7, 13, 13, 0, 0, 'R', 0.0},
    {1003, "D", 12, 9, 9, 0, 0, 'R', 0.0},
};

int ready[MAXNUM]; //就绪队列,存放进程在pcbdata中的位置
//例如上面的数据经过某种调度算法排序为CBADE
//就绪队列第一个进程为C
//存放在ready数组的第一个数值为2,因为pcbdata[2]是c进程的PCB
//就绪队列第一个进程为B
//存放在ready数组的第二个数值为1,因为pcbdata[1]是B进程的PCB
int order[MAXNUM]; //记录排序使用哪个数值作为排序对象

// ----- 手动输入函数 -----
void Input(){
    printf("进程总数为:");
    scanf("%d", &process_num);
    for (int i = 0; i < process_num; i ++ ) {
        pcbdata[i].id = 1000 + i;
        printf("输入第%d个进程名称:", i+1);
        scanf("%s", &pcbdata[i].name);
        printf("输入第%d个到达时间:", i+1);
        scanf("%d", &pcbdata[i].time_start);
        printf("输入第%d个服务时间:", i+1);
        scanf("%d", &pcbdata[i].time_need);
        // 剩余运行时间
        pcbdata[i].time_left = pcbdata[i].time_need;
        printf("\n");
        pcbdata[i].time_used = 0;
        pcbdata[i].ratio_response = 0.0;
        pcbdata[i].state = 'R';
    }
}

int Level2TimeUnit(int level) {
    if (level == 1) return high_time_unit;
    if (level == 2) return mid_time_unit;
    if (level == 3) return low_time_unit;
    return  -1;
}

static void SortOrderReady() {
    int temp;
    for (int i = 0; i < process_num; i ++) {
        for (int j = 0; j < process_num; j ++) {
            if (order[i] < order[j]) {
                temp = order[i];
                order[i] = order[j];
                order[j] = temp;
                
                temp = ready[i];
                ready[i] = ready[j];
                ready[j] = temp;
            }
        }
    }
}

static void print_pcb_info(int next_i, int t) {
    printf("\n---------------------------------\n");
    printf("进程%d : %s, ", next_i + 1, pcbdata[next_i].name);
    printf("到达时间 --- %d, 服务时间 --- %d ",
           pcbdata[next_i].time_start,
           pcbdata[next_i].time_need);
    int e_time = t + pcbdata[next_i].time_need; // 进程完成时间
    int ta_time = e_time - pcbdata[next_i].time_start; // 周转时间
    double wta_time = float(ta_time)/pcbdata[next_i].time_need; // 带权周转时间
    printf("完成时间 -- %d, 周转时间 -- %d, 带权周转时间 -- %.1f", e_time, ta_time, wta_time);
    printf("\n---------------------------------\n\n");
}

static void print_pcb_info(pcb* pcbdata, int t) {
    printf("\n---------------------------------\n");
    printf("进程号%d : %s, ", pcbdata->id, pcbdata->name);
    printf("到达时间 --- %d, 服务时间 --- %d ",
           pcbdata->time_start,
           pcbdata->time_need);
    int e_time = t; // 进程完成时间
    int ta_time = e_time - pcbdata->time_start; // 周转时间
    double wta_time = float(ta_time)/pcbdata->time_need; // 带权周转时间
    printf("完成时间 -- %d, 周转时间 -- %d, 带权周转时间 -- %.3f", e_time, ta_time, wta_time);
    printf("\n---------------------------------\n\n");
}


// ----- 调度函数 -----
// 先来先服务
void FCFS() {
    int temp;
    for (int i = 0; i < process_num; i ++) {
        order[i] = pcbdata[i].time_start; // 依据开始时间进行排序
        ready[i] = i;
    }
    SortOrderReady();
    printf("--------先来先服务算法调度,非抢占,无时间片--------\n");
    temp = pcbdata[ready[0]].time_start;
    for (int i = 0; i < process_num; i ++) {
        printf("进程%d : %s, ", i + 1, pcbdata[ready[i]].name);
        printf("到达时间 --- %d, 服务时间 --- %d\n",
               pcbdata[ready[i]].time_start,
               pcbdata[ready[i]].time_need);
        printf("本进程正在运行....");
        Sleep(1);
        printf("运行完毕\n");
        temp += pcbdata[ready[i]].time_need; // 进程完成时间
        int ta_time = temp - pcbdata[ready[i]].time_start; // 周转时间
        double wta_time = float(ta_time)/pcbdata[ready[i]].time_need; // 带权周转时间
        printf("完成时间 -- %d, 周转时间 -- %d, 带权周转时间 -- %.1f\n\n", temp, ta_time, wta_time);
    }
    printf("-------所有进程调度完成--------\n");
}

// 短作业优先
void SJF(){
    int temp;
    for (int i = 0; i < process_num; i ++) {
        order[i] = pcbdata[i].time_need; // 依据需要时间进行排序
        ready[i] = i;
    }
    SortOrderReady();
    
    printf("--------短作业优先算法调度,非抢占,无时间片--------\n");
    temp = pcbdata[ready[0]].time_start;
    for (int i = 0; i < process_num; i ++) {
        printf("进程%d : %s, ", i + 1, pcbdata[ready[i]].name);
        printf("到达时间 --- %d, 服务时间 --- %d\n",
               pcbdata[ready[i]].time_start,
               pcbdata[ready[i]].time_need);
        printf("本进程正在运行....");
        Sleep(1);
        printf("运行完毕\n");
        temp += pcbdata[ready[i]].time_need; // 进程完成时间
        int ta_time = temp - pcbdata[ready[i]].time_start; // 周转时间
        double wta_time = float(ta_time)/pcbdata[ready[i]].time_need; // 带权周转时间
        printf("完成时间 -- %d, 周转时间 -- %d, 带权周转时间 -- %.1f\n\n", temp, ta_time, wta_time);
    }
    printf("-------所有进程调度完成--------\n");
}

// 高响应比优先, 无需排序, 每次执行时找最大响应比进程
void HRRN() {
    int total = MAXNUM;
    int max_time = 0; // 最长可能的时间
    for (int i = 0; i < MAXNUM; i ++) {
        max_time += pcbdata[i].time_need + pcbdata[i].time_start ;
    }
    printf("--------高响应比优先算法调度,非抢占,无时间片--------\n");
    int current_end_time = 0;
    bool running = false;
    for (int t = 0; t < max_time && total > 0; t ++) {
        // t 为当前时间
        // 时间片走完 或者 进程全部结束,就退出
        if (running && t < current_end_time) continue;
        running = false; // 运行结束了,重新计算
        float max_ratio_response = 0.0;
        int next_i = -1;
        for (int i = 0 ; i < MAXNUM; i ++) {
            // 遍历所有进程
            if (pcbdata[i].state == 'R' && t >= pcbdata[i].time_start) {
                // 如果进程没有运行过
                pcbdata[i].ratio_response = float(t  - pcbdata[i].time_start + pcbdata[i].time_need) / pcbdata[i].time_need;
                // 就计算相应比
                if (pcbdata[i].ratio_response > max_ratio_response) {
                    // 找到最大的相应比的进程
                    max_ratio_response = pcbdata[i].ratio_response;
                    next_i = i;
                }
                
            }
        }
        if (next_i != -1) { // 如果有一个进程可以运行
            running = true;
            current_end_time = t + pcbdata[next_i].time_need;
            pcbdata[next_i].state = 'E'; // 进程结束了
            print_pcb_info(next_i, t);
        }
        
    }
    // 循环结束
    printf("-------所有进程调度完成--------\n");
}

// 时间片轮转算法, 使用队列的数据结构, 通过时间片找寻
void TimeSlice() {
    queue<pcb> q;
    int max_time = 0; // 最长可能的时间
    for (int i = 0; i < process_num; i ++) {
        max_time += pcbdata[i].time_need + pcbdata[i].time_start ;
    }
    printf("--------时间片轮转算法调度,非抢占,有时间片--------\n");
    pcb* current_pcb = nullptr;
    int clock = 0; // 当前时间片的时间
    for (int t = 0; t < max_time; t ++) {
        // t 为当前时间
        // 每次到了一个新的时间,就把start时间到了的pcb插入到队列中
        for (int i = 0; i < process_num; i ++) {
            if (pcbdata[i].time_start == t) {
                q.push(pcbdata[i]);
            }
        }
        // ✅
        
        clock ++;
        if (current_pcb != nullptr) {
            current_pcb->time_used += 1;
            current_pcb->time_left -= 1;
        }
        
        // 1. 如果时间片走完 或 时间片走完的时候进程 同时也结束了
        if (clock == time_unit) {
            if (current_pcb == nullptr) {
                continue;
            }
            if (current_pcb->time_left == 0) {
                printf(" ---> 时间%d 进程运行结束 \n", t);
                print_pcb_info(current_pcb, t);
                current_pcb = nullptr;
            } else {
                printf(" ---> 时间%d 时间片消耗殆尽 \n", t);
                q.push(*current_pcb);
                current_pcb = nullptr;
            }
            // 时间片走完,检测是否可以插入新的进程
            if (!q.empty()) {
                current_pcb = &q.front();
                printf("时间%d, 进程 %s 开始使用", t, current_pcb->name);
                q.pop();
            }
            clock = 0;
            continue;
        }

        
        // 2. 如果当前current_pcb是空,就把pcb插入到队列中 (说明上个时间也为nullptr,检测可不可以插入一个进程)
        if (current_pcb == nullptr) {
            if(q.empty()) continue;
            current_pcb = &q.front();
            printf("时间%d, 进程 %s 开始使用", t, current_pcb->name);
            q.pop();
            clock = 0;
            continue;
        }
        
        // 3. 如果(进程运行结束)
        if (current_pcb != nullptr && current_pcb->time_left == 0) {
            // 若果剩余时间为0了
            printf(" ---> 时间%d 进程运行结束 \n", t);
            print_pcb_info(current_pcb, t);
            current_pcb = nullptr;
            clock = 0;
            t--;
            continue;
        }
    }
}

// 多级反馈队列
void MRLA() {
    int max_time = 0; // 最长可能的时间
    queue<pcb> high_q, mid_q, low_q;
    for (int i = 0; i < process_num; i ++) {
        max_time += pcbdata[i].time_need + pcbdata[i].time_start ;
    }
    printf("--------多级反馈队列算法调度,抢占,多级队列时间片--------\n");
    pcb* current_pcb = nullptr;
    int current_level = 1; // 1 - high, 2 - mid, 3 - low

    for (int t = 0; t < max_time; t ++) {
        // t 为当前时间
        // 每次到了一个新的时间,就把start时间到了的pcb插入到队列中
        for (int i = 0; i < process_num; i ++) {
            if (pcbdata[i].time_start == t) {
                high_q.push(pcbdata[i]);
                // 进行抢占
                
                if (current_pcb != nullptr) { // 如果是空,就不抢占
                    if (current_pcb->time_left == 1) {
                        printf(" ---> 时间%d 进程运行结束 \n", t);
                        print_pcb_info(current_pcb, t);
                        current_pcb = nullptr;
                    }
                    else {
                        if (current_pcb->time_clock + 1 == Level2TimeUnit(current_level)) {
                        printf(" ---> 时间%d 时间片耗尽 同时 进程被抢占 \n", t);
                        // 同时时间片和进程抢占,也要把进程放到下一个队列
                        current_level ++;
                        current_pcb->time_clock = 0; // 因为运行完了,所以可以归零
                        } else {
                            printf(" ---> 时间%d 进程被抢占 \n", t);
                            current_pcb->time_clock++;
                        }
                        if(current_level == 1) high_q.push(*current_pcb);
                        if(current_level == 2) mid_q.push(*current_pcb);
                        if(current_level == 3) low_q.push(*current_pcb);
                    }
                }
            
                current_level = 1;
                current_pcb = &high_q.front();
                current_pcb->time_clock = -1; // 抢占者的时钟周期一定是从0开始的
                high_q.pop();
                printf("时间%d, 队列I中   进程 %s 开始使用", t, current_pcb->name);
                
            }
        }
        
//        clock ++;
        if (current_pcb != nullptr) {
            current_pcb->time_used += 1;
            current_pcb->time_clock += 1;
            current_pcb->time_left -= 1;
        }
        
        // 1. 如果时间片走完 或 时间片走完的时候进程同时也结束了
        if (current_pcb != nullptr && current_pcb->time_clock == Level2TimeUnit(current_level)) {
            if (current_pcb == nullptr) {
                continue;
            }
            if (current_pcb->time_left == 0) {
                printf(" ---> 时间%d 进程运行结束 \n", t);
                print_pcb_info(current_pcb, t);
                current_pcb = nullptr;
            } else {
                printf(" ---> 时间%d 时间片消耗殆尽 \n", t);
                current_pcb->time_clock = 0; // 时间片耗完是可以归零时钟的
                // 根据现在进程current_level来push到对应的队列
                if (current_level == 1) mid_q.push(*current_pcb);
                else low_q.push(*current_pcb);
                current_pcb = nullptr;
            }
            // 时间片走完,检测是否可以插入新的进程
            if (!high_q.empty()) { // 首先检查high_q
                current_pcb = &high_q.front();
                current_level = 1;
                printf("时间%d, 队列I中   进程 %s 开始使用", t, current_pcb->name);
                high_q.pop();
            } else if(!mid_q.empty()) { // 然后检查mid_q
                current_pcb = &mid_q.front();
                current_level = 2;
                printf("时间%d, 队列II中  进程 %s 开始使用", t, current_pcb->name);
                mid_q.pop();
            } else if(!low_q.empty()) { // 最后检查low_q
                current_pcb = &low_q.front();
                current_level = 3;
                printf("时间%d, 队列III中 进程 %s 开始使用", t, current_pcb->name);
                low_q.pop();
            }
            continue;
        }
        
        // 2. 如果当前current_pcb是空,就把pcb插入到队列中 (说明上个时间也为nullptr,检测可不可以插入一个进程)
        if (current_pcb == nullptr) {
            if(high_q.empty() && mid_q.empty() && low_q.empty()) continue;
            if (!high_q.empty()) { // 首先检查high_q
                current_pcb = &high_q.front();
                current_level = 1;
                printf("时间%d, 队列I中   进程 %s 开始使用", t, current_pcb->name);
                high_q.pop();
            } else if(!mid_q.empty()) { // 然后检查mid_q
                current_pcb = &mid_q.front();
                current_level = 2;
                printf("时间%d, 队列II中  进程 %s 开始使用", t, current_pcb->name);
                mid_q.pop();
            } else if(!low_q.empty()) { // 最后检查low_q
                current_pcb = &low_q.front();
                current_level = 3;
                printf("时间%d, 队列III中 进程 %s 开始使用", t, current_pcb->name);
                low_q.pop();
            }
            continue;
        }
        
        // 3. 如果(进程运行结束),但是时钟片没有用完
        if (current_pcb->time_left == 0) {
            // 若果剩余时间为0了
            printf(" ---> 时间%d 进程运行结束 \n", t);
            print_pcb_info(current_pcb, t);
            current_pcb = nullptr;
//            clock = 0;
            continue;
        }
    
    }
}

int main() {
    int sch = 99;
//    Input();
    while (sch != 0) {
        printf ("\n请选择其中一种调度算法:\n");
        printf ("(1)先来先服务FCFS\n");
        printf ("(2)短作业优先SJF\n");
        printf ("(3)高响应比HRRN\n");
        printf ("(4)时间片轮转Timeslice\n");
        printf ("(5)多级馈队列MRLA\n");
        printf ("(0)退出, \n");
        printf ("请输入上述一个数字: ");
        scanf ("%d",&sch);
        switch (sch) {
           case 1: FCFS(); break;
           case 2: SJF(); break;
           case 3: HRRN(); break;
           case 4: TimeSlice(); break;
           case 5: MRLA(); break;
           case 0: printf("退出程序\n"); break ;
        }
        Keygo();
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值