一、实验目的
- 对进程调度的工作做进一步的理解。
- 了解进程调度的任务。
- 通过编程掌握基于优先数的时间片轮转调度算法具体实现过程。
二、实验内容
1、设计一个程序模拟实现基于优先数的时间片轮转调度算法调度处理。
2、每个进程用一个进程控制块PCB来代表,建议进程控制块的结构如下所示:
进程名
要求运行时间
优先数
已运行时间
指针
- 进程名:作为进程的标识。
- 到达时间:进程进入系统时间。
- 要求运行时间:假设进程需要运行的单位时间数。
- 已运行时间:假设进程已经运行的单位时间数,初值为0。
- 状态:可假设有三种状态:未进入状态、就绪状态和结束状态。进程的初始状态都为未进入状态,到达时间-当前时间数时=0进程从未进入状态进入就绪状态;当要求运行时间-已运行时间=0时进程进入结束状态。
- 优先数:范围0-100,优先数越小,级别越高。
- 周转时间:进程从到达开始到结束运行所经历时间数。
- 指针:进程按顺序排成链表(不同队列进程排列顺序不一样),用指针指出下一个进程的进程控制块首地址,最后一个进程中的指针指出第一个进程的进程控制块首地址。
3、程序开始运行时输入多个进程信息,用于实现对进程控制块的初始化。测试输入数据表1所示:
表1 输入数据表
进程名
A
B
C
D
E
F
到达时间
0
0
1
1
3
5
要求运行时间
4
5
5
4
4
4
优先数
3
2
0
10
5
2
4、程序在没有开始运行前,全部进程所处的状态都处于未进入状态。
5、程序当中应该组织三个队列:未进入队列、就绪队列和结束队列。
6、就绪队列中的就绪进程按优先数由小到大排列,优先级最高的进程,获得被调度的资格。当某一进程运行完一个时间片后,其优先级应下调(如优先数加3)。
7、启动自动调度,系统时间单位开始自动计时,系统开始自动调度执行。程序显示每个时间单位的各个队列的 PCB信息。
8、最后输出每个进程的周转时间。
三、再学轮转调度算法
四、编程
运行环境:Visual Studio 2019
编程语言:c、c++
逻辑图:
代码:
#include <iostream>
using namespace std;
typedef int DataType;
/*
全局变量
*/
int process_count;//输入进程数
int syProcess = process_count;//剩余进程数量
int sjp_Time;//时间片大小
int os_time=0;//系统时间
/*
定义PCB的结构体
*/
struct node //结构标记
{
char name; //进程名
DataType arrive_time; //进程进入系统的到达时间
DataType run_time; //进程要求运行的时间
DataType yjyx_time; //进程已经运行的时间
DataType state; //进程状态3种,0表示未进入状态,1表示就绪状态,2表示完成状态
DataType Priority; //优先数
DataType zz_time; //进程周转时间
DataType wc_time; //进程完成结束时间
struct node* next; //意味着next能存储一个指向该结构体的指针(要求使用结构标记才能用这个,不能用typedef)
}process[10]; //结构体数组,用于后面第一次给就绪队列排序辅助用的
/*
定义三个指向结构体的指针,分别用于三个队列的首节点,便于操作
*/
struct node* noEnter = (struct node*)malloc(sizeof(struct node)); //这是相当于声明了一个指向结构体的指针变量—未进入队列的头结点
struct node* runList = (struct node*)malloc(sizeof(struct node)); //就绪队列
struct node* wcList = (struct node*)malloc(sizeof(struct node)); //完成队列
typedef struct node *PCB;//给struct node取了一个简单名字,减少struct node*的写法
/*
初始化进程的PCB信息,存入未进入队列noEnter
*/
void Initia(PCB head) {
struct node* q;
q = head;
/*
循环创建新节点空间并赋值,且存入结构体数组process[]
*/
for (int i = 0; i < process_count; i++) {
PCB p = (PCB)malloc(sizeof(struct node));
cout << "第" << i + 1 << "个进程PCB信息:";
cin >> p->name >> p->arrive_time >> p->run_time >> p->Priority;
p->state = 0; //状态初始化为0未开始运行
p->yjyx_time = 0;
process[i].name = p->name;
process[i].arrive_time = p->arrive_time;
process[i].run_time = p->run_time;
process[i].Priority = p->Priority;
process[i].state = p->state;
q->next = p;
q = p;
}
q->next = head->next; //末尾PCB指向第一个节点PCB,实现单向循环队列
}
/*
打印未进入、就绪队列中PCB的信息
*/
void Show(PCB head) {
PCB p = head->next;
PCB q = p->next;
//cout << " *********进程信息*********" << endl;
cout << " 进程名 到达时间 运行时间 已运行时间 优先数" << endl;
//for(int i=0;i<process_count;i++)
//{
// cout << "\t" << p->name << "\t";
// cout << p->arrive_time << "\t";
// cout << p->run_time << "\t";
// cout << p->Priority << endl;
// p = p->next;//最后一个PCB输出后,p将指向第一个PCB
//}
do{
cout << "\t" << p->name << "\t";
cout << p->arrive_time << "\t";
cout << p->run_time << "\t ";
cout << p->yjyx_time << "\t ";
cout << p->Priority << endl;
p = p->next;//最后一个PCB输出后,p将指向第一个PCB
} while (p != head->next);
cout << endl;
}
/*
打印完成队列中PCB的信息
*/
void ShowWcList(PCB wclist) {
PCB p = wclist->next;
cout << " *********完成进程信息*********" << endl;
cout << " 进程名 到达时间 运行时间 优先数 完成时间 周转时间" << endl;
//for (int i = 0; i < process_count; i++)
//{
// cout << "\t" << p->name << "\t";
// cout << p->arrive_time << "\t";
// cout << p->run_time << "\t";
// cout << p->Priority << "\t";
// cout << p->zz_time << endl;
// p = p->next;//最后一个PCB输出后,p将指向第一个PCB
//}
do{
cout << "\t" << p->name << "\t";
cout << p->arrive_time << "\t";
cout << p->run_time << "\t";
cout << p->Priority << "\t";
cout << p->wc_time << "\t";
cout << p->zz_time << endl;
p = p->next;//最后一个PCB输出后,p将指向第一个PCB
} while (p!=wclist->next);
cout << endl;
}
/*
给就绪队列第一个排序时用的排序算法,
因为要创建新节点,所以借用结构体数组process来赋值
*/
void sortRunList(PCB runList) {
//按照优先数大小排序-直接对noEnter队列进行比较,最小的优先数放第一个节点
//重新排序process数组
int i, j, n = process_count;
for (i = 0; i < n - 1; i++)
{
for (j = 0; j < n - 1 - i; j++)
{
if (process[j].Priority > process[j + 1].Priority)
{
process[n] = process[j + 1];
process[j + 1] = process[j];
process[j] = process[n];
}
}
}
//赋值给runList队列
PCB q = runList;
for (int i = 0; i < process_count; i++) {
PCB p = (PCB)malloc(sizeof(struct node));
p->name = process[i].name;
p->arrive_time = process[i].arrive_time;
p->run_time = process[i].run_time;
p->Priority = process[i].Priority;
p->state = 1; //状态为1,就绪状态
p->yjyx_time = 0; //已经运行时间初始化为0
q->next= p;
q = p;
}
q->next = runList->next;
}
/*
重新设计排序算法,以runlist为对象,以链表排序
sortRunList排序算法已经给runlist创建了空间并排序,在RR()中runlist中的结点迟早会被删除完。
怎么给链表排序???
*/
void sortRunList2(PCB runList) {
PCB pre = runList;
PCB p = runList->next;
PCB q = p->next;
//只需要判断前一个进程的优先数是否大于后一个进程的优先数
//while (q!=runList->next) //当q为第一个进程时循环结束
//{
// if (p->Priority > q->Priority) {
// pre->next = q;
// p->next = q->next;
// q->next = p;
// //重定位pre p q
// pre = q;
// q = p->next;
// }
// else
// {
// pre = p;
// p= q;
// q = p->next;
// }
//}
for (int i = 0; i < syProcess; i++)
{
//当q为第一个进程时循环结束
if (p->Priority > q->Priority) {
pre->next = q;
p->next = q->next;
q->next = p;
//重定位pre p q
pre = q;
q = p->next;
}
else
{
pre = p;
p = q;
q = p->next;
}
}
}
/*
轮转调度算法
*/
void RR(PCB runlist) {
int sum=0; //已经完成的进程数量
PCB p= runList->next;
PCB q = runList;
PCB wc = wcList;
int sjp_count = 1;
while(sum<process_count) {
/*
如果该进程优先数最小,优先级最大,并且到达时间小于等于此时系统时间,
就执行该进程,给它一个时间片,已经运行时间=时间片
*/
if (p->arrive_time <= os_time) { //开始执行
/*
1、判断此刻能不能在一个时间片内完成
判断条件:运行时间-已经运行时间 <= 时间片
若能:则要从就绪队列中删除该进程,放入完成队列
系统时间=原系统时间 + 最后一次所需要的运行时间(等于要求运行时间 - 已经运行时间)
进程完成时间=此时的系统时间
若不能:优先数+3,当前指向进程的CPU资源给下一个进程
系统时间=原系统时间 + 一个时间片大小
每执行一次进程,都有判断该进程是否是就绪队列最后一个
若是最后一个:重新排序就绪队列
*/
if ((p->run_time-p->yjyx_time) <= sjp_Time) { //如果一个时间片就能完成
sum = sum + 1;
os_time = os_time +( p->run_time - p->yjyx_time); //此时就是该进程的完成时间
p->wc_time = os_time;
p->zz_time = os_time - p->arrive_time; //周转时间=系统时间-到达时间
//一个时间片就完成的话,就要删除该进程,放入完成队列,且sum+1
//接着把该进程放入完成队列中wcList,先放入完成队列中,再删除原就绪队列中的节点,不然数据丢失了
PCB t= (PCB)malloc(sizeof(struct node));
wc->next = t;
t->arrive_time = p->arrive_time;
t->name = p->name;
t->run_time = p->run_time;
t->wc_time = p->wc_time;
t->zz_time = p->zz_time;
t->state = 2;
t->Priority = p->Priority;
wc = t;
wc->next = wcList->next;
//删除该进程
q->next = p->next;
p= p->next;
syProcess--;
/*cout << "=========================================" << endl;
cout << "执行完的进程从就绪队列中删除,剩余就绪队列进程"<<endl;
Show(runList);
cout << "=========================================" << endl;*/
/* 编程过程中的问题分析:
查看runlist里面有没有删除完成进程,,,发现并未被删除——发现是下面else里面q赋值错误,q = p;写在了p = p->next;的后面导致的
还要删除process[]中的。后面才能继续排序——不用此方法,改为直接修改链表排序
试试不删除输出是怎么样的。????——根据排序算法,不删除就会把所有进程再重新赋值给runlist队列,不可行——此方法不用了
*/
}
//运行一个时间片内还未完成—当某一进程运行完一个时间片后,其优先级应下调,加3
else
{
os_time = os_time + sjp_Time;
p->yjyx_time = p->yjyx_time+sjp_Time;
//已经运行时间=之前的已经运行时间+时间片,继续在一个时间片执行的进程,已经运行时间就时间片
p->Priority = p->Priority +3;
//优先数变了,就要重新排序
q = p;
p = p->next;
/*
process[countnode].Priority = p->Priority;//解决排序后的priority不变的问题——不用此方法了,重新设计新的利用链表修改排序
sortRunList(runList);//对就绪队列排序
不应该在此时重新排序,应该是先遍历一遍所有就绪队列,然后重新排序
sortRunList2(runList);
-----------此处排序不能再使用这个了,因为该排序使用的还是process里面的数据,此时的priority已经改变局还没看
cout << "=========================================" << endl;
cout << "------调整优先数后重新排序的进程信息------"<<endl;
Show(runList);//会发现重新排序后已经完成的进程还在runlist队列中
cout << "=========================================" << endl;
????????????????????????????????要修改这里
此处有问题——重新排序后,p的指针还是只想修改优先级的进程上的,但是此时优先数改变了,该进程的位置也向后面调整了
就没有从未改变位置时的后面一个进程开始遍历,直接跳过了原位置到修改位置后中间的所有进程
改进方法:在排序前切换到下一个就绪队列中的进程
q = p;
p = p->next;
*/
}
//======== = 执行完一次进程,即执行完一个时间片了,可以显示打印一下此时所用进程的信息========
cout << "===============第" << sjp_count << "个时间片内=============" << endl;
sjp_count++;
Show(runlist);
} //if结束
else//若该进程还未到达时间,就执行下一个进程
{
q = p;
p = p->next;
}
/*
如果p到了第一个进程,也就是说第一遍遍历完了后
就要重新排序—轮转调度算法要先把就绪队列遍历完,
在根据重新排序的就绪队列来再遍历调度
*/
if (p == runlist->next) {
/* cout << "===============第" << sjp_count << "个时间片内=============" << endl;
sjp_count++;*/
sortRunList2(runList);
/*cout << "=========================================" << endl;
cout << "------调整优先数后重新排序的就绪队列进程信息------"<<endl;
Show(runList);
cout << "=========================================" << endl;*/
}
}//while结束
}
void main() {
cout << "请输入进程数量:";
cin >> process_count;
cout << "请输入时间片大小:";
cin >> sjp_Time;
Initia(noEnter);
cout << "------未进入队列的进程信息------" << endl;
Show(noEnter);//显示未进入队列
sortRunList(runList);//对就绪队列排序
cout << "------刚开始的就绪队列的进程信息------" << endl;
Show(runList);//显示就绪队列
RR(runList);
ShowWcList(wcList);
system("pause");
}