**
进程调度算法
**
实验目的及内容
设计进程控制块的数据结构PCB,进程控制块可以包含如下信息:进程号、优先数、到达时间、需要运行时间、已用CPU时间、进程状态等等;进程的优先数及需要的运行时间可以事先指定(也可以由随机数产生)。进程的到达时间为进程输入的时间。进程的运行时间以时间片为单位进行计算。仿真时,每个时间片可以让cpu暂停1秒(或100ms,可自定义)。每个进程的状态可以是就绪W(Wait)、运行R(Run)、或完成F(Finish)三种状态之一。就绪进程获得 CPU后只能运行一个时间片。
要求:对一个非抢占式多道批处理系统采用以下算法的任意两种,实现进程调度,并计算进程的开始执行时间,周转时间,带权周转时间,平均周转时间,平均带权周转时间。
- 先来先服务算法
- 短进程优先算法
- 高响应比优先算法
示例数据如下(也可自定义数据)
进程号 提交时间 执行时间
1 8:00 25分钟
2 8:20 10分钟
3 8:25 20分钟
4 8:30 20分钟
5 8:35 15分钟
实验原理
两个上机任务都是在考虑时间片的基础上考虑两个算法(时间片可以任意设置)。1.先来先服务算法:我使用的是用一个循环模拟CPU的运行,用一个结构体数组来存放各个进程的信息,使用一个链表来存放处于就绪状态的进程,当一个进程被创建完成后,便让该进程插入到当前正在运行进程的前面,当运行到链表的尾部时,将指针重新指到链表的首部;如果有进程已经执行完毕,则将该进程从链表中删除,直到链表为空时,所以进程执行完毕,最后根据进程已被修改的信息计算开始执行时间,周转时间,带权周转时间,平均周转时间,平均带权周转时间。 2. 短进程优先算法:该算法也是使用一个循环来模拟CPU,基本思想:初始时,每个进程的运行的时间片数量为0,每上一个CPU运行,便让时间片加一,当一个进程下CPU时,比较每个进程的运行的时间片数量,选出运行的时间片数量较少的上CPU运行,然后时间片数量一样,则比较长短进程。这可以避免进程饥饿问题。
代码
#include<iostream>
#include<stdlib.h>
#include<time.h>
#include<string>
using namespace std;
/*
进程号、优先数、到达时间、需要运行时间、已用CPU时间、进程状态等等
*/
typedef struct PCB{
int id;//进程号
int arrive;// 到达时间(相对与8:00)
int start;//开始执行的时间
int end;//结束时间
int run;//还需要运行时间
int cpu;//总共需要运行时间
char zhu;//进程状态,s表示进程未被创建,就绪 W(Wait)、运行R(Run)、或完成F(Finish)
int timecounts;//用于记录已经执行了多少个时间片
}PCB;
//就绪链表
typedef struct LNode{
PCB data;
struct LNode *next;
}LNode,*linkList;
int allcpu = 0;//cpu已经运行fjfj的时间
int timecpu = 5;//时间片
//初始化五个
void In(PCB PCB[],linkList &L){
L = (LNode *)malloc(sizeof(LNode));
L->next = NULL;
PCB[0].id = 1;
PCB[0].arrive = 0;
PCB[0].cpu = 25;
PCB[0].end = 0;
PCB[0].run = 25;
PCB[0].start = 0;
PCB[0].zhu = 's';
PCB[0].timecounts = 0;
PCB[1].id = 2;
PCB[1].arrive = 20;
PCB[1].cpu = 10;
PCB[1].end = 0;
PCB[1].run = 10;
PCB[1].start = 0;
PCB[1].zhu = 's';
PCB[1].timecounts = 0;
PCB[2].id = 3;
PCB[2].arrive = 25;
PCB[2].cpu = 20;
PCB[2].end = 0;
PCB[2].run = 20;
PCB[2].start = 0;
PCB[2].zhu = 's';
PCB[2].timecounts = 0;
PCB[3].id = 4;
PCB[3].arrive = 30;
PCB[3].cpu = 20;
PCB[3].end = 0;
PCB[3].run = 20;
PCB[3].start = 0;
PCB[3].zhu = 's';
PCB[3].timecounts = 0;
PCB[4].id = 5;
PCB[4].arrive = 35;
PCB[4].cpu = 15;
PCB[4].end = 0;
PCB[4].run = 15;
PCB[4].start = 0;
PCB[4].zhu = 's';
PCB[4].timecounts = 0;
}
//先来先服务
void FIFO(PCB pro[],linkList &L){
LNode *p;
if(L->next == NULL){//将第一个到达的进程进入就绪队列
p = (LNode *)malloc(sizeof(LNode));
p->data = pro[0];
p->data.start = 0;
p->data.zhu = 'w';
p->next = L->next;
L->next = p;
pro[0].zhu = 'w';
}
LNode *pre,*s;
pre = L;
s = L->next;
bool isdelete = false;//用于判断是否有进程出队列
while(L->next != NULL){
//模拟CPU上运行
if(s->data.run > timecpu){
if(s->data.zhu == 'w'){//将第一次开始执行的精选进行标记
s->data.start = allcpu;
s->data.zhu = 'R';
}
allcpu +=timecpu;
s->data.run -= timecpu;
pro[s->data.id - 1] = s->data;//改变数组里的值
}else{//改进程将要结束
if(s->data.zhu == 'w'){//将第一次开始执行的精选进行标记
s->data.start = allcpu;
s->data.zhu = 'R';
}
allcpu += s->data.run;
s->data.run = 0;
s->data.end = allcpu;
pre->next = s->next;
s->data.zhu = 'F';//将进程状态设为结束态,并将进程从队列中删除
pro[s->data.id - 1] = s->data;//改变数组里的值
free(s);
s = pre ->next;
isdelete = true;
}
//判断哪个进程到达了,将进程放入发到就绪队列中
for(int i = 0;i<5;i++){
if(pro[i].arrive <= allcpu && pro[i].run > 0 && pro[i].zhu == 's'){//放入当前执行进行之前,(相当于队尾)
p = (LNode *)malloc(sizeof(LNode));
p->data = pro[i];
p->data.zhu = 'w';
p->next = pre->next;
pre->next = p;
pre = p;
pro[i].zhu = 'w';
}
}
if(s == NULL){//如果到达队尾(已将一个进程移出队列)
pre = L;
s = L->next;
}else if(!isdelete){//判断是否有进程出队列
if(s->next != NULL){//判断是否到达最后一个节点
pre = s;
s = pre->next;
}else{
pre = L;
s = L->next;
}
isdelete = false;
}
isdelete = false;
}
}
//找出要当前要执行的进程,并返回该进程下标
int Find(PCB pro[]){
int j = 0;//用于存放当前需要上CPU运行的进程的下标
while(pro[j].zhu == 'F'){
j++;
}
for(int i = j + 1;i < 5;i++){
if(pro[i].zhu != 'F' && pro[i].arrive <= allcpu){
//先比较哪个的时间片数量少,如果相等再比较长短进程
if(pro[i].timecounts < pro[j].timecounts){
j = i;
}else if(pro[i].run < pro[j].run){
j = i;
}
}
}
return j;
}
//短进程优先算法
void SJF(PCB pro[]){
allcpu = 0;
//计算所以进程总的需要的执行时间
int sum;
for(int i = 0;i < 5;i++){
sum += pro[i].cpu;
}
int j;//用于存放当前需要上CPU运行的进程的下标
while(allcpu <= sum){
j = Find(pro);
if(pro[j].zhu == 's'){//将第一次开始执行的精选进行标记
pro[j].start = allcpu;
pro[j].zhu = 'R';
}
//判断该进程执行完该时间片后便结束
if(pro[j].run > timecpu){
allcpu += timecpu;
pro[j].run -= timecpu;
pro[j].timecounts++;//时间片加一
}else{
allcpu += pro[j].run;
pro[j].run = 0;
pro[j].zhu = 'F';
pro[j].end = allcpu;
}
}
}
//计算并打印开始执行时间,周转时间,带权周转时间,平均周转时间,平均带权周转时间
void cap(PCB pro[]){
cout<<"进程号 始执行时间 周转时间 带权周转时间 "<<endl;
//计算并打印开始执行时间,周转时间,带权周转时间,
for(int i = 0;i < 5;i++){
cout<<pro[i].id<<" 8:";
cout<<pro[i].start<<" ";
cout<<pro[i].end-pro[i].arrive<<" ";
cout<<(double)(pro[i].end-pro[i].arrive)/(double)pro[i].cpu<<endl;
}
//计算平均周转时间
int count = 0;
for(int i = 0;i < 5;i++){
count +=pro[i].end-pro[i].arrive;
}
cout<<"平均周转时间:"<<count/5.0<<endl;
//计算平均带权周转时间
double count1 = 0;
for(int i = 0;i < 5;i++){
count1 +=(double)(pro[i].end-pro[i].arrive)/(double)pro[i].cpu;
}
cout<<"平均带权周转时间:"<<count1/5.0<<endl;
}
// 打印进程信息
void print(PCB pro[]){
for(int i = 0;i < 5;i++){
// cout<<"进程号: "<< pro[i].id<<" 进程到达时间: 8:"<<pro[i].arrive
// <<" 进程开始执行时间 "<<pro[i].start<<" 进程运行结束时间: "<<pro[i].end
// <<" 进程还需要的执行时间: "<<pro[i].run<<endl;
cout<<"进程号: "<< pro[i].id<<" 进程到达时间: 8:"<<pro[i].arrive
<<" 进程需要的执行时间: "<<pro[i].run<<endl;
}
cout<<endl;
}
//拷贝数组
void copy(PCB pro1[],PCB pro[]) {
for(int i = 0;i<5;i++){
pro1[i] = pro[i];
}
}
int main(){
PCB pro[5];
linkList L;
In(pro,L);
cout<<"初始时进程信息:"<<endl;
print(pro);
PCB pro1[5];
copy(pro1,pro);
cout<<"先来先服务算法:"<<endl;
FIFO(pro,L);
cap(pro);
cout<<"短进程优先算法:"<<endl;
SJF(pro1);
cap(pro1);
}
效果
当时间片为9时
时间片为15时
时间片为5时