什么是调度?调度有什么作用?
调度,顾名思义,就是分配。这就好比是士兵和战场指挥官的关系,当战场上的某一侧士兵满足某个条件时,就会被调到另一边。而这里的线程就相当于士兵,而操作系统和CPU就相当于战场上的指挥官,当满足这些算法里面的条件时,就会被调度。
调度可以最大化利用CPU的性能,尽量的减少CPU的闲置率,从而达到提升性能的效果。
调度算法介绍
调度算法有FCFS,RR,SJF(抢占式和非抢占式),EDF,HRRN(抢占式和非抢占式)等,这个其他的博主已经介绍的非常详细了,我们专注于代码实现。
参考链接:常用的调度算法(包含实例)|操作系统-CSDN博客
HRRN算法简介
HRRN算法(最高响应比算法)分为抢占式和非抢占式两种,每次调度时都会选择响应比最高的进程(现成)进入到执行队列中,当HRRN分别为抢占式或非抢占式时,分别有两种计算方式:
抢占式HRRN:响应比=(W为等待时间,L为该进程剩余时间)
非抢占式HRRN:响应比=(W为等待时间,S为预计执行时间)
请注意,W+S的时间在数值上等于周转时间,但是我们无法在进程执行的时候提前得知具体的周转时长,需要灵活使用。
FCFS算法简介
FCFS(先来先服务)算法,是让进程(线程)按照顺序执行的一种方法,当前一个进程调度结束或报错之前,一般不会进行后一个任务。
HRRN和FCFS的利弊和使用情景
HRRN算法:结合了FCFS和SJF的优点,对长短作业都较公平,但是会进行频繁的线程切换,导致效率较低。需要同时执行长短作业的时候会被使用,例:Windows系统(非抢占式HRRN)
FCFS算法:平均周转时间较长,但是线程切换最少,在性能要求较高的时候会被使用。
代码实现
我们这里在FCFS的部分中使用了队列作为容器,而在HRRN算法中使用了vector作为容器,主要是考虑到精简容器的需求,方法调用更加清晰。以下是代码实现:
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
#define NotFound -1
#define None -1
//我们假设进程会一直执行,在执行完毕之前不会被强制退出或停止
//且会按照进程的下标顺序逐个进行
typedef struct Process
{
int id;//id号
int arrivalTime;//到达时间
int burstTime;//执行时间
int leftTime;//剩余时间
int startTime;//开始时间
int finishTime;//完成时间
int turnaroundTime;//周转时间
//初始化↓
Process(int id,int arrivalTime,int burstTime,int leftTime) :id(id), arrivalTime(arrivalTime), burstTime(burstTime),leftTime(leftTime),startTime(None), finishTime(None), turnaroundTime(None) {};
}Process;
class FCFS_Analysis
{
public:
void caculateFCFSTimes();//求FCFS的开始时间、完成时间、周转时间
void printProcesses();
private:
int nowtime = 0;
int totalTurnaroundTime = 0;
float averageTurnaroundTime = 0;
float totalWeighedTurnaroundTime = 0;
float averageWeighedTurnaroundTime = 0;
vector<Process> processes =
{
{1,0,3,3},
{2,2,6,6},
{3,4,4,4},
{4,6,5,5},
{5,8,2,2},
};
queue<Process> wait_Queue;
queue<Process> bursting_Queue;
};
class HRRN_Analysis//抢占式HRRN
{
public:
void caculateHRRNTimes();
float caculateResponseRatio(const Process& process);
void printProcesses();
int findIndexByID(int targetProcessID);//结构体里的id是小写的
private:
int nowtime = 0;
int totalTurnaroundTime = 0;
float averageTurnaroundTime = 0;
float totalWeighedTurnaroundTime = 0;
float averageWeighedTurnaroundTime = 0;
vector<Process> processes =
{
{1,0,3,3},
{2,2,6,6},
{3,4,4,4},
{4,6,5,5},
{5,8,2,2},
};
queue<Process> wait_Queue;
queue<Process> bursting_Queue;
};
//FCFS算法
void FCFS_Analysis::caculateFCFSTimes()
{
bool allDone = false;
int burstingProcessIndex = 0;//用于while循环,记录的是正在进行的进程,对应的是processes里面的
int arrivingProcessIndex = 0;//指示即将到达的进程在vector里的下标,对应的也是processes里面的
int completeProcessesNum = 0;//记录已经完成的进程,用于后续完成的判断
while (!allDone)
{
//模拟进入等待队列的过程
if (arrivingProcessIndex < processes.size())
{
if (nowtime == processes[arrivingProcessIndex].arrivalTime)
{
if (processes[arrivingProcessIndex].leftTime > 0)//此处加入了leftTime的判定,防止新进程为空
{
wait_Queue.push(processes[arrivingProcessIndex]);//如果nowtime等于到达时间,若burstTime大于0,那么就会加入等待队列,然后等待执行
arrivingProcessIndex++;
}
else
{
arrivingProcessIndex++;
burstingProcessIndex++;
completeProcessesNum++;
}
}
}
//模拟进入执行队列的过程
//这里没有信号量,所以等待队列最多只有一个元素,应该加上信号量,但题目没有给出
if (!wait_Queue.empty())//只需要队列大小判空,因为前面已经对wait_Queue里面的进程执行时间判空过了
{
bursting_Queue.push(wait_Queue.front());
wait_Queue.pop();
}
//模拟在执行队列中执行的过程
if (!bursting_Queue.empty())
{
if (bursting_Queue.front().burstTime == bursting_Queue.front().leftTime)
{
bursting_Queue.front().startTime = nowtime;//如果是新进程,则赋开始时间的值
}
bursting_Queue.front().leftTime--;
nowtime++;
if (bursting_Queue.front().leftTime == 0)
{
//开始结束时间
bursting_Queue.front().finishTime = nowtime;//这个是执行队列出队之前
bursting_Queue.front().turnaroundTime = bursting_Queue.front().finishTime - bursting_Queue.front().arrivalTime;
processes[burstingProcessIndex] = bursting_Queue.front();
bursting_Queue.pop();
burstingProcessIndex++;//因为这里是FCFS,所以可以直接++
completeProcessesNum++;
}
}
else nowtime++;
//判断有没有全部完成
if (completeProcessesNum == processes.size())
{
allDone = true;
for (const Process& tempProcess : processes)
{
totalTurnaroundTime = totalTurnaroundTime + tempProcess.turnaroundTime;
totalWeighedTurnaroundTime = totalWeighedTurnaroundTime + (tempProcess.turnaroundTime / tempProcess.burstTime);
}
averageTurnaroundTime = static_cast<float>(totalTurnaroundTime) / processes.size();
averageWeighedTurnaroundTime = static_cast<float>(totalWeighedTurnaroundTime) / processes.size();
}
}
}
void FCFS_Analysis::printProcesses()
{
cout << "FCFS算法数据如下:" << endl;
for (const Process& tempProcess : processes)
{
cout << "进程id: " << tempProcess.id << "开始时间: " << tempProcess.startTime << "完成时间: " << tempProcess.finishTime << "周转时间: " << tempProcess.turnaroundTime << endl;
}
cout << "平均周转时间: " << averageTurnaroundTime << "平均带权周转时间: " << averageWeighedTurnaroundTime << endl;
}
//HRRN算法
float HRRN_Analysis::caculateResponseRatio(const Process& process)
{
//这个响应比在数值上等于单个进程的带权周转时长,但是没有办法提前得知实际周转时长,所以采用等待时间/剩余时间+1来计算
if (process.leftTime == 0)
{
return 0.0f;
}
else
{
if (process.startTime==None)
{
return static_cast<float>(nowtime - process.arrivalTime) / process.leftTime + 1;
}
else
{
return static_cast<float>(nowtime-process.arrivalTime-(process.burstTime-process.leftTime))/process.leftTime+1;
}
}
}
int HRRN_Analysis::findIndexByID(int targetProcessID)//这个方法是在processes里面找index
{
if (targetProcessID != NotFound)
{
for (int i = 0; i < processes.size(); i++)
{
if (processes[i].id == targetProcessID)
{
return i;
}
}
return NotFound;//若没找到对应id编号的进程,则返回NotFound,作为未找到的判别值
}
else return NotFound;//如果没有给ID赋值,则也是输出NotFound
}
void HRRN_Analysis::caculateHRRNTimes()
{
bool allDone = false;
int burstingProcessIndex = NotFound;//用于while循环,记录的是正在进行的进程,对应的是processes里面的,初始化,因为0号进程是最先到的,所以初始化为0
int arrivingProcessIndex = 0;//指示即将到达的进程在vector里的下标,对应的也是processes里面的
int completeProcessesNum = 0;//记录已经完成进程的数量,用于后续判定完成的判断
vector<Process*> wait_Queue;
vector<Process*> bursting_Queue;
while (!allDone)
{
int selectedProcessIndex = NotFound;
int selectedProcessID = NotFound;
float maxProcessRatio = None;//需要判空,当执行队列为空的时候需要进行额外操作
//模拟进程到达时进入等待队列的过程
if (arrivingProcessIndex < processes.size())//一定要先判定数组有没有越界,不然会报越界错误,因为没有processes[arrivingProcessIndex]这种东西
{
if (nowtime == processes[arrivingProcessIndex].arrivalTime)
{
if (processes[arrivingProcessIndex].leftTime > 0)//此处加入了leftTime的判定,防止新进程为空
{
wait_Queue.push_back(&processes[arrivingProcessIndex]);//如果nowtime等于到达时间,且burstTime大于0,那么就会加入等待队列,然后等待执行
arrivingProcessIndex++;
}
else
{
//这里并没有涉及到当前进行的任务的下标
arrivingProcessIndex++;
completeProcessesNum++;
}
}
}
//以下是最高响应比的计算,需要有两块,一块是执行队列的,一块是等待队列的
//执行队列↓
for(const Process* tempProcess:bursting_Queue)//把所有进程的响应比都计算一遍,然后存储最大值
{
float tempResponseRatioValue = caculateResponseRatio(*tempProcess);//存储一个临时的响应比的值,使代码更直观,也减少了重复计算
if (tempResponseRatioValue > maxProcessRatio)
{
maxProcessRatio = tempResponseRatioValue;
selectedProcessID = tempProcess->id;//得到该进程的下标
}
}
//等待队列↓
for (const Process* tempProcess:wait_Queue)
{
float tempResponseRatioValue = caculateResponseRatio(*tempProcess);//存储一个临时的响应比的值,使代码更直观,也减少了重复计算
if (tempResponseRatioValue > maxProcessRatio)
{
maxProcessRatio = tempResponseRatioValue;
selectedProcessID = tempProcess->id;//得到该进程的下标
}
}
//最后在两个队列中挑选出来的进程下标,先对selectedProcessIndex判空,否则就输出
if (selectedProcessID == NotFound)//但是这里有一个问题,就是会导致忙等,且可能会造成死循环
{
nowtime++;
continue;
}
selectedProcessIndex = findIndexByID(selectedProcessID);//可以直接用,因为当ID为未找到的时候,也能正常调用该方法
//把原先的执行队列放回等待队列,如果进程相同则不用放回,并且存储leftTime用于使用
//因为操作系统在同一时刻只能执行一个任务,所以可以直接把执行队列中的第一个元素出队(这里是以vector为容器)
//要判空,因为很有可能上一个进程刚刚结束,已经从执行队列中移出
bool isSelectedProcessAlreadyInBurstingQueue = false;//判断是不是相同进程,避免重复调用
if (!bursting_Queue.empty())
{
for (int i=0;i<bursting_Queue.size();i++)
{
if (bursting_Queue[i]->id == selectedProcessID)
{
isSelectedProcessAlreadyInBurstingQueue = true;
break;
}
}
if (!isSelectedProcessAlreadyInBurstingQueue)
{
vector<Process*>::iterator it=find(bursting_Queue.begin(),bursting_Queue.end(),&processes[selectedProcessIndex]);
if (it != bursting_Queue.end())
{
wait_Queue.push_back(*it);
bursting_Queue.erase(it);
}
}
}
//模拟进入执行队列的过程
if (!wait_Queue.empty())//只需要队列大小判空,因为前面已经对wait_Queue里面的进程执行时间判空过了
{
if (!isSelectedProcessAlreadyInBurstingQueue)
{
bursting_Queue.push_back(&processes[selectedProcessIndex]);
vector<Process*>::iterator toErase = find(wait_Queue.begin(), wait_Queue.end(), &processes[selectedProcessIndex]);
if (toErase != wait_Queue.end())
{
wait_Queue.erase(toErase);
}
}
}
else
{
if (selectedProcessIndex != NotFound&&!isSelectedProcessAlreadyInBurstingQueue)
{
bursting_Queue.push_back(&processes[selectedProcessIndex]);
}
}
//模拟在执行队列中执行的过程
if (!bursting_Queue.empty())
{
burstingProcessIndex = selectedProcessIndex;
if (processes[burstingProcessIndex].burstTime == processes[burstingProcessIndex].leftTime)
{
processes[burstingProcessIndex].startTime = nowtime;//如果是新进程,则赋开始时间的值
}
processes[burstingProcessIndex].leftTime--;
nowtime++;
if (processes[burstingProcessIndex].leftTime == 0)
{
//开始结束时间
processes[burstingProcessIndex].finishTime = nowtime;//这个是执行队列出队之前的进程
processes[burstingProcessIndex].turnaroundTime = processes[burstingProcessIndex].finishTime - processes[burstingProcessIndex].arrivalTime;
vector<Process*>::iterator toErase = find(bursting_Queue.begin(), bursting_Queue.end(), &processes[burstingProcessIndex]);//找到执行队列中的迭代器,然后使其出队
if (toErase != bursting_Queue.end())
{
bursting_Queue.erase(toErase);
}
completeProcessesNum++;
}
}
else nowtime++;
//判断有没有全部完成
if (completeProcessesNum == processes.size())
{
allDone = true;
for (const Process& tempProcess : processes)
{
totalTurnaroundTime = totalTurnaroundTime + tempProcess.turnaroundTime;
totalWeighedTurnaroundTime = totalWeighedTurnaroundTime + (tempProcess.turnaroundTime / tempProcess.burstTime);
}
//我们默认进程数是正整数
averageTurnaroundTime = static_cast<float>(totalTurnaroundTime) / processes.size();
averageWeighedTurnaroundTime = static_cast<float>(totalWeighedTurnaroundTime) / processes.size();
}
}
}
void HRRN_Analysis::printProcesses()
{
cout << "HRRN算法的数据如下:" << endl;
for (const Process& tempProcess : processes)
{
cout << "进程id: " << tempProcess.id <<"到达时间: "<<tempProcess.arrivalTime << "开始时间: " << tempProcess.startTime << "完成时间: " << tempProcess.finishTime << "周转时间: " << tempProcess.turnaroundTime << endl;
}
cout << "平均周转时间: " << averageTurnaroundTime << "平均带权周转时间: " << averageWeighedTurnaroundTime << endl;
}
int main()
{
FCFS_Analysis FCFS;
FCFS.caculateFCFSTimes();
FCFS.printProcesses();
HRRN_Analysis HRRN;
HRRN.caculateHRRNTimes();
HRRN.printProcesses();
}