【操作系统】多级反馈队列算法
- 原理介绍
- RR时间片轮转原理
在采用时间片轮转算法中,所有的就绪进程按FCFS策略排成一个就绪队列。系统可设置每隔一定时间便产生一次中断,去激活进程调度程序进行调度,把CPU分配给队首进程,并令其执行一个时间片。当它运行完毕后,又把处理机分配给就绪队列中新的队首进程,也让它执行一个时间片。这样,就可以保证就绪队列中的所有进程在确定的时间段内,都能获得一个时间片的处理机时间。
在RR调度算法中进程的切换,可分为两种情况:①若一个时间片尚未用完,正在运行的进程便已经完成,就立即激活调度程序,将它从就绪队列中删除,再调度就绪队列中队首的进程运行,并启动一个新的时间片。②在一个时间片用完时,计时器中断处理程序被激活。如果进程尚未运行完毕,调度程序将把它送往就绪队列的末尾。
2.多级反馈队列调度机制
设置多个就绪队列。在系统中设置多个就绪队列,并为每个队列赋予不同的优先。第一个队列的优先级最高,第二个次之,其余队列的优先级逐个降低。该算法为不同列中的进程所赋予的执行时间片的大小也各不相同,在优先级愈高的队列中,其时间片愈小。
每个队列都采用FCFS算法。当新进程进入内存后,首先将它放入第一队列的末尾,按FCFS原则等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可撤离系统。否则,即它在一个时间片结束时尚未完成,调度程序将其转入第二队列的末尾等待调度;如果它在第二队列中运行个时间片后仍未完成, 再依次将它放入第三队列...依此类推。当进程最后被降到第n队列后,在第n队列中便采取按RR方式运行。
按队列优先级调度。调度程序首先调度最高优先级队列中的诸进程运行,仅当第一队列空闲时才调度第二队列中的进程运行;仅当第1到(i-1)所有队列均空时,才会调度第i队列中的进程运行。如果处理机正在第i队列中为某进程服务时又有新进程进入任一优先级较高的队列,此时须立即把正在运行的进程放回到第i队列的末尾,而把处理机分配给新到的高优先级进程。
2.设计原理
PCB设计
设计结构体PCB,其中包括进程的标识符,到达时间,服务时间,完成时间,周转时间和带权周转时间。利用动态数组将所有进程存储起来。
1 struct PCB 2 3 { 4 5 int ID; //标识符 6 7 int ComeTime; //到达时间 8 9 int ServerTime; //服务时间 10 11 int FinishTime; //完成时间 12 13 int TurnoverTime; //周转时间 14 15 double WeightedTurnoverTime; //带权周转时间 16 17 };
进程就绪队列
采用STL中的队列设置3个队列,queue<PCB> Ready[3];
时间片由数组timeslice[3]表示,大小分别为2,4,8。
函数模块
主函数模块分为3个模块:读入数据函数,多级反馈调度函数,输出函数。
3.流程图
4.代码实现
1 #include <iostream> 2 #include <algorithm> 3 #include <iomanip> 4 #include <vector> 5 #include <queue> 6 using namespace std; 7 8 //作业结构体 9 typedef struct PCB 10 { 11 int ID; //标识符 12 int ComeTime; //到达时间 13 int ServerTime; //服务时间 14 int FinishTime; //完成时间 15 int TurnoverTime; //周转时间 16 double WeightedTurnoverTime; //带权周转时间 17 }PCB; 18 const int QueueNum = 3;//就绪队列长度 19 int timeslice[QueueNum];//第一个时间片 20 21 22 23 //输入作业信息 24 void InputPCB(vector<PCB> &PCBList, int xlice[]) 25 { 26 27 cout << "输入3个时间片大小: "<<endl; 28 for(int i=0;i<3;i++){ 29 cin >> timeslice[i]; 30 } 31 32 while(1) { 33 PCB temp; 34 cout << "输入标识符 "<<"到达时间 "<<"服务时间 "<<endl; 35 cin >> temp.ID>> temp.ComeTime>> temp.ServerTime; 36 temp.FinishTime = 0; //暂时存放运行了多少时间,来判断此作业是否运行结束 37 PCBList.push_back(temp); 38 cout<<"继续?Y/N:"<<endl; 39 char n; 40 cin>>n; 41 if(n=='Y'||n=='y') 42 continue; 43 else 44 break; 45 46 } 47 } 48 49 50 bool CmpByComeTime(const PCB &p1, const PCB &p2) 51 { 52 return p1.ComeTime < p2.ComeTime; 53 } 54 55 56 //MFQ算法 57 void MFQ(vector<PCB> &PCBList, int timeslice[]) 58 { 59 sort(PCBList.begin(), PCBList.end(), CmpByComeTime); //按到达时间排序 60 vector<PCB> result; //保存结果 61 int BeginTime = (*PCBList.begin()).ComeTime; //第一个作业开始时间 62 queue<PCB> Ready[QueueNum]; //设置3个就绪队列 63 Ready[0].push(*PCBList.begin()); 64 PCBList.erase(PCBList.begin()); 65 cout<<"当前时刻:"<<BeginTime<<" 当前就绪队列:"<<0<<endl; 66 cout<<"第一个进程进入就绪队列0"<<endl; 67 cout<<endl; 68 while (!PCBList.empty())//进程数组不空 69 { 70 //这段是为了防止前面的进程运行完了,后面的进程还没到,造成死循环 71 bool flag = false; 72 for (int i = 0; i < QueueNum; ++i) 73 { 74 if (!Ready[i].empty()) 75 { 76 flag = true; 77 break; 78 } 79 } 80 if(!flag) 81 { 82 Ready[0].push(*PCBList.begin()); 83 PCBList.erase(PCBList.begin()); 84 BeginTime = Ready[0].front().ComeTime; 85 } 86 87 for (int i = 0; i < QueueNum; ++i) 88 { 89 90 if (i != QueueNum - 1) //不是最后一个队列 91 { 92 while (!Ready[i].empty()) //当前队列不空 93 { 94 if (!PCBList.empty() && BeginTime>= (*PCBList.begin()).ComeTime) //有新作业到达,加入就绪队列,转到第一队列 95 { 96 cout<<"当前时刻:"<<BeginTime<<endl; 97 cout<<"新进程"<<(*PCBList.begin()).ID<<"到达,将其放在第一队列尾部"<<endl; 98 cout<<endl; 99 Ready[0].push(*PCBList.begin()); 100 PCBList.erase(PCBList.begin()); 101 i = 0; 102 103 continue; 104 } 105 106 if (Ready[i].front().FinishTime + timeslice[i] < Ready[i].front().ServerTime) //时间片用完没运行完,加入下一队列队尾 107 { 108 cout<<"当前时刻:"<<BeginTime+ timeslice[i]<<" 当前就绪队列:"<<i<<endl; 109 cout<<"当前进程"<<Ready[i].front().ID<<"在时间片内没有运行完,加入下一个队列尾部!"<<endl; 110 cout<<endl; 111 112 Ready[i].front().FinishTime += timeslice[i] ; 113 Ready[i + 1].push(Ready[i].front());//加入下一个队列 114 Ready[i].pop(); 115 BeginTime += timeslice[i] ; 116 } 117 else //此作业运行完 118 { 119 BeginTime += Ready[i].front().ServerTime - Ready[i].front().FinishTime; 120 Ready[i].front().FinishTime = BeginTime; 121 Ready[i].front().TurnoverTime = Ready[i].front().FinishTime - Ready[i].front().ComeTime; 122 Ready[i].front().WeightedTurnoverTime = (double)Ready[i].front().TurnoverTime / Ready[i].front().ServerTime; 123 cout<<"当前时刻:"<<BeginTime<<" 当前就绪队列:"<<i<<endl; 124 cout<<"当前进程"<< Ready[i].front().ID<<"在时间片内运行完!"<<endl; 125 cout<<endl; 126 //从就绪队列中移除作业 127 result.push_back(Ready[i].front()); 128 Ready[i].pop(); 129 } 130 } 131 } 132 else 133 { 134 while (!Ready[i].empty()) 135 { 136 if (!PCBList.empty() && BeginTime >= (*PCBList.begin()).ComeTime) //有新作业到达,加入就绪队列,转到第一队列 137 { 138 cout<<"当前时刻:"<<BeginTime<<endl; 139 cout<<"新进程"<<(*PCBList.begin()).ID<<"到达,将其放在最后队列尾部"<<endl; 140 cout<<endl; 141 Ready[0].push(*PCBList.begin()); 142 PCBList.erase(PCBList.begin()); 143 i = -1; 144 break; 145 } 146 if (Ready[i].front().FinishTime + timeslice[i] < Ready[i].front().ServerTime) //时间片用完没运行完,加入队尾 147 { cout<<"当前时刻:"<<BeginTime+ timeslice[i]<<" 当前就绪队列:"<<i<<endl; 148 cout<<"当前进程"<<Ready[i].front().ID<<"在时间片内没有运行完,加入该队列尾部!"<<endl; 149 cout<<endl; 150 Ready[i].front().FinishTime += timeslice[i] ; 151 Ready[i].push(Ready[i].front()); 152 Ready[i].pop(); 153 BeginTime += timeslice[i] ; 154 } 155 else //此作业运行完 156 { 157 BeginTime += Ready[i].front().ServerTime - Ready[i].front().FinishTime; 158 Ready[i].front().FinishTime = BeginTime; 159 Ready[i].front().TurnoverTime = Ready[i].front().FinishTime - Ready[i].front().ComeTime; 160 Ready[i].front().WeightedTurnoverTime = (double)Ready[i].front().TurnoverTime / Ready[i].front().ServerTime; 161 cout<<"当前时刻:"<<BeginTime<<" 当前就绪队列:"<<i<<endl; 162 cout<<"当前进程"<< Ready[i].front().ID<<"在时间片内运行完!"<<endl; 163 cout<<endl; 164 //从就绪队列中移除作业 165 result.push_back(Ready[i].front()); 166 Ready[i].pop(); 167 } 168 } 169 } 170 } 171 } 172 173 //按ComeTime升序排序,便于显示结果 174 PCBList = result; 175 sort(PCBList.begin(), PCBList.end(), CmpByComeTime); 176 } 177 178 //显示结果 179 void show(vector<PCB> &PCBList) 180 { 181 int SumTurnoverTime = 0; 182 double SumWeightedTurnoverTime = 0; 183 184 cout<<"标识符 "<<"达到时间 "<<"服务时间 "<<"完成时间 "<<"周转时间 "<<"带权周转时间" <<endl; 185 186 for (vector<PCB>::iterator it = PCBList.begin(); it < PCBList.end(); ++it){ 187 cout<<(*it).ID<<" "<<(*it).ComeTime<<" "<< (*it).ServerTime<<" "<<(*it).FinishTime<<" "<<(*it).TurnoverTime<<" "<< (*it).WeightedTurnoverTime<<endl; 188 SumTurnoverTime+=(*it).TurnoverTime; 189 SumWeightedTurnoverTime+=(*it).WeightedTurnoverTime; 190 } 191 192 cout << "平均周转时间: " << (double)SumTurnoverTime / PCBList.size() << endl; 193 cout << "平均带权周转时间: " << SumWeightedTurnoverTime / PCBList.size() << endl; 194 } 195 196 //比较函数,按ComeTime升序排列 197 198 199 200 int main() 201 { 202 vector<PCB> PCBList;//动态数组存放进程 203 204 InputPCB(PCBList, timeslice);//输入时间片大小,作业信息 205 206 MFQ(PCBList, timeslice); 207 208 show(PCBList);//显示结果 209 210 return 0; 211 } 212
5.实例测试
结果分析:
如图5所示,0时刻,1进程到达,运行1个时间片2,此时进程没有运行完,加入下一队列,进程2到达,转到队列0调度进程2,运行1个时间片2,进入队列1队尾等待。
时刻4开始,第一队列为空,此时调度第二队列,在进程1运行1个时间片4,没有运行完,加入队列2。
时刻8,进程3到达,加入到第一队列尾部,调度进程3,进程3运行1个时间片2之后,加入下一队列。
时刻10,队列1中进程2开始运行,时刻12运行结束。删除进程2.进程3开始运行,当前时间片内运行不完,加到队列2队尾。
时刻16,进入队列2,调度进程1进行运行,时刻20,进程1运行结束,删除进程1。运行进程3,在当前时间片下不能运行完,则加入队列2。
时刻28,进入队列2,调度进程3,时刻29运行完毕。