一、实验目的
①理解有关进程控制块、进程队列的概念。
②掌握进程优先权调度算法和时间片轮转调度算法的处理逻辑。
二、实验内容
①设计进程控制块PCB的结构,分别适用于优先权调度算法和时间片轮转调度算法。
②建立进程就绪队列。
③编制两种进程调度算法:优先权调度和时间片轮转调度。
三、实验步骤
3.1 数据结构
1、枚举类型
enum state
{
ready, // 准备
execute, //执行
block, //阻塞
finish //完成
};//定义进程状态
2、结构体变量
//定义进程PCB
struct pcb
{
char name[4];//进程名
int priority;//优先权
int cpuTime;//CPU运行时间
int needTime;//进程运行所需时间
int count;//进程执行次数
int round;//时间片轮转轮次
state process;//进程状态
pcb* next; //指向下一个进程
};
3.2 函数以及功能
函数名称 | 功能描述 |
---|---|
void | Loading() |
pcb * | GetProcess() |
void | Display(pcb * p) |
int | ProcessFinish(pcb * q) |
void | CpuExe(pcb * q) |
void | PriorityCal() |
void | DisplayMenu() |
pcb * | GetprocessRound() |
void | CpuRound(pcb * q) |
pcb * | GetNext(pcb * k, pcb * head) |
void | SetState(pcb * p) |
void | DisplayMenu(pcb * p) |
void | RoundCal() |
void | Error() |
2.程序流程图
四、实验代码
#include<cstdio> //printf scanf
#include<windows.h> //sleep pause cls
#include<set> //set
#include<string> //string
#define PNUM 5 //进程数量
#define PTIME 50 //进程数量
#define SYSINFO "进程调度演示系统" // 系统信息
#define AUTHOR "Yrani - 依然" //作者
const char* nowTime = __DATE__ " "__TIME__ ; //当前时间
using namespace std;
/**
* @Author: Yrani - 依然
* @Date: 2022-04-11 21:00:17
* @LastEditTime: 2022-04-16 10:55:00
* @keywords: 进程调度、优先权调度 、时间片轮转、
**/
set<string> s; //判重集合
//枚举类型状态
enum state
{
ready, // 准备
execute, //执行
block, //阻塞
finish //完成
};//定义进程状态
//定义进程PCB
struct pcb
{
char name[4];//进程名
int priority;//优先权
int cpuTime;//CPU运行时间
int needTime;//进程运行所需时间
int count;//进程执行次数
int round;//时间片轮转轮次
state process;//进程状态
pcb* next;
};
//加载动画实现
void Loading()
{
int i = 0, j = 0, count = 5;
for (i = 0; i < 20; count += 5, i++)
{
printf("\n\n\n\n\n\n\n\n\n\t\t\t\t\t正在载入%s",SYSINFO);
for (j = 0; j < i % 3 + 1; j++)
{
printf(".");
}
printf( "\n\n\t\t\t\t");
for (j = 0; j <= i; j++)
{
printf("■");
}
for (j = 19 - i; j >= 0; j--)
{
printf(" ");
}
printf("%d%%", count);
if (count == 100)
{
printf(" 载入完毕! ");
system("pause");
}
Sleep(30);
system("CLS");
}
}
//输入模拟测试的进程名和执行所需时间,初始设置可模拟5个进程的调度
pcb* GetProcess()
{
pcb* q;
pcb* t;
pcb* p;
int i=0;
printf("请输入五组进程名和时间并空格隔开\n");
while(i < PNUM)
{
q = (struct pcb *)malloc(sizeof(pcb));
scanf("%s %d", q->name, &q->needTime);
while(q ->needTime > 50)
{
printf("进程运行所需时间必须小于50!\n");
scanf("%s %d", q->name, &q->needTime);
}
if (!s.count(q->name))
{
s.insert(q->name);
}
else
{
printf("对不起,该进程已存在,请从新输入!\n");
continue;
}
q->cpuTime=0;
q->priority = PTIME - q->needTime;
q->process = ready;
q->next = NULL;
if(i == 0)
{
p = q;
t = q;
}
else
{
t->next = q;//创建就绪进程队列
t = q;
}
i++;
}//while
return p;
};
//显示模拟结果,包含进程名、CPU时间、运行所需时间以及优先级
void Display(pcb* p)
{
printf("\n进程名\tcpuTime\t服务时间\t优先级\t状态\n");
while(p)
{
printf("%s", p->name);
printf("\t");
printf("%d", p->cpuTime);
printf("\t");
printf("%d", p->needTime);
printf("\t\t");
printf("%d", p->priority);
printf("\t");
switch(p->process)
{
case ready:
printf("就绪\n");
break;
case execute:
printf("执行\n");
break;
case block:
printf("阻塞\n");
break;
case finish:
printf("完成\n");
break;
}
p = p->next;
}
}
//结束进程,即将队列中各进程的所需时间设置为0
int ProcessFinish(pcb *q)
{
int bl = 1;
while(bl&&q)
{
bl = bl&&q->needTime == 0;
q = q->next;
}
return bl;
}
//选择某一进程,给它分配CPU
void CpuExe(pcb *q)
{
pcb *t = q;
int tp = 0;
while(q)
{
if(q->process != finish)
{
q->process = ready;
if(q->needTime == 0)
{
q->process = finish;
}
}
if(tp < q->priority && q->process != finish)
{
tp = q->priority;
t = q;
}
q = q->next;
}
if(t->needTime != 0)
{
t->priority -=3;
t->needTime--;
t->process = execute;
t->cpuTime++;
}
}
//计算进程优先级
void PriorityCal()
{
pcb * p;
system("cls");
//clrscr();
p = GetProcess();
int cpu = 0;
system("cls");
while(!ProcessFinish(p))
{
cpu++;
printf("cpuTime: %d",cpu);
CpuExe(p);
Display(p);
Sleep(2);
system("pause");
system("cls");
//clrscr();
}
printf("\n所有流程已经完成,请按任意键退出");
}
//展示菜单
void DisplayMenu()
{
//显示调度算法菜单,可供用户选择优先权调度算法和时间片轮转调度算法
printf("★★★★★★★★★★★★★★★★★\n");
printf("★☆☆☆%s☆☆☆☆★\n",SYSINFO);
printf("★☆☆☆◆1、优先权调度 ☆☆☆☆★\n");
printf("★☆☆☆◆2、时间片轮转 ☆☆☆☆★\n");
printf("★☆☆☆◆3、退出系统 ☆☆☆☆★\n");
printf("☆☆☆☆ @Author:%s ☆☆☆☆★\n",AUTHOR);
printf("★★★★★★★★★★★★★★★★★\n");
printf("★★★★★★★★★★★★★★★★★\n");
printf("最后编译时间:%s\n", nowTime);
printf("请输入功能编号:");
}
//时间片轮转调度算法创建就绪进程队列
pcb* GetprocessRound()
{
pcb* q;
pcb* t;
pcb* p;
int i = 0;
printf("请输入五组进程名和时间并空格隔开\n");
while(i < PNUM)
{
q = (struct pcb *)malloc(sizeof(pcb));
scanf("%s %d",q->name, &q->needTime);
while(q ->needTime > 50)
{
printf("进程运行所需时间必须小于50!\n");
scanf("%s %d", q->name, &q->needTime);
}
if (!s.count(q->name))
{
s.insert(q->name);
}
else
{
printf("对不起,该进程已存在,请从新输入!\n");
continue;
}
q->cpuTime = 0;
q->round = 0;
q->count = 0;
q->process = ready;
q->next = NULL;
if(i == 0)
{
p=q;
t=q;
}
else
{
t->next = q;
t = q;
}
i++;
}
return p;
}
//采用时间片轮转调度算法执行某一进程
void CpuRound(pcb *q)
{
q->cpuTime +=2;
q->needTime -= 2;
if(q->needTime<0)
{
q->needTime = 0;
}
q->count++;
q->round++;
q->process = execute;
}
//获取下一个进程
pcb* GetNext(pcb *k,pcb *head)
{
pcb *t;
t = k;
do
{
t = t->next;
}
while(t&&t->process == finish);
if(t == NULL)
{
t=head;
while(t->next != k&&t->process == finish)
{
t = t->next;
}
}
return t;
}
//设置队列中进程执行状态
void SetState(pcb *p)
{
while(p)
{
if(p->needTime == 0)
{
p->process = finish;//如果所需执行时间为0,则设置运行状态为结東
}
if(p->process == -execute)
{
p->process = ready;//如果为执行状态则设置为就绪
}
p = p->next;
}
}
//时间片轮转调度算法输出调度信息
void DisplayMenu(pcb *p)
{
printf("\n进程名\tcpuTime\t服务时间\t执行次数\t轮转次数\t状态\n");
while(p)
{
printf("%s", p->name);
printf("\t");
printf("%d", p->cpuTime);
printf("\t");
printf("%d", p->needTime);
printf("\t\t");
printf("%d", p->count);
printf("\t\t");
printf("%d", p->round);
printf("\t\t");
switch(p->process)
{
case ready:
printf("就绪\n");
break;
case execute:
printf("执行\n");
break;
case finish:
printf("完成\n");
break;
}
p=p->next;
}
}
//时间片轮转调度算法计算轮次及输出调度信息
void RoundCal()
{
pcb *p;
pcb *r;
system("cls");
p = GetprocessRound();
int cpu=0;
system("cls");
r = p;
while(!ProcessFinish(p))
{
cpu += 2;
CpuRound(r);
r = GetNext(r,p);
printf("cpuTime: %d", cpu);
DisplayMenu(p);
SetState(p);
Sleep(5);
system("pause");
system("cls");
}
printf("\n所有流程已经完成,请按任意键退出");
}
//错误提示
void Error()
{
printf("输入有误!!!请重新输入功能编号\n");
}
// main函数
int main()
{
Loading();
DisplayMenu();
int k = 0;
scanf("%d", &k);
while (k)
{
switch(k)
{
case 1:
PriorityCal();
k = 0;
break;
case 2:
RoundCal();
k = 0;
break;
case 3:
k = 0;
printf("退出成功!");
break;
default:
Error();
DisplayMenu();
scanf("%d", &k);
break;
}
}
return 0;
}
五、实验结果
1、载入界面
2、优先权调度
输入
优先权调度
3、时间片轮转
输入
轮转
4、输入错误
5、退出
六、实验体会总结
体会:
- 通过此次实验加深了我对进程控制块、进程队列的概念。
- 此次实验是两种算法调度模式,方式一:优先权调度算法,优点是用优先级区分紧急程度,重要程度,适用于实时操作系统。方式二:时间片轮转调度算法,优点是公平,响应快,适合于分时操作系统。
- 需要深刻的去理解掌握进程优先权调度算法和时间片轮转调度算法的处理逻辑,还需要进一步理解代码的具体逻辑是什么,每一步的操作会有什么样的结果,都需要去仔细推敲。
总结:
- 代码比较难进行修改,因为代码中涉及指针结构体,枚举类型等,具有很强的挑战性,对于c语言需要有很强的功底才可以理解和掌握其中的逻辑。
- 此次实验中,对于程序的健壮性,做了很多的考虑。比如误输入,误操作,还有程序中的bug需要去调试和修改,比如当进程的执行时间大于50的时候,程序就会出现死循环的现象,这是我们需要注意的重点内容,可以在输入的时候进行判断,以及pid 是唯一的,所以不可以重复创建相同的pid,只能有一个该pid 的进程,所以引入了判重集合进行判重,如果有相同的则无法创建。
- 对于空间的动态分配,以及指针的运用有些生疏了,需要在日后的学习中,多多查阅资料,进行知识点的查缺补漏。
- 这两种调度算法思想都很巧妙,需要我们多花时间去理解与掌握,并加以实现和运用。
- 路漫漫其修远兮,吾将上下而求索。