进程调度基本方法及实现
进程的四大特点: 并发、共享、虚拟、异步。
进程调度便是实现并发的关键一环。
在操作系统中存在多种调度算法,其中有的调度算法适用于作业调度,有的调度算法适用于进程调度,有的调度算法两者都适用。
1.先来先服务(FCFS)
一种简单的调度算法,适用于作业和进程调度。先来先服务算法按照进程/作业到达先后顺序来进行调度。当作业调度采用该算法时,每次调度都会从后备队列中取出最先到达的作业,为他分配内存,创建PCB,放入就绪队列中;当进程调度采用该算法时,每次调度都会从就绪队列中取出最先进入该队列的进程,给他分配处理机(处理机=CPU+主存储器+IO设备)。
2.短作业优先(SJF)
作业或进程的长短是以作业或进程要求运行时间的长短来衡量的。
3.优先级调度
作业或进程的优先级来确定优先调度权。
(1)静态优先级 --- 优先级在进程/作业调度前就确定并不会更改。
(2)动态优先级 --- 优先级会随进程的执行情况而改变,更灵活,科学。
4.时间片轮转法(RR)
主要用于分时系统的进程调度。进程/作业放在一个队列上,CPU拿出第一个进程运行一个时间片后,将其放在队尾,轮询执行。
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<string.h> #include <algorithm> using namespace std; int choice; //选项 int n; //进程数 bool ok[100] = {0}; //作业完成情况 1为完成 0为未完成 int last[100] = { 0 }; //任务剩余时间 struct pcb{ int pid; //进程号 int atime; //到达时间 int rtime; //运行时间 int stime; //开始时间 int etime; //结束时间 int time; //周转时间 float dtime; //带权周转时间 int priority; //优先级 int timechip; //时间片 }pro[100]; //最大进程数组,输入输出参数 //先来先服务算法进程排序规则, 先按照到达时间升序,再按照进程号升序排列 bool fcfscmp(pcb a,pcb b) { if (a.atime != b.atime)return a.atime < b.atime; return a.pid < b.pid; } //动态优先级算法进程排序规则,先按照进程优先数升序,再按到达时间升序,再按进程号升序排列 bool pricmp(pcb a, pcb b) { if (a.priority != b.priority)return a.priority < b.priority; if (a.atime != b.atime)return a.atime < b.atime; return a.pid < b.pid; } //先来先服务 void FCFS() { int t = 0; int dt = 0; //排序到达时间最早的作业 sort(pro + 1, pro + 1 + n, fcfscmp); //依次完成作业 printf("\n==== PID 到达时间 运行时间 开始时间 结束时间 周转时间 带权周转时间 ====\n"); for (int i = 1; i <= n; i++) { pro[i].stime = max(t, pro[i].atime);//开始时间 = max(系统时间,到达时间) pro[i].etime = pro[i].stime + pro[i].rtime; pro[i].time = pro[i].etime - pro[i].atime; //周转时间 = 结束 - 到达 pro[i].dtime = pro[i].time/pro[i].rtime; //带权周转时间 = 周转时间/运行时间 //总周转时间,总带权周转时间 t += pro[i].time; dt += pro[i].dtime; printf(" %d %d %d %d %d %d %3.3f\n", pro[i].pid, pro[i].atime, pro[i].rtime, pro[i].stime,pro[i].etime,pro[i].time,pro[i].dtime); } float avet = (float)t / n; float avedt = (float)dt / n; printf("平均周转时间为%3.3f,平均带权周转时间为%3.3f\n",avet,avedt); } //短作业优先 void SPF() { //作业完成情况先置0 memset(ok, 0, sizeof(ok)); int t = 0; int dt = 0; int min = 1; printf("\n==== PID 到达时间 运行时间 开始时间 结束时间 周转时间 带权周转时间 ====\n"); for (int i = 1; i <= n; i++) { //1.找出当前最短时间作业 int flag = 0; //标记是否有到达作业 while (flag == 0){ for (int j = 1; j <= n; j++) { if (ok[j] == 1)continue; //已经运行完成的进程 if (pro[j].atime > t)continue; //没有到达的进程 //出现一个已到达进程 if (pro[j].atime <= t && flag == 0) { min = j; flag = 1; } //出现两个已到达进程,比较出最短时间进程 else if (pro[j].atime <= t && flag == 1) { if ((pro[j].rtime < pro[min].rtime) || (pro[j].rtime == pro[min].rtime&&pro[j].pid < pro[min].pid)) { //运行时间短的进程优先 运行时间相同时,按顺序 min = j; } } }// end of for //此时,没有进程到达,时间+1 if (flag == 0) t++; } //2.完成作业 pro[i].stime = max(t, pro[min].atime); pro[i].etime = pro[i].stime + pro[min].rtime; pro[i].time = pro[i].etime - pro[i].atime; //周转时间 = 结束 - 到达 pro[i].dtime = pro[i].time / pro[i].rtime; //带权周转时间 = 周转时间/运行时间 //总周转时间,总带权周转时间 t += pro[i].time; dt += pro[i].dtime; printf(" %d %d %d %d %d %d %3.3f\n", pro[i].pid, pro[i].atime, pro[i].rtime, pro[i].stime, pro[i].etime, pro[i].time, pro[i].dtime); ok[min] = 1; //标记这个进程完成 } float avet = (float)t / n; float avedt = (float)dt / n; printf("平均周转时间为%3.3f,平均带权周转时间为%3.3f\n", avet, avedt); } /*判断是否全部进程都执行完毕*/ int charge() { for (int i = 1; i<=n; i++) { if (last[i] != 0) return 1; } return 0; } //轮转法 void RR() { //作业完成数组置0 memset(ok, 0, sizeof(ok)); int t = 0 , dt = 0; sort(pro + 1, pro + n + 1, fcfscmp);//排序:先来先服务原则 --- 有利于后面轮询 int i = 0; //*关键 : 记录进程剩余时间 for (int i = 1; i <= n; i++) { last[i] = pro[i].rtime; } int chip = pro[1].timechip; //时间片 int time = pro[1].atime; //当前时间的初值 //按顺序轮询,就绪态运行,阻塞态等待到达时间 printf("\n==== PID 到达时间 运行时间 开始时间 结束时间 周转时间 带权周转时间 ====\n"); while (charge()) //能进charge说明还有进程 { int flag = 0; //标记是否还有就绪任务 for (i = 1; i <= n; i++) //时间片轮转法执行各进程 { if (ok[i] == 1) continue; //已完成的进程 //就绪进程轮转 if (last[i]<=chip && time>=pro[i].atime)//未完成的进程但是还需服务的时间少于等于一个时间片 { flag = 1; //记录第一次开始运行时间 if (pro[i].rtime == last[i]) pro[i].stime = time; //开始时间 = 当前时间 //时间更新,任务完成 time += chip; last[i] = 0; ok[i] = 1; pro[i].etime = time; //结束时间 pro[i].time = pro[i].etime - pro[i].atime; //周转时间 = 结束 - 到达 pro[i].dtime = pro[i].time / pro[i].rtime; //带权周转时间 = 周转时间/运行时间 //总周转时间,总带权周转时间 t += pro[i].time; dt += pro[i].dtime; printf(" %d %d %d %d %d %d %3.3f\n", pro[i].pid, pro[i].atime, pro[i].rtime, pro[i].stime, pro[i].etime, pro[i].time, pro[i].dtime); } else if (last[i]>chip && time>=pro[i].atime)//未完成的进程但其还需服务时间至少大于一个时间片 { flag = 1; //记录第一次开始运行时间 if (last[i]==pro[i].rtime) pro[i].stime = time; //开始时间 = 当前时间 time += chip; last[i] -= chip; } }//end of for //没有一个就绪进程,自增时间 if (flag == 0) { time += chip; } }//end of while float avet = (float)t / n; float avedt = (float)dt / n; printf("平均周转时间为%3.3f,平均带权周转时间为%3.3f\n", avet, avedt); } //优先级调度 void PRI() { //作业完成数组置0 memset(ok, 0, sizeof(ok)); int t = 0, dt = 0; sort(pro + 1, pro + n + 1, pricmp);//排序:优先级 int i = 0; //*关键 : 记录进程剩余时间 for (int i = 1; i <= n; i++) { last[i] = pro[i].rtime; } int chip = pro[1].timechip; //时间片 int time = pro[1].atime; //当前时间的初值 //按优先级顺序轮询,轮询完再次优先级排序 printf("\n==== PID 到达时间 运行时间 开始时间 结束时间 周转时间 带权周转时间 ====\n"); while (charge()) //能进charge说明还有进程 { int flag = 0; //标记是否还有就绪任务 for (i = 1; i <= n; i++) //时间片轮转法执行各进程 { if (ok[i] == 1) continue; //已完成的进程 //就绪进程轮转 if (last[i] <= chip && time >= pro[i].atime)//未完成的进程但是还需服务的时间少于等于一个时间片 { flag = 1; //记录第一次开始运行时间 if (pro[i].rtime == last[i]) pro[i].stime = time; //开始时间 = 当前时间 //时间更新,任务完成 time += chip; last[i] = 0; ok[i] = 1; pro[i].etime = time; //结束时间 pro[i].time = pro[i].etime - pro[i].atime; //周转时间 = 结束 - 到达 pro[i].dtime = pro[i].time / pro[i].rtime; //带权周转时间 = 周转时间/运行时间 //总周转时间,总带权周转时间 t += pro[i].time; dt += pro[i].dtime; printf(" %d %d %d %d %d %d %3.3f\n", pro[i].pid, pro[i].atime, pro[i].rtime, pro[i].stime, pro[i].etime, pro[i].time, pro[i].dtime); } else if (last[i]>chip && time >= pro[i].atime)//未完成的进程但其还需服务时间至少大于一个时间片 { flag = 1; //记录第一次开始运行时间 if (last[i] == pro[i].rtime) pro[i].stime = time; //开始时间 = 当前时间 time += chip; last[i] -= chip; pro[i].priority -= 1; //如果没完成,优先级-1 } }//end of for sort(pro + 1, pro + n + 1, pricmp);//排序:优先级 //没有一个就绪进程,自增时间 if (flag == 0) { time += chip; } }//end of while float avet = (float)t / n; float avedt = (float)dt / n; printf("平均周转时间为%3.3f,平均带权周转时间为%3.3f\n", avet, avedt); } //输入界面 void Menu() { while (1){ n = 0; choice = 0; printf("=== 请选择算法: ===\n* 1.先来先服务 *\n* 2.短作业优先 *\n* 3.时间片轮转 *\n* 4.优先级调度 *\n* 0.退出 *\n选择:"); scanf("%d", &choice); if (choice == 0) return; printf("请选择进程数:"); scanf("%d", &n); printf("*** 请依次写入 ***\n PID 到达时间 运行时间 优先级 时间片大小:\n"); for (int i = 1; i <= n; ++i) { scanf("%d %d %d %d %d", &pro[i].pid, &pro[i].atime, &pro[i].rtime, &pro[i].priority, &pro[i].timechip); } if (n == 0) return; switch (choice) { case 1:FCFS(); break;//先来先服务调度算法 case 2:SPF(); break;//短作业优先调度算法 case 3:RR(); break;//时间片轮转调度算法 case 4:PRI(); break;//优先级调度算法 } printf("\n"); } } int main() { Menu(); return 0; }