一、实验目的
用高级语言编写和调试一个进程调度程序,以加深对进程的概念及进程调度算法的理解
二、实验类别
综合性实验。综合高级语言编程、进程调度模型、进程调度算法及数据结构等多方面的知识
三、实验示例
例题: 设计一个有 N个进程共行的进程调度程序
进程调度算法:采用最高优先数优先的调度算法(即把处理机分配给优先数最高的进程)和先来先服务算法。
每个进程有一个进程控制块( PCB)表示。进程控制块可以包含如下信息:进程名、优先数、到达时间、需要运行时间、已用CPU时间、进程状态等等。
进程的优先数及需要的运行时间可以事先人为地指定(也可以由随机数产生)。进程的到达时间为进程输
入的时间。
进程的运行时间以时间片为单位进行计算。
每个进程的状态可以是就绪 W(Wait)、运行R(Run)、或完成F(Finish)三种状态之一。
就绪进程获得 CPU后都只能运行一个时间片。用已占用CPU时间加1来表示。
如果运行一个时间片后,进程的已占用 CPU时间已达到所需要的运行时间,则撤消该进程,如果运行一个时间片后进程的已占用CPU时间还未达所需要的运行时间,也就是进程还需要继续运行,此时应将进程的优先数减1(即降低一级),然后把它插入就绪队列等待CPU。
每进行一次调度程序都打印一次运行进程、就绪队列、以及各个进程的 PCB,以便进行检查。
重复以上过程,直到所要进程都完成为止。
调度算法的流程图如下 :
四、源代码
#include "stdio.h" #include#include #define getpch(type) (type*)malloc(sizeof(type)) //分配type类型大小的空间 #define NULL 0 struct pcb { /* 定义进程控制块PCB */ char name[10]; //进程名 char state; //进程状态 int super; //进程优先级 int ntime; //进程总共需要运行的时间 int rtime; //进程已经运行的时间 struct pcb* link; //指向后继节点的指针 }*ready=NULL,*p; //ready指针永远指向队列的第一个进程 typedef struct pcb PCB; sort() /* 建立对进程进行优先级排列函数*/ { PCB *first, *second; // 指针second指向队列中当前处理的进程,指针first指向second的前驱节点,指针p指向准备插入队列的进程; int insert=0; //判断是否 if((ready==NULL)||((p->super)>(ready->super))) /*优先级最大者,插入队首*/ { p->link=ready; //p指向的进程优先级最高,要成为队列中新的队首节点,即ready要修改指向p; ready=p; } else /* 如果队列有至少一个进程,则进程比较优先级,插入适当的位置中*/ { first=ready; // first指向队首节点; second=first->link; // second指向第2个节点; while(second!=NULL) //如果有第2个节点,即队列至少有2个节点; { if((p->super)>(second->super)) /*若插入进程比当前进程优先数大,*/ { /*插入到当前进程前面*/ //p指向的进程要插入到first和sencond之间,修改指针; p->link=second; first->link=p; second=NULL; //找到了进程插入的位置,把second设为NULL,退出while循环; insert=1; //insert为1表示p指向的进程插入在两个进程之间; } else /* 插入进程优先数最低,则插入到队尾*/ { first=first->link; // 此时first指向原队列最后一个进程; second=second->link; //此时second为NULL; } } if(insert==0) first->link=p; // insert为0表示p指向的进程插入到队尾 } } input() /* 建立进程控制块函数*/ { int i,num; //clrscr(); /*清屏*/ printf("\n 请输入进程号?"); //输入需要调度的进程数量 scanf("%d",&num); for(i=0;i name); printf("\n 输入进程优先数:"); scanf("%d",&p->super); printf("\n 输入进程运行时间:"); scanf("%d",&p->ntime); printf("\n"); p->rtime=0;p->state='w'; p->link=NULL; sort(); /* 调用sort函数*/ //把新创建的进程按优先级插入到队列中; } } int space() //获取当前队列的长度,即需要调度的进程个数; { int l=0; PCB* pr=ready; //pr指向队列第一个进程,即对首进程 while(pr!=NULL) //依次遍历整个队列,计算长度 { l++; //是字符l,不是数字1,代表队列长度; pr=pr->link; } return(l); } disp(PCB * pr) /*建立进程显示函数,用于显示当前进程的信息*/ { printf("\n qname \t state \t super \t ndtime \t runtime \n"); printf("|%s\t",pr->name); printf("|%c\t",pr->state); printf("|%d\t",pr->super); printf("|%d\t",pr->ntime); printf("|%d\t",pr->rtime); printf("\n"); } check() /* 建立进程查看函数 */ //查看当前队列中每个进程信息 { PCB* pr; printf("\n **** 当前正在运行的进程是:%s",p->name); /*显示当前运行进程*/ disp(p); pr=ready; // pr指向队列对首进程 printf("\n ****当前就绪队列状态为:\n"); /*显示就绪队列状态*/ while(pr!=NULL) //循环遍历队列中每个进程 { disp(pr); pr=pr->link; } } destroy() /*建立进程撤消函数(进程运行结束,撤消进程)*/ { printf("\n 进程 [%s] 已完成.\n",p->name); free(p); } running() /* 建立进程就绪函数(进程运行时间到,置就绪状态*/ { (p->rtime)++; //p指向当前运行进程,即从原队列中的对首进程,运行次数加1,rtime表示已运行的时间片; if(p->rtime==p->ntime) //如果进程需要运行总时间ntime和已运行时间rtime相等,表示该进程已经运行完毕,需要撤销; destroy(); /* 调用destroy函数*/ else //如果该进程没有运行完毕,则使它的优先级减1,状态变为就绪,重新按优先级顺序插入到队列中; { (p->super)--; p->state='w'; sort(); /*调用sort函数*/ } } main() /*主函数*/ { int len,h=0; //len:队列进程的个数;h:进程调度的时间片次数 char ch; // 获取getchar()函数的字符 input(); //输入要处理进程控制块信息 len=space(); //当前进程队列的长度,即要处理的进程数量 while((len!=0)&&(ready!=NULL)) //若队列不为空,即还有进程需要处理; { ch=getchar(); //从键盘获取字符 h++; // h:进程执行的时间片次数序号 printf("\n The execute number:%d \n",h); // p=ready; // ready永远指向队列的对首进程,p此时也指向队列的对首进程,但该对首进程需要出队列以便运行,; ready=p->link; //原队列的第2个进程成为新的对首进程 p->link=NULL; //原对首进程出队列后,没有后继; p->state='R'; //原对首进程出队列后状态变为执行; check(); //查看当前队列中所有进程的信息,即原对首进程出队列后 running();//原对首进程运行 printf("\n 按任一键继续......"); ch=getchar();//从键盘获取字符 } printf("\n\n 进程已经完成.\n"); ch=getchar();//从键盘获取字符 }
五、运行截图
六、报告总结
通过本次实验,进程动态调度的过程与人工分析的结果一致,基本达到预期的目标。通过用户自己定义进程号、进程名、进程优先数、进程运行时间。优先数高的进程先进入运行状态,其他进程则为就绪状态。若优先数高的进程运行一个时间片后仍需运行,则进入就绪状态,该进程优先数-1。若进程运行一个时间片后,已经达成所需要的运行时间,则该进程完成,进入完成状态。如果就绪队列里存有多个优先数相同的进程,则根据进程号先来先得的原则运行。