本文章写于操作系统实验,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;
}