实验简介:
本程序是实现操作系统中进程调度的模拟实验,使用的调度方法有两种,为:先来先服务方法(First Come First Served,FCFS)和短作业优先方法(Short Job First,SJF)。
本实验所用语言为C语言,使用的编译器为Code::Blocks 20.03。
实验目的:
1.学习进程的相关概念,理解进程各状态及状态之间的转换过程。
2.了解进程调度任务执行的机制与方式。
3.掌握多种进程调度算法的实现,并对比分析不同算法的性能差异与优缺点。
具体实验内容:
首先将本程序的关键对象——进程定义为一个结构体变量,包含进程编号、进程名、到达时间、服务时间、开始服务时间、完成服务时间、等待时间、周转时间和带权周转时间。其次,需要定义一些模拟系统的基础变量方便进行计算。代码如下:
typedef struct{
int num; //进程编号
char name[10]; //进程名
float arrive_time; //到达时间
float serve_time; //服务时间
float start_time; //开始时间
float finish_time; //完成时间
float wait_time; //等待时间
float turnover; //周转时间
float wei_turnover; //带权周转时间
}Process;
float wei_sum,wei_avg; //带权周转时间之和与平均值
int p_amt; //进程数
int k=0,k2=0;
float sys_time=0; //系统时间
用户输入若干进程的信息(包括进程名,进程到达时间和进程服务时间)组成进程队列,通过结构体数组存储。执行进程调度算法时,先将用户输入的进程队列复制至待运行进程队列,然后选择进程调度算法进行排序。程序的主函数代码如下:
void main(){
void FCFS(Process*);
void SJF(Process*);
int i;
printf("单道处理系统的进程调度(非抢占式)模拟程序\n\n");
printf("请输入进程数:");
scanf("%d",&p_amt);
Process process_list[p_amt]; //为进程列表数组赋值
for(i=0;i<p_amt;i++){
process_list[i].num=i+1;
printf("请输入第%d个进程的信息\n",i+1);
printf("进程名称:",i+1);
scanf("%s",&process_list[i].name);
printf("进程的到达时间:",i+1);
scanf("%f",&process_list[i].arrive_time);
printf("进程的服务时间:",i+1);
scanf("%f",&process_list[i].serve_time);
}
getchar();
printf("进程队列及其信息如下:\n");
printf("进程名称 到达时间 服务时间 \n");
for(i=0;i<p_amt;i++){
printf("%-10s%-10.1f%-10.1f\n",process_list[i].name,process_list[i].arrive_time,process_list[i].serve_time);
}
do{
printf("\n请选择进程调度模式:\n");
printf("1.先到先服务 2.短作业优先\n");
switch(getchar()){
case'1':
getchar();
FCFS(process_list);
break;
case'2':
getchar();
SJF(process_list);
break;
default:
printf("选择错误,请重新选择");
break;
}
}while(1);
return;
}
两种算法大体思路是一致的:通过相关算法对待运行进程队列进行排序,然后计算每个进程的其他信息,最后将待运行进程队列的每条进程依次输出在屏幕上。所不同的一步是计算待运行进程队列的方法。下面我会对结合代码两种方法的具体计算进行解释。
FCFS方法:在待运行进程队列中,以每一条进程的到达时间为排序标准,对每条进程进行排序,到达时间早的在前。这里我使用的是简单的冒泡排序法。FCFS方法代码如下:
void FCFS(Process* process_list){ //先来先服务进程调度方法
Process process_copy(Process,Process);
void process_swap(Process[],int,int);
int i,j;
float t;
sys_time=0;
//将目标进程列表写入待运行进程队列
Process process_queue[p_amt]; //待运行进程队列
for(i=0;i<p_amt;i++)
process_queue[i]=process_copy(process_queue[i],process_list[i]);
//对进程进行排序
for(i=0;i<p_amt-1;i++)
for(j=0;j<p_amt-1-i;j++)
if(process_queue[j].arrive_time>process_queue[j+1].arrive_time)
process_swap(process_queue,j,j+1);
//计算每个进程的相关信息(模拟进程运行)
for(i=0;i<p_amt;i++){
t=process_queue[i].arrive_time-sys_time;
if(process_queue[i].arrive_time>sys_time)
sys_time+=t;
process_queue[i].start_time=sys_time;
sys_time=process_queue[i].finish_time=sys_time+process_queue[i].serve_time;
process_queue[i].wait_time=process_queue[i].start_time-process_queue[i].arrive_time;
process_queue[i].turnover=process_queue[i].finish_time-process_queue[i].arrive_time;
process_queue[i].wei_turnover=process_queue[i].turnover/process_queue[i].serve_time;
}
//输出进程调度结果
printf("\n1.选择“先到先服务”方法\n");
printf("进程队列及其信息如下:\n");
printf("序号 进程名称 到达时间 开始时间 服务时间 结束时间 等待时间 周转时间 带权周转时间 \n");
wei_sum=0;
wei_avg=0;
for(i=0;i<p_amt;i++){
printf("%-5d%-10s%-10.2f%-10.2f%-10.2f%-10.2f%-10.2f%-10.2f%-15.2f\n",i+1,process_queue[i].name,process_queue[i].arrive_time,process_queue[i].start_time,process_queue[i].serve_time,process_queue[i].finish_time,process_queue[i].wait_time,process_queue[i].turnover,process_queue[i].wei_turnover);
wei_sum+=process_queue[i].wei_turnover;
}
wei_avg=wei_sum/p_amt;
printf("\n平均带权周转时间:%.2f\n",wei_avg);
return;
}
SJF方法:设两条进程队列,一条为主函数中提到的待运行进程队列,使用系统时间变量;一条为计算待运行进程队列,使用模拟系统时间变量。计算待运行进程队列初始与待运行进程队列相同,设置两个队列的目的是方便算法运行过程中对队列进行修改而不会影响到最后的输出结果。对进程队列进行排序分四个步骤,分别为1.选出最早到达的进程;2.从其中选出到达时间小于等于系统时间的进程,若没有则令模拟系统时间增加并重复该步骤;3.选出服务时间最短的一个进程并按序将其写入待运行进程队列,完成后更新模拟系统时间;4.重复步骤1、2、3直到所有进程都被写入待运行进程队列。SJF方法代码如下:
void SJF(Process* process_list){ //短作业优先进程调度方法
Process process_copy(Process,Process);
void process_swap(Process[],int,int);
int num,i,j,fg,n=p_amt;
float t,sys_time2; //t变量为进程队列第一个进程(最早到达系统的进程之一)的开始时间减去系统当前时间;sys_time2为模拟系统时间,用于计算进程队列
sys_time2=0;
sys_time=0; //重置系统时间
Process process_queue[p_amt]; //待运行进程队列(用于计算)
Process process_queue2[p_amt]; //最终进程队列
//将目标进程列表写入待运行进程队列
for(i=0;i<p_amt;i++)
process_queue[i]=process_copy(process_queue[i],process_list[i]);
//对进程进行排序
for(num=0;num<p_amt;num++){
for(i=0;i<n-1;i++)
for(j=0;j<n-1-i;j++)
if(process_queue[j].arrive_time>process_queue[j+1].arrive_time)
process_swap(process_queue,j,j+1);
fg=0;
do{
t=process_queue[0].arrive_time-sys_time2;
for(i=0;i<n;i++)
if(process_queue[i].arrive_time<=sys_time2)
fg++;
if(fg==0)
sys_time2+=t;
}while(fg==0);
//printf("num:%d fg:%d sys_time2:%.2f",num,fg,sys_time2);
for(i=0;i<fg;i++)
for(j=0;j<fg-1-i;j++)
if(process_queue[j].serve_time>process_queue[j+1].serve_time)
process_swap(process_queue,j,j+1);
//目前的队列中第一个进程为到达时间最早(或之一)且服务时间最短(或之一)的进程
sys_time2+=process_queue[0].serve_time;
process_queue2[num]=process_copy(process_queue2[num],process_queue[0]);
for(i=0;i<n;i++)
process_queue[i]=process_copy(process_queue[i],process_queue[i+1]);
n--; //删除进程队列中第一个队员,其余组成新的队列
}
//计算每个进程的相关信息(模拟进程运行)
for(i=0;i<p_amt;i++){
t=process_queue2[i].arrive_time-sys_time;
if(process_queue2[i].arrive_time>sys_time)
sys_time+=t;
process_queue2[i].start_time=sys_time;
sys_time=process_queue2[i].finish_time=sys_time+process_queue2[i].serve_time;
process_queue2[i].wait_time=process_queue2[i].start_time-process_queue2[i].arrive_time;
process_queue2[i].turnover=process_queue2[i].finish_time-process_queue2[i].arrive_time;
process_queue2[i].wei_turnover=process_queue2[i].turnover/process_queue2[i].serve_time;
}
//输出进程调度结果
printf("\n2.选择“短作业优先”方法\n");
printf("进程队列及其信息如下:\n");
printf("序号 进程名称 到达时间 开始时间 服务时间 结束时间 等待时间 周转时间 带权周转时间 \n");
wei_sum=0;
wei_avg=0;
for(i=0;i<p_amt;i++){
printf("%-5d%-10s%-10.2f%-10.2f%-10.2f%-10.2f%-10.2f%-10.2f%-15.2f\n",i+1,process_queue2[i].name,process_queue2[i].arrive_time,process_queue2[i].start_time,
process_queue2[i].serve_time,process_queue2[i].finish_time,process_queue2[i].wait_time,process_queue2[i].turnover,process_queue2[i].wei_turnover);
wei_sum+=process_queue2[i].wei_turnover;
}
wei_avg=wei_sum/p_amt;
printf("\n平均带权周转时间:%.2f\n",wei_avg);
return;
}
此外,在两种进程调度算法中都使用到了两个方法,一个是进程交换函数,用于将进程队列中的两条进程位置互换;一个是进程复制函数,用于将某条进程的全部信息复制到另一条进程中去。两个方法的代码如下:
void process_swap(Process p[],int n1,int n2){ //交换两个进程的运行顺序,p为进程队列
Process p1; //p1做中间变量
p1.num=p[n1].num;
strcpy(p1.name,p[n1].name);
p1.arrive_time=p[n1].arrive_time;
p1.serve_time=p[n1].serve_time;
p[n1].num=p[n2].num;
strcpy(p[n1].name,p[n2].name);
p[n1].arrive_time=p[n2].arrive_time;
p[n1].serve_time=p[n2].serve_time;
p[n2].num=p1.num;
strcpy(p[n2].name,p1.name);
p[n2].arrive_time=p1.arrive_time;
p[n2].serve_time=p1.serve_time;
}
Process process_copy(Process p1,Process p2){ //复制进程相关信息并返回该进程
p1.num=p2.num;
strcpy(p1.name,p2.name);
p1.arrive_time=p2.arrive_time;
p1.serve_time=p2.serve_time;
p1.start_time=p2.start_time;
p1.finish_time=p2.finish_time;
p1.wait_time=p2.wait_time;
p1.turnover=p2.turnover;
p1.wei_turnover=p2.wei_turnover;
return p1;
}
运行示例:
小结:
以上便是关于本实验的全部内容。由于笔者水平有限,文章难免出现相关知识点出现错误,如果您对相关内容有疑问,欢迎在评论区留言或者私信我。最后,感谢您的阅读,如果本文对您有帮助,希望能留下个小小的赞。