调度的基本概念:从就绪队列中按照一定的算法选择一个进程并将处理机分配给它运行,以实现进程并发地执行。
进程信息
1 struct node { 2 string name;//进程名称 3 int id;//进程id 4 int time;//进程服务时间 5 int rtime;//进程服务时间(主要用于时间片轮转算法) 6 int level;//进程优先级 7 int start;//进程提交时间 8 int lst;//进程调度时间 9 };
1 set<string> pname;//存放进程名称,防止创建重复进程 2 queue<node> qq;//时间片轮转时用到的就绪队列 3 queue<node> pp;//进程的执行队列 4 queue<node> db;//时间片算法中的调度顺序 5 priority_queue<node, vector<node>, cmpspf> spf;//短时间优先算法队列 6 priority_queue<node, vector<node>, cmpfpf> fpf;//优先级算法队列 7 vector<node> ready;//就绪队列 8 vector<node> emy;//已删除的进程
用vector容器存放就绪的进程(每插入一个,sort一下,依据进程提交时间升序排列)
spf(短作业优先算法)
算法思想:服务时间短的进程在就绪队列前面。
算法规则:要求服务时间最短的进程/作业优先得到服务。
算法实现:模拟时间,将已提交的进程插入优先队列,在依据时间是否到达完成时间来判断哪个进程被移入内存中运行
代码:
1 struct cmpspf { 2 bool operator() (node a, node b) { 3 if (a.time == b.time)return a.start > b.start; 4 return a.time > b.time; 5 } 6 }; 7 8 int j = 0, num = ready.size(),ok=1; 9 sum = -1, ans = 0, avg = 0.00; 10 //sum作为在执行进程的完成时间 11 if (t == 1) { 12 for(int i=0;i<=100000;i++) { 13 if (i == sum) {//首先判断是否到达在执行进程的完成时间 14 node temp; 15 temp = spf.top(); spf.pop(); 16 temp.lst = i - temp.start;//计算周转时间 17 ans += temp.lst;//总的周转时间 18 avg += double(temp.lst) / double(temp.time);//总的带权周转时间 19 pp.push(temp);//执行完毕的进程放入执行队列 20 if (!spf.empty())sum += spf.top().time; 21 } 22 while (j < num && i == ready[j].start) {//将到达进程提交时间的进程放入就绪队列 23 spf.push(ready[j]); 24 //当CPU执行过程中出现空闲时,更新sum值 25 if (i > sum&&sum<=spf.top().start)sum = spf.top().start + spf.top().time; 26 j++; 27 } 28 if (ok&&!spf.empty()) {//第一个执行的进程的完成时间 29 sum = i + spf.top().time; 30 ok = 0; 31 } 32 if (j == num && spf.empty())break;//所有进程执行完毕 33 } 34 printf("进程 周转时间 带权周转时间\n"); 35 while (!pp.empty()) { 36 node out; 37 out = pp.front(); pp.pop(); 38 cout << out.name; 39 printf(" %d %.2f\n", out.lst, double(out.lst) / double(out.time)); 40 } 41 printf("平均周转时间为:%.2f\n", double(ans) / double(num)); 42 printf("平均带权周转时间为%.2f\n", avg);
fpf(优先级调度算法)
算法思想:进程优先级高的进程在就绪队列前面。
算法规则:要求进程优先级高的进程/作业优先得到服务。
算法实现:模拟时间,将已提交的进程插入优先队列,在依据时间是否到达完成时间来判断哪个进程被移入内存中运行
代码:逻辑跟spf算法是一样的这里不过多叙述。
1 //spf、fpf的区别就在于优先队列中的规则不同 2 struct cmpfpf { 3 bool operator() (node a, node b) { 4 if (a.level == b.level)return a.start > b.start; 5 return a.level < b.level; 6 } 7 };
for(int i=0;i<=10000;i++) { if (i == sum) { node temp; temp = fpf.top(); fpf.pop(); temp.lst = i - temp.start; ans += temp.lst; avg += double(temp.lst) / double(temp.time); pp.push(temp); if (!fpf.empty())sum += fpf.top().time; } while (j < num && i == ready[j].start) { fpf.push(ready[j]); if (i > sum&&sum<=fpf.top().start)sum = fpf.top().start + fpf.top().time; j++; } if (ok&&!fpf.empty()) { sum = i + fpf.top().time; ok = 0; } if (j == num && fpf.empty())break; } printf("进程 周转时间 带权周转时间\n"); while (!pp.empty()) { node out; out = pp.front(); pp.pop(); cout << out.name; printf(" %d %.2f\n", out.lst, double(out.lst) / double(out.time)); } printf("平均周转时间为:%.2f\n", double(ans) /double(num)); printf("平均带权周转时间为%.2f\n", avg);
时间片轮转算法
算法思想:公平的、轮流的为各个进程服务,让每个进程在一定时间间隔内都可以得到响应
算法规则:系统根据FCFS策略,将所有的就绪进程排成一个就绪队列。
轮流让各个进程执行一个时间片的,若进程未在一个时间片内执行完,则被剥夺处理机,将进程放到就绪队列队尾重新排队。
算法实现:利用队列模拟就绪队列,模拟时间,每次时间增加一个时间片长度,先判断是否有进程在时间片内结束,如果有的话,就对时间进行修改回退到刚完成进程的时间,再判断时间片内是否有进程提交,有的话加入队列。
代码
1 printf("请设置时间片大小:\n"); 2 sf(m); 3 for (int i = 0; i <= 100000; i += m) {//每次自增一个时间片 4 if (!qq.empty()) {//当运行队列有进程时,则运行该进程 5 node temp; 6 temp = qq.front(); qq.pop(); 7 db.push(temp); 8 if (temp.time > m) {//若进程不能在该时间片内运行完毕,则将服务时间减去时间片,再重新放入队列,这也是使用rtime计算带权周转时间的原因 9 temp.time -= m; 10 qq.push(temp); 11 } 12 else {//反之回退时间,并将已完成的进程放入执行完毕队列 13 i =i- m + temp.time; 14 temp.lst = i - temp.start; 15 ans += temp.lst; 16 pp.push(temp); 17 } 18 } 19 while (j < num && i >= ready[j].start) {//到达时间片的进程放入队列 20 if (ok||qq.empty()) { 21 i = ready[j].start; 22 ok = 0; 23 } 24 ready[j].rtime = ready[j].time; 25 qq.push(ready[j]); 26 j++; 27 } 28 if (j == num && qq.empty())break; 29 } 30 printf("进程调度顺序:\n"); 31 while(!db.empty()){cout<<db.front().name<<" ";db.pop();} 32 printf("\n进程执行完毕顺序 周转时间 带权周转时间\n"); 33 while (!pp.empty()) { 34 node out; 35 out = pp.front(); pp.pop(); 36 cout << out.name; 37 printf(" %d %.2f\n", out.lst, double(out.lst) / double(out.rtime)); 38 avg += double(out.lst) / double(out.rtime); 39 } 40 printf("平均周转时间%.2f\n", double(ans) / double(num)); 41 printf("平均带权周转时间为%.2f\n", avg/double(num));
总代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 typedef vector<int> vi; 5 typedef pair<int, int> ii; 6 #define inf 1e9 7 #define F first 8 #define S second 9 #define dbg(x) cout<<#x<<" value : "<<x<<"\n"; 10 #define rep(i,j,k) for(int i = (j); i <= (k); i++) 11 #define rep__(i,j,k) for(int i = (j); i < (k); i++) 12 #define per(i,j,k) for(int i = (j); i >= (k); i--) 13 #define per__(i,j,k) for(int i = (j); i > (k); i--) 14 #define mst(a,b) memset(a,b,sizeof(a)) 15 #define sf(n) scanf_s("%d",&n) 16 #define stf(n,m) scanf("%d%d",&n,&m) 17 #define pf(n) printf("%d\n",n) 18 #define slf(n) scanf("%lld",&n) 19 #define plf(n) printf("%lld\n",n) 20 const int N = 1e3 + 10; 21 priority_queue<int, vector<int>, less<int> > q; 22 int t, x, sum, ans, m; 23 double avg; 24 string k; 25 struct node { 26 string name;//进程名称 27 int id;//进程id 28 int time;//进程服务时间 29 int rtime;//进程服务时间(主要用于时间片轮转算法) 30 int level;//进程优先级 31 int start;//进程提交时间 32 int lst;//进程调度时间 33 }; 34 struct cmpspf { 35 bool operator() (node a, node b) { 36 if (a.time == b.time)return a.start > b.start; 37 return a.time > b.time; 38 } 39 }; 40 struct cmpfpf { 41 bool operator() (node a, node b) { 42 if (a.level == b.level)return a.start > b.start; 43 return a.level < b.level; 44 } 45 }; 46 set<string> pname;//存放进程名称,防止创建重复进程 47 queue<node> qq;//时间片轮转时用到的就绪队列 48 queue<node> pp;//进程的执行队列 49 queue<node> db;//时间片算法中的调度顺序 50 priority_queue<node, vector<node>, cmpspf> spf;//短时间优先算法队列 51 priority_queue<node, vector<node>, cmpfpf> fpf;//优先级算法队列 52 vector<node> ready;//就绪队列 53 vector<node> emy;//已删除的进程 54 bool cmp(const node& a, const node& b) { 55 return a.start < b.start; 56 } 57 void create() { 58 node a; 59 printf("请输入新进程的名称:\n"); 60 cin >> a.name; 61 if (pname.find(a.name) != pname.end()) { 62 printf("进程已存在,请从新输入:\n"); 63 create(); 64 return; 65 } 66 pname.insert(a.name); 67 printf("请输入新进程的到达时间、服务时间:\n"); 68 sf(a.start);sf(a.time); 69 printf("请输入新进程的PID:\n");sf(a.id); 70 printf("请输入新进程的优先级:\n");sf(a.level); 71 ready.push_back(a); 72 sort(ready.begin(), ready.end(), cmp); 73 } 74 void kill() { 75 node b; 76 printf("请输入要终止的进程名字\n"); 77 cin >> k; 78 if (pname.find(k) != pname.end()) { 79 int num = ready.size(); 80 for (int i = 0; i < num; i++) { 81 if (ready[i].name == k) { 82 b = ready[i]; 83 emy.push_back(b); 84 ready.erase(ready.begin() + i); 85 printf("终止进程成功!\n"); 86 } 87 if (num == ready.size()) { 88 printf("该进程已在空队列中!\n"); 89 } 90 } 91 } 92 else { 93 printf("该进程不存在,请输入正确的进程名!\n"); 94 kill(); 95 return; 96 } 97 } 98 void display() { 99 while (!pp.empty())pp.pop(); 100 while (!spf.empty())spf.pop(); 101 while (!fpf.empty())fpf.pop(); 102 while (!qq.empty())qq.pop(); 103 if (ready.empty()) { 104 printf("就绪队列为空!\n"); 105 return; 106 } 107 printf("请选择调度算法\n"); 108 printf("1、spf调度算法\n"); 109 printf("2、fpf调度算法\n"); 110 printf("3、时间片轮转算法\n"); 111 printf("4、返回菜单\n"); 112 sf(t); 113 int j = 0, num = ready.size(),ok=1; 114 sum = -1, ans = 0, avg = 0.00; 115 //sum作为在执行进程的完成时间 116 if (t == 1) { 117 rep(i, 0, 100000) { 118 if (i == sum) {//首先判断是否到达在执行进程的完成时间 119 node temp; 120 temp = spf.top(); spf.pop(); 121 temp.lst = i - temp.start;//计算周转时间 122 ans += temp.lst;//总的周转时间 123 avg += double(temp.lst) / double(temp.time);//总的带权周转时间 124 pp.push(temp); 125 if (!spf.empty())sum += spf.top().time; 126 } 127 while (j < num && i == ready[j].start) {//将到达进程提交时间的进程放入就绪队列 128 spf.push(ready[j]); 129 //当CPU执行过程中出现空闲时,更新sum值 130 if (i > sum&&sum<=spf.top().start)sum = spf.top().start + spf.top().time; 131 j++; 132 } 133 if (ok&&!spf.empty()) {//第一个执行的进程的完成时间 134 sum = i + spf.top().time; 135 ok = 0; 136 } 137 if (j == num && spf.empty())break;//所有进程执行完毕 138 } 139 printf("进程 周转时间 带权周转时间\n"); 140 while (!pp.empty()) { 141 node out; 142 out = pp.front(); pp.pop(); 143 cout << out.name; 144 printf(" %d %.2f\n", out.lst, double(out.lst) / double(out.time)); 145 } 146 printf("平均周转时间为:%.2f\n", double(ans) / double(num)); 147 printf("平均带权周转时间为%.2f\n", avg); 148 } 149 else if (t == 2) { 150 rep(i, 0, 100000) { 151 if (i == sum) { 152 node temp; 153 temp = fpf.top(); fpf.pop(); 154 temp.lst = i - temp.start; 155 ans += temp.lst; 156 avg += double(temp.lst) / double(temp.time); 157 pp.push(temp); 158 if (!fpf.empty())sum += fpf.top().time; 159 } 160 while (j < num && i == ready[j].start) { 161 fpf.push(ready[j]); 162 if (i > sum&&sum<=fpf.top().start)sum = fpf.top().start + fpf.top().time; 163 j++; 164 } 165 if (ok&&!fpf.empty()) { 166 sum = i + fpf.top().time; 167 ok = 0; 168 } 169 if (j == num && fpf.empty())break; 170 } 171 printf("进程 周转时间 带权周转时间\n"); 172 while (!pp.empty()) { 173 node out; 174 out = pp.front(); pp.pop(); 175 cout << out.name; 176 printf(" %d %.2f\n", out.lst, double(out.lst) / double(out.time)); 177 } 178 printf("平均周转时间为:%.2f\n", double(ans) / double(num)); 179 printf("平均带权周转时间为%.2f\n", avg); 180 } 181 else if (t == 3) { 182 printf("请设置时间片大小:\n"); 183 sf(m); 184 for (int i = 0; i <= 100000; i += m) {//每次自增一个时间片 185 if (!qq.empty()) {//当运行队列有进程时,则运行该进程 186 node temp; 187 temp = qq.front(); qq.pop(); 188 db.push(temp); 189 if (temp.time > m) {//若进程不能在该时间片内运行完毕,则将服务时间减去时间片,再重新放入队列,这也是使用rtime计算带权周转时间的原因 190 temp.time -= m; 191 qq.push(temp); 192 } 193 else {//反之回退时间,并将已完成的进程放入执行完毕队列 194 i =i- m + temp.time; 195 temp.lst = i - temp.start; 196 ans += temp.lst; 197 pp.push(temp); 198 } 199 } 200 while (j < num && i >= ready[j].start) {//到达时间片的进程放入队列 201 if (ok||qq.empty()) { 202 i = ready[j].start; 203 ok = 0; 204 } 205 ready[j].rtime = ready[j].time; 206 qq.push(ready[j]); 207 j++; 208 } 209 if (j == num && qq.empty())break; 210 } 211 printf("进程调度顺序:\n"); 212 while (!db.empty()) { cout << db.front().name << " "; db.pop(); } 213 printf("\n进程执行完毕顺序 周转时间 带权周转时间\n"); 214 printf("进程 周转时间 带权周转时间\n"); 215 while (!pp.empty()) { 216 node out; 217 out = pp.front(); pp.pop(); 218 cout << out.name; 219 printf(" %d %.2f\n", out.lst, double(out.lst) / double(out.rtime)); 220 avg += double(out.lst) / double(out.rtime); 221 } 222 printf("平均周转时间%.2f\n", double(ans) / double(num)); 223 printf("平均带权周转时间为%.2f\n", avg/double(num)); 224 } 225 else if (t == 4) { 226 return; 227 } 228 else { 229 printf("输入有误,请按照提示输入:\n"); 230 display(); 231 return; 232 } 233 } 234 inline void meun() { 235 printf("******************菜单************************\n\n"); 236 printf("********** 1、输入进程 ***************\n"); 237 printf("********** 2、输出队列 ***************\n"); 238 printf("********** 3、终止进程 ***************\n"); 239 printf("********** 4、退出程序 ***************\n"); 240 } 241 void solve() { 242 while (1) { 243 meun(); 244 sf(x); 245 switch (x) { 246 case 1: 247 create(); 248 break; 249 case 2: 250 display(); 251 break; 252 case 3: 253 kill(); 254 break; 255 case 4: 256 return; 257 default: 258 printf("请按照提示信息进行输入\n"); 259 break; 260 } 261 } 262 return; 263 } 264 265 int main() 266 { 267 solve(); 268 return 0; 269 }
可以补充的地方,阻塞进程,加入新的vector,每阻塞一个进程,就将其在ready中删除放入新的vector中,唤醒时在阻塞队列中找到该进程,将其移回ready队列。
其实代码写的不是很漂亮,懒省事用了stl,没写链表,时间也是模拟出来的跟真实情况有很大差别,实现的效率也不高(进程少了还行,多了就挺耗时间的,草率地说是O(log(n!)的算法)。这次代码实现的三种算法都是非抢占式的算法,实际情况要比这复杂的多,代码仅供参考,欢迎大家提意见。
优点就是容易想,算是非常暴力的模拟了;能随便加进程,不断更新调度顺序。以后看情况会把程序给完善完善。