实验过程
- 用高级语言设计PCB结构
1、 用高级语言设计PCB结构
#define WAIT "Wait"//就绪状态
#define RUN "Run"//运行状态
#define FINISH "Finish"//完成状态
#define JOBNUMBER 5 //设置进程测试数为5
typedef struct JCB {
char jobName[10];//作业名
int arriveTime;//到达时间
int runTime;//需要运行时间
int startTime;//开始时间
int endTime;//完成时间
int turnoverTime;//周转时间
float useWeightTurnoverTime;//带权周转时间
char processStatus[10];//进程状态
};
static int currentTime = 0;//当前时间
static int finishNumber = 0;//进程完成数量
char JobArray[JOBNUMBER][10];//存放数组名信息的二元数组
float priority[JOBNUMBER];//存放进程优先级的一元数组
//创建JCB
void createJCB(struct JCB* jcb) {
freopen("input.txt","r",stdin);
printf("从文件中读入三个参数的数据:\n");
printf("作业号 到达时间 需要运行时间\n");
for(int i = 0; i < 5; i++) {
scanf("%s", &jcb[i].jobName);//作业号
scanf("%d", &jcb[i].arriveTime);//到达时间
scanf("%d", &jcb[i].runTime);//需要运行时间
jcb[i].startTime = 0;
jcb[i].endTime = 0;
jcb[i].turnoverTime = 0;
jcb[i].useWeightTurnoverTime = 0.0;
strcpy(jcb[i].processStatus, WAIT);
printf("%s\t%d\t%d\n",jcb[i].jobName, jcb[i].arriveTime,jcb[i].runTime);
}
printf("---------------------------------------------\n");
freopen("CON", "r", stdin);
}
2、 建立进程就绪队列
//打印就绪队列
void printJob(struct JCB* jcb) {
printf("当前时间为%d\n", currentTime);
printf("作业号 到达时间 需要运行时间 开始时间 完成时间 周转时间 带权周转时间 进程状态\n");
for(int i = 0; i < JOBNUMBER; i++) {
if(strcmp(jcb[i].processStatus, FINISH) == 0)//如果进程为finish状态,这样输出
printf("%s\t%d\t%4d\t\t%d\t%d\t %d\t %.2f\t %s\n", jcb[i].jobName, jcb[i].arriveTime, jcb[i].runTime, jcb[i].startTime, jcb[i].endTime, jcb[i].turnoverTime, jcb[i].useWeightTurnoverTime, jcb[i].processStatus);
else if(strcmp(jcb[i].processStatus, RUN) == 0)//如果进程为run状态,这样输出
printf("%s\t%d\t%4d\t\t%d\t运行中\t none\t none %s\n", jcb[i].jobName, jcb[i].arriveTime, jcb[i].runTime, jcb[i].startTime, jcb[i].processStatus);
else //如果进程为wait状态,这样输出
printf("%s\t%d\t%4d\t\t未运行\tnone\t none\t none %s\n", jcb[i].jobName, jcb[i].arriveTime, jcb[i].runTime, jcb[i].processStatus);
}
printf("---------------------------------------------\n");
}
//计算平均带权周转时间
float weightTurnoverTimeCount(struct JCB* jcb) {
float sum = 0.0;
for(int i = 0; i < JOBNUMBER; i++)
sum += jcb[i].useWeightTurnoverTime;
return sum / JOBNUMBER;
}
//计算平均周转时间
float turnOverTimeCount(struct JCB* jcb) {
float sum = 0.0;
for(int i = 0; i < JOBNUMBER; i++)
sum += jcb[i].turnoverTime;
return sum / JOBNUMBER;
}
//比较各个进程之间的到达时间,按升序排列
void compare(struct JCB* jcb) {
for(int i = 0; i < JOBNUMBER; i++) {
int min = jcb[i].arriveTime, minIndex = i;
for(int j = i + 1; j < JOBNUMBER; j++) {
if(jcb[j].arriveTime < min) {
min = jcb[j].arriveTime;
minIndex = j;
}
}
struct JCB temp = jcb[i];
jcb[i] = jcb[minIndex];
jcb[minIndex] = temp;
}
}
3、编制先来先服务调度算法进程调度算法
//先来先服务调度算法
void firstComeFirstServed(struct JCB* jcb){
createJCB(jcb);
compare(jcb);
int i = 0;
//进程调度currentTime每次加1,直到进程全部被调度完成为止
for(; finishNumber != JOBNUMBER; currentTime++){
if(currentTime < jcb[0].arriveTime)//当前时间小于第一个节点到来时间时,直接打印
printJob(jcb);
else{
strcpy(JobArray[i], jcb[i].jobName);
loop(jcb, i);
i++;
}
}
printInfo(jcb);//打印进程调度顺序,平均周转时间及平均带权周转时间
currentTime = 0;//静态变量当前时间置位
finishNumber = 0;//静态变量完成进程数量置位
}
//循环遍历部分
void loop(struct JCB* jcb, int i) {
jcb[i].startTime = currentTime;
jcb[i].endTime = jcb[i].startTime + jcb[i].runTime;
jcb[i].turnoverTime = jcb[i].endTime - jcb[i].arriveTime;
jcb[i].useWeightTurnoverTime = jcb[i].turnoverTime * 1.0 / jcb[i].runTime;
strcpy(jcb[i].processStatus, RUN);
while(true) {
if(currentTime == jcb[i].endTime) {
strcpy(jcb[i].processStatus, FINISH);
finishNumber++;
if(finishNumber == JOBNUMBER)
printJob(jcb);
currentTime--;
break;
} else {
printJob(jcb);
currentTime++;
}
}
}
//打印进程调度顺序,平均周转时间及平均带权周转时间
void printInfo(struct JCB* jcb) {
printf("1、进程调度顺序为:%s -> %s -> %s -> %s -> %s\n", JobArray[0], JobArray[1], JobArray[2], JobArray[3], JobArray[4]);
printf("2、平均周转时间为:%.2f\n",turnOverTimeCount(jcb));
printf("3、平均带权周转时间为:%.2f\n", weightTurnoverTimeCount(jcb));
printf("------------------测试完毕 ---------\n");
}
//主函数
int main() {
struct JCB jcb[JOBNUMBER];
printf("******** 先来先服务调度算法 ********* \n");
firstComeFirstServed(jcb);
system("pause");
return 0;
}
五、测试/调试及实验结果分析 1、当前时间为0时,模拟系统从外存的后备队列中选取五项作业(A,B,C,D,E)调入内存,并为它们创建进程,分配必要的资源。然后再将新创建的进程排在就绪队列上等待调度,其进程状态为wait状态,如图1所示。 ![](https://i-blog.csdnimg.cn/blog_migrate/88cc45ef0b234ff20ad2ed3bc78bc938.png)
2、当前时间为1时,C和E到达,但C比E先到达,所以根据先来先服务算法,C先开始执行,进程状态由Wait转换为Run,如图2所示。 ![](https://i-blog.csdnimg.cn/blog_migrate/71c81a1579a9032102e0345beb8c5abd.png)
3、当前时间为3时,C执行完成,进程状态由Run转换为Finish,与此同时,E开始执行,进程状态由Wait转换为Run,如图3所示。 ![](https://i-blog.csdnimg.cn/blog_migrate/9a2737a942a1a6032561577d6d9145a4.png)
4、当前时间为5时,E执行完成,进程状态由Run转换为Finish,与此同时,根据到达时间,D开始执行,进程状态由Wait转换为Run,如图4所示。 ![](https://i-blog.csdnimg.cn/blog_migrate/30fd78e967c7a45aeaec6a753ec401c2.png)
5、当前时间为6时,D执行完成,进程状态由Run转换为Finish,与此同时,A开始执行,进程状态由Wait转换为Run,如图5所示。 ![](https://i-blog.csdnimg.cn/blog_migrate/1ce3ff665f80c709b1245df1f8e538d0.png)
6、当前时间为9时,A执行完成,进程状态由Run转换为Finish,与此同时,B开始执行,进程状态由Wait转换为Run,如图6所示。 ![](https://i-blog.csdnimg.cn/blog_migrate/a00069c9d3ee1e9e40033698edf66882.png)
7、当前时间为12时,B执行完成,进程状态由Run转换为Finish,如图7所示。 ![](https://i-blog.csdnimg.cn/blog_migrate/e58c8efd44187d44432161bf85af417c.png)
8、所以用先来先服务算法对所示的作业进行进程调度, 进程调度顺序为:C -> E -> D -> A -> B,如图8所示。 ![](https://i-blog.csdnimg.cn/blog_migrate/7d7aaab9e1c11ca46cf77370c6aecdd5.png)
六、实验结论与体会 1、FCFS的特点是什么?它有什么缺点? 特点: 先来先服务。 优点: 公平、算法实现简单。 缺点: 排在长作业(进程)后面的短作业需要等待很长时间,带权周转时间很大,对短作业来说用户体验不好。即,FCFS算法对长作业有利,对短作业不利。
- 周转时间 = 作业完成时间 - 作业提交时间
3、平均周转时间 = 各作业周转时间之和 / 作业数
- 带权周转时间 = 作业周转时间 / 作业实际运行的时间 =(作业完成时间-作业提交时间)/ 作业实际运行的时间
4、先来先服务算法,一个进程到达后要么在等待,要么在运行。 | |