1. 目的和要求
1.1. 实验目的
用高级语言完成一个进程调度程序,以加深对进程的概念及进程调度算法的理解。
1.2. 实验要求
1.2.1例题:设计一个有 N个进程并发执行的进程调度模拟程序。
进程调度算法:采用最高优先级优先的调度算法(即把处理机分配给优先级最高的进程)和先来先服务(若优先级相同)算法。
(1). 每个进程有一个进程控制块(PCB)表示。进程控制块包含如下信息:进程名、优先级、到达时间、需要运行时间、已用CPU时间、进程状态等等。
(2). 进程的优先级及需要的运行时间可以事先人为地指定,进程的运行时间以时间片为单位进行计算。
(3). 每个进程的状态可以是就绪 r(ready)、运行R(Running)、或完成F(Finished)三种状态之一。
(4). 就绪进程获得 CPU后都只能运行一个时间片。用已占用CPU时间加1来表示。
(5). 如果运行一个时间片后,进程的已占用 CPU时间已达到所需要的运行时间,则撤消该进程,如果运行一个时间片后进程的已占用CPU时间还未达所需要的运行时间,也就是进程还需要继续运行,此时应将进程的优先数减1(即降低一级),然后把它插入就绪队列等待调度。
(6). 每进行一次调度程序都打印一次运行进程、就绪队列中各个进程的 PCB,以便进行检查。
(7). 重复以上过程,直到所要进程都完成为止。
1.2.2实验题A:编写并调试一个模拟的进程调度程序,采用“最高优先数优先”调度算法对N(N不小于5)个进程进行调度。
“最高优先级优先”调度算法的基本思想是把CPU分配给就绪队列中优先数最高的进程。
(1). 静态优先数是在创建进程时确定的,并在整个进程运行期间不再改变。
(2). 动态优先数是指进程的优先数在创建进程时可以给定一个初始值,并且可以按一定规则修改优先数。例如:在进程获得一次CPU后就将其优先数减少1,并且进程等待的时间超过某一时限(2个时间片时间)时增加其优先数等。
(3). (**) 进程的优先数及需要的运行时间可以事先人为地指定,(也可以由随机数产生)。
(4). (**)在进行模拟调度过程可以创建(增加)进程,其到达时间为进程输入的时间。
1.2.3实验题B:编写并调试一个模拟的进程调度程序,采用“基于时间片轮转法”调度算法对N(N不小于5)个进程进行调度。 “轮转法”有简单轮转法、多级反馈队列调度算法。
(1). 简单轮转法的基本思想是:所有就绪进程按 FCFS排成一个队列,总是把处理机分配给队首的进程,各进程占用CPU的时间片长度相同。如果运行进程用完它的时间片后还未完成,就把它送回到就绪队列的末尾,把处理机重新分配给队首的进程。直至所有的进程运行完毕。(此调度算法是否有优先级?)
(2). 多级反馈队列调度算法的基本思想是:
将就绪队列分为N级(N=3~5),每个就绪队列优先数不同并且分配给不同的时间片:队列级别越高,优先数越低,时间片越长;级别越小,优先数越高,时间片越短。
系统从第一级调度,当第一级为空时,系统转向第二级队列,.....当处于运行态的进程用完一个时间片,若未完成则放弃CPU,进入下一级队列。
当进程第一次就绪时,进入第一级队列。
(3). (**)考虑进程的阻塞状态B(Blocked)增加阻塞队列。进程的是否阻塞和阻塞的时间由产生的“随机数”确定(阻塞的频率和时间长度要较为合理)。注意进程只有处于运行状态才可能转换成阻塞状态,进程只有处于就绪状态才可以转换成运行状态。
2. 实验内容
根据指定的实验课题:A(1),A(2),B(1)和B(2)
完成设计、编码和调试工作,完成实验报告。
注:带**号的条目表示选做内容。
3. 实验环境
可以选用Turbo C作为开发环境。也可以选用Windows下的VB,CB等可视化环境,利用各种控件较为方便。自主选择实验环境。
4. 实验原理及核心算法参考程序段
动态优先数(优先数只减不加):
5.参考代码如下:
#include <stdio.h> #include <stdlib.h> struct PCB{ char p_name[20]; int p_priority; int p_needTime;a int p_runTime; char p_state; struct PCB* next; }; void HighPriority(); void RoundRobin(); void Information(); char Choice(); struct PCB* SortList(PCB* HL); int main() { Information(); char choice = Choice(); switch(choice) { case '1': system("cls"); HighPriority(); break; case '2': system("cls"); RoundRobin(); break; default: break; } system("pause"); return 0; } void Information() { printf("\n\n"); printf(" ********************************************* \n"); printf(" 模拟进程调度算法\n"); printf(" ********************************************* \n\n\n"); printf(" 班 级: 网络工程班\n"); printf(" 姓 名: 张文雅\n"); printf(" 学 号: 201306114136\n"); printf(" 实验日期: 2015年05月17日\n\n\n\n\n\n"); printf(" 按回车键进入演示程序"); getchar(); system("cls"); } char Choice() { printf("\n\n"); printf(" ********************************************* \n"); printf(" 进程调度演示\n"); printf(" ********************************************* \n\n\n"); printf(" 1.演示最高优先数优先算法。\n"); printf(" 2.演示轮转法算法。\n"); printf(" 3.退出程序。\n\n\n\n"); printf(" 选择进程调度方法:"); char ch = getchar(); return ch; system("cls"); } void HighPriority() { struct PCB *processes, *pt; //pt作为临时节点来创建链表 processes = pt = (struct PCB*)malloc(sizeof(struct PCB)); for (int i = 0; i != 5; ++i) { struct PCB *p = (struct PCB*)malloc(sizeof(struct PCB)); printf("进程号No.%d:\n", i); printf("输入进程名:"); scanf("%s", p->p_name); printf("输入进程优先数:"); scanf("%d", &p->p_priority); printf("输入进程运行时间:"); scanf("%d", &p->p_needTime); p->p_runTime = 0; p->p_state = 'W'; p->next = NULL; pt->next = p; pt = p; printf("\n\n"); } getchar(); //接受回车 //processes作为头结点来存储链表 processes = processes->next; int cases = 0; struct PCB *psorted = processes; while (1) { ++cases; pt = processes; //对链表按照优先数排序 //psorted用来存放排序后的链表 psorted = SortList(psorted); printf("The execute number: %d\n\n", cases); printf("**** 当前正在运行的进程是:%s\n", psorted->p_name); psorted->p_state = 'R'; printf("qname state super ndtime runtime\n"); printf("%s\t%c\t%d\t%d\t%d\t\n\n", psorted->p_name, psorted->p_state, psorted->p_priority, psorted->p_needTime, psorted->p_runTime); pt->p_state = 'W'; psorted->p_runTime++; psorted->p_priority--; printf("**** 当前就绪状态的队列为:\n\n"); //pt指向已经排序的队列 pt = psorted->next; while (pt != NULL) { printf("qname state super ndtime runtime\n"); printf("%s\t%c\t%d\t%d\t%d\t\n\n", pt->p_name, pt->p_state, pt->p_priority, pt->p_needTime, pt->p_runTime); pt = pt->next; } //pt指向已经排序的链表,判断链表是否有已用时间啊等于需要时间的 pt = psorted; struct PCB *ap; ap = NULL; //ap指向pt的前一个节点 while (pt != NULL) { if (pt->p_needTime == pt->p_runTime) { if (ap == NULL) { pt = psorted->next; psorted = pt; } else ap->next = pt->next; } ap = pt; pt = pt->next; } if (psorted->next == NULL) break; getchar(); } } struct PCB* SortList(PCB* HL) { struct PCB* SL; SL = (struct PCB*)malloc(sizeof(struct PCB)); SL = NULL; struct PCB* r = HL; while (r != NULL) { struct PCB* t = r->next; struct PCB* cp = SL; struct PCB* ap = NULL; while (cp != NULL) { if (r->p_priority > cp->p_priority) break; else { ap = cp; cp = cp->next; } } if (ap == NULL) { r->next = SL; SL = r; } else { r->next = cp; ap->next = r; } r = t; } return SL; } //轮转算法 void RoundRobin() { struct PCB *processes, *pt; //pt作为临时节点来创建链表 processes = pt = (struct PCB*)malloc(sizeof(struct PCB)); for (int i = 0; i != 5; ++i) { struct PCB *p = (struct PCB*)malloc(sizeof(struct PCB)); printf("进程号No.%d:\n", i); printf("输入进程名:"); scanf("%s", p->p_name); printf("输入进程运行时间:"); scanf("%d", &p->p_needTime); p->p_runTime = 0; p->p_state = 'W'; p->next = NULL; pt->next = p; pt = p; printf("\n\n"); } getchar(); //接受回车 //processes作为头结点来存储链表 processes = processes->next; int cases = 0; while (1) { ++cases; pt = processes; printf("The execute number: %d\n\n", cases); printf("**** 当前正在运行的进程是:%s\n", pt->p_name); pt->p_state = 'R'; printf("qname state super ndtime runtime\n"); printf("%s\t%c\t%d\t%d\t%d\t\n\n", pt->p_name, pt->p_state, pt->p_priority, pt->p_needTime, pt->p_runTime); pt->p_state = 'W'; pt->p_runTime++; pt->p_priority--; printf("**** 当前就绪状态的队列为:\n\n"); pt = pt->next; while (pt != NULL) { printf("qname state super ndtime runtime\n"); printf("%s\t%c\t%d\t%d\t%d\t\n\n", pt->p_name, pt->p_state, pt->p_priority, pt->p_needTime, pt->p_runTime); pt = pt->next; } //检测是否运行时间等于需要时间,是的话从队列里面删除,不是的话加到队列最尾部 pt = processes; if (pt->p_needTime == pt->p_runTime) { pt->p_state = 'C'; pt = processes->next; processes = pt; } else { if (pt ->next != NULL) { //寻找最后一个节点 while (pt->next != NULL) pt = pt->next; struct PCB* ptem;//临时节点用来帮助把头结点插到尾部 ptem = processes->next; pt->next = processes; processes->next = NULL; processes = ptem; } } pt = processes; if (pt == NULL) break; getchar(); } }
运行结果:x
实验总结:编代码需要细心以及耐心,一开始错误连连,通过参考资料,不断修整代码,不厌其烦地纠正一个个错误,直至运行成功与运算正确,这个过程虽略有辛苦,但也充满了欢乐,正是所谓的乐在苦中吧。