一、目的和要求
进程调度是处理机管理的核心内容。本实验要求用高级语言编写模拟进程调度程序,以便加深理解有关进程控制快、进程队列等概念,并体会和了解优先数算法和时间片轮转算法的具体实施办法。
二、实验内容
1.设计进程控制块PCB的结构,通常应包括如下信息:
进程名、进程优先数(或轮转时间片数)、进程已占用的CPU时间、进程到完成还需要的时间、进程的状态、当前队列指针等。
2.编写两种调度算法程序:
优先数调度算法程序
循环轮转调度算法程序
3.按要求输出结果。
三、提示和说明
分别用两种调度算法对伍个进程进行调度。每个进程可有三种状态;执行状态(RUN)、就绪状态(READY,包括等待状态)和完成状态(FINISH),并假定初始状态为就绪状态。
(一)进程控制块结构如下:
NAME——进程标示符
PRIO/ROUND——进程优先数/进程每次轮转的时间片数(设为常数2)
CPUTIME——进程累计占用CPU的时间片数
NEEDTIME——进程到完成还需要的时间片数
STATE——进程状态
NEXT——链指针
注:
1.为了便于处理,程序中进程的的运行时间以时间片为单位进行计算;
2.各进程的优先数或轮转时间片数,以及进程运行时间片数的初值,均由用户在程序运行时给定。
(二)进程的就绪态和等待态均为链表结构,共有四个指针如下:
RUN——当前运行进程指针
READY——就需队列头指针
TAIL—— 就需队列尾指针
FINISH—— 完成队列头指针
(三)程序说明
1. 在优先数算法中,进程优先数的初值设为:
50-NEEDTIME
每执行一次,优先数减1,CPU时间片数加1,进程还需要的时间片数减1。
在轮转法中,采用固定时间片单位(两个时间片为一个单位),进程每轮转一次,CPU时间片数加2,进程还需要的时间片数减2,并退出CPU,排到就绪队列尾,等待下一次调度。
2. 程序的模块结构提示如下:
整个程序可由主程序和如下7个过程组成:
(1)INSERT1——在优先数算法中,将尚未完成的PCB按优先数顺序插入到就绪队列中;
(2)INSERT2——在轮转法中,将执行了一个时间片单位(为2),但尚未完成的进程的PCB,插到就绪队列的队尾;
(3)FIRSTIN——调度就绪队列的第一个进程投入运行;
(4)PRINT——显示每执行一次后所有进程的状态及有关信息。
(5)CREATE——创建新进程,并将它的PCB插入就绪队列;
(6)PRISCH——按优先数算法调度进程;
(7)ROUNDSCH——按时间片轮转法调度进程。
主程序定义PCB结构和其他有关变量。
(四)运行和显示
程序开始运行后,首先提示:请用户选择算法,输入进程名和相应的NEEDTIME值。
每次显示结果均为如下5个字段:
name cputime needtime priority state
注:
1.在state字段中,"R"代表执行态,"W"代表就绪(等待)态,"F"代表完成态。
2.应先显示"R"态的,再显示"W"态的,再显示"F"态的。
3.在"W"态中,以优先数高低或轮转顺序排队;在"F"态中,以完成先后顺序排队。
说明: 程序包含三种调度算法
1、抢占式优先数调度算法
2、非抢占式优先数调度算法
3、时间片轮转法
//Lz毅
#include <iostream>
#include <string>
using namespace std;
typedef struct PCB
{
string NAME = "";//进程名
int PRIO_ROUND = 0;//进程优先数(或轮转时间片数)
int CPUTIME = 0;//进程已占用的CPU时间
int NEEDTIME = 0;//进程到完成还需要的时间
string STATE = "";//进程的状态
PCB* NEXT = NULL;//指向下一条的指针
}PCB;
PCB* RUN;//当前运行进程指针
PCB* READY;//就绪队列头指针
PCB* TAIL;//就绪队列尾指针
PCB* FINISH;//完成队列头指针
PCB* TAIL_F;//完成队列尾指针
PCB* create_pcb(int i, int flag)//PCB创建
{
PCB* pcb = new PCB;
cout << "请输入第" << i << "个进程名" << endl;
cin >> pcb->NAME;
cout << "请输入第" << i << "个进程需要时间" << endl;
cin >> pcb->NEEDTIME;
if (flag)//1表示优先
{
pcb->PRIO_ROUND = 50 - pcb->NEEDTIME;
}
else//0表示时间片轮转
{
pcb->PRIO_ROUND = 2;
}
pcb->STATE = "W";
return pcb;
}
void readyadd(PCB* pcb)//ready队列添加
{
PCB* s = pcb, * p, * q;//q记录p节点的前一个节点指针
p = READY->NEXT;
q = READY;
while (p != NULL && (s->PRIO_ROUND <= p->PRIO_ROUND))//保证p有节点的情况下,让p指向的节点优先数小于s ,保证队列顺序与输入的顺序相同
{
q = p;
p = p->NEXT;
}
//插入就绪队列里 插入到p前面
s->NEXT = p;
q->NEXT = s;
if (s->NEXT == NULL)//新数据插入到了队尾,需要维护尾指针
{
TAIL = s;
}
}
void run_to_ready_prio()//执行队列队头节点进入就绪队列,按照优先数从大到小排列
{
PCB* s, * p, * q;//q记录p节点的前一个节点指针
s = RUN->NEXT;
RUN->NEXT = NULL;
s->STATE = "W";
p = READY->NEXT;
q = READY;
//保证p有节点的情况下,让p指向的节点优先数小于s ,即插入后与新节点优先级相同的节点在新节点前面 (只是较之前加了个等号)
//这里考虑了由于程序没有 等待时优先级提升 的机制 同优先级下让等待时间长的程序排前面先执行(不知道是否合适)
while (p != NULL && (s->PRIO_ROUND <= p->PRIO_ROUND))
{
q = p;
p = p->NEXT;
}
//插入就绪队列里 插入到p前面
s->NEXT = p;
q->NEXT = s;
if (s->NEXT == NULL)//新数据插入到了队尾,需要维护尾指针
{
TAIL = s;
}
}
void ready_to_run_prio()//就绪队头节点放入运行队列 由于一个时间片只运行一个进程,所以运行队列同时只有一个进程节点,直接放到执行队列头结点后面作为队头节点
{
RUN->NEXT = READY->NEXT;
READY->NEXT = READY->NEXT->NEXT;
RUN->NEXT->NEXT = NULL;
RUN->NEXT->STATE = "R";
}
void run_and_ready_change_prio()//查找就绪队列中优先级最高的任务与当前运行的任务那个优先级高
{
if (READY->NEXT == NULL)//就绪队列空 直接跳过,一直执行运行队列中的进程
{
return;
}
else if (RUN->NEXT == NULL)//运行队列为空 直接从就绪队列中调入队头节点
{
ready_to_run_prio();
}
else//全不为空 取两队列队头元素优先级 判读是否抢断
{
int ready_pri = READY->NEXT->PRIO_ROUND;//ready_pri就绪队列中的最大优先级进程的优先级,就绪队列按照优先级排序,队头即为最高者
int run_pri = RUN->NEXT->PRIO_ROUND;//run_pri 执行的进程的优先级
if (ready_pri > run_pri)//优先数大的先执行,为真表示抢断 将运行队列队头节点插入到就绪队列中,就绪队头节点放入运行队列
{
run_to_ready_prio();//运行队列队头节点插入到就绪队列中
ready_to_run_prio();//就绪队头节点放入运行队列
}
}
}
void run_and_ready_change_prio_no()//非抢断 运行队列为空就将就绪队列队头放入运行队列里
{
if (RUN->NEXT == NULL)
{
ready_to_run_prio();//就绪队头节点放入运行队列
}
}
/*
在RR调度算法中,应在何时进行进程的切换,可分为两种情况:
①若一个时间片尚未用完,正在运行的进程便已经完成,就立即激活调度程序,将它从就绪队列中删除,再调度就绪队列中队首的进程运行,并启动一个新的时间片。
②在一个时间片用完时,计时器中断处理程序被激活。如果进程尚未运行完毕,调度程序将把它送往就绪队列的末尾。
*/
int ready_to_run_time()//就绪队列队头元素进入运行队列,返回使用的时间片数,最大为2
{
READY->NEXT->STATE = "R";
RUN->NEXT = READY->NEXT;
READY->NEXT = READY->NEXT->NEXT;
RUN->NEXT->NEXT = NULL;
if (READY->NEXT == NULL)//维护尾指针 指向READY队列头结点 而不是RUN队列队头元素节点
{
TAIL = READY;
}
if (RUN->NEXT->NEEDTIME >= 2)//用完时间片
{
return 2;
}
else//两个时间片没用完,进程运行完毕,立即激活调度程序,启动一个新的时间片单位(两个时间片)
{
return 1;
}
}
void into_ready_time()//执行队列任务进入就绪队列队尾
{
RUN->NEXT->STATE = "W";
TAIL->NEXT = RUN->NEXT;//维护队尾指针
TAIL = TAIL->NEXT;
RUN->NEXT = RUN->NEXT->NEXT;
}
void into_finsh()//执行队列队头节点进入完成队列队尾
{
RUN->NEXT->STATE = "F";
TAIL_F->NEXT = RUN->NEXT;
RUN->NEXT = RUN->NEXT->NEXT;
TAIL_F = TAIL_F->NEXT;//维护尾指针
TAIL_F->NEXT = NULL;//节点移动之后不会把节点之后其他队列的节点也连接到完成队列上
}
void run_prio_pcb()//任务在执行队列进行的计算。如果需要时间等于0,放入完成队列队尾,否则仍放在运行队列中
{
RUN->NEXT->CPUTIME += 1;
RUN->NEXT->NEEDTIME -= 1;
RUN->NEXT->PRIO_ROUND -= 1;
if (RUN->NEXT->NEEDTIME <= 0)//如果需要时间小于等于0.放入完成队列
{
into_finsh();
}
}
void run_time_pcb()//任务在执行队列进行的计算。如果需要时间等于0,放入完成队列,否则放入就绪的队列队尾
{
if (RUN->NEXT->NEEDTIME >= 2)//能使用两个时间片
{
RUN->NEXT->CPUTIME += 2;
RUN->NEXT->NEEDTIME -= 2;
}
else//用不完
{
RUN->NEXT->CPUTIME += 1;
RUN->NEXT->NEEDTIME -= 1;
}
if (RUN->NEXT->NEEDTIME <= 0)//如果需要时间小于等于0.放入完成队列
{
into_finsh();
}
else//否则放入就绪队列队尾
{
into_ready_time();
}
}
void show_all()//输出三个队列
{
PCB* s = NULL;
cout << "\"R\"态:" << endl;
for (s = RUN->NEXT; s != NULL; s = s->NEXT)//输出运行队列
{
cout << "name:" << s->NAME << " cputime:" << s->CPUTIME << " needtime:" << s->NEEDTIME << " priority:" << s->PRIO_ROUND << " state:" << s->STATE << endl;
}
cout << "\"W\"态:" << endl;
for (s = READY->NEXT; s != NULL; s = s->NEXT)//输出就绪队列
{
cout << "name:" << s->NAME << " cputime:" << s->CPUTIME << " needtime:" << s->NEEDTIME << " priority:" << s->PRIO_ROUND << " state:" << s->STATE << endl;
}
cout << "\"F\"态:" << endl;
for (s = FINISH->NEXT; s != NULL; s = s->NEXT)//输出完成队列
{
cout << "name:" << s->NAME << " cputime:" << s->CPUTIME << " needtime:" << s->NEEDTIME << " priority:" << s->PRIO_ROUND << " state:" << s->STATE << endl;
}
}
int main()
{
PCB* pcb;
int choice = 0;//选择算法
int choice_2 = 0;//选择是否添加进程
int num;//输入进程数量
//初始化头指针
RUN = new PCB;
READY = new PCB;
FINISH = new PCB;
RUN->NEXT = NULL;
READY->NEXT = NULL;
FINISH->NEXT = NULL;
//初始化尾指针
TAIL = READY;
TAIL_F = FINISH;
cout << "请输入选择的算法:\n1.抢占式优先数调度算法\n2.非抢占式优先数调度算法\n3.循环轮转调度算法" << endl;
cin >> choice;
switch (choice)
{
case 1:
cout << "请输入输入进程个数:" << endl;
cin >> num;
for (int i = 0; i < num; i++)
{
pcb = create_pcb(i + 1, 1);//1表示优先,0表示时间片轮转
readyadd(pcb);//就绪队列添加任务
}
cout << "-----------------------------------初始状态-----------------------------------" << endl;
show_all();
for (int cs = 1; READY->NEXT != NULL || RUN->NEXT != NULL; cs++)//运行和就绪队列有一个不空就继续执行
{
//循环
run_and_ready_change_prio();//调整运行队列和就绪队列的元素
cout << "---------------------------- 执行第 " << cs << " 个时间片 ----------------------------" << endl;
show_all();
run_prio_pcb();//对进入执行队列的任务进行操作
}
cout << "----------------------------------- 执行结束 -----------------------------------" << endl;
show_all();
break;
case 2:
cout << "请输入输入进程个数:" << endl;
cin >> num;
for (int i = 0; i < num; i++)
{
pcb = create_pcb(i + 1, 1);//1表示优先,0表示时间片轮转
readyadd(pcb);//就绪队列添加任务
}
cout << "-----------------------------------初始状态-----------------------------------" << endl;
show_all();
for (int cs = 1; READY->NEXT != NULL || RUN->NEXT != NULL; cs++)//运行和就绪队列有一个不空就继续执行
{
//循环
run_and_ready_change_prio_no();//调整运行队列和就绪队列的元素
cout << "---------------------------- 执行第 " << cs << " 个时间片 ----------------------------" << endl;
show_all();
run_prio_pcb();//对进入执行队列的任务进行操作
cout << "\n请输入要执行的动作:\n1.继续执行一个时间片\n2.新增进程" << endl;
do {
cin >> choice_2;
switch (choice_2)
{
case 1:
break;
case 2:
cout << "请输入输入进程个数:" << endl;
cin >> num;
for (int i = 0; i < num; i++)
{
pcb = create_pcb(i + 1, 1);//1表示优先,0表示时间片轮转
readyadd(pcb);//就绪队列添加任务
}
break;
default:
cout << "输入不正确,请重试" << endl;
break;
}
} while (choice_2 != 1 &&choice_2 != 2);
}
cout << "----------------------------------- 执行结束 -----------------------------------" << endl;
show_all();
break;
case 3:
cout << "请输入输入进程个数:" << endl;
cin >> num;
for (int i = 0; i < num; i++)
{
pcb = create_pcb(i + 1, 0);//1表示优先,0表示时间片轮转
readyadd(pcb);//就绪队列添加任务
}
cout << "-----------------------------------初始状态-----------------------------------" << endl;
show_all();
int x;
for (int cs = 1; READY->NEXT != NULL || RUN->NEXT != NULL;)//运行和就绪队列有一个不空就继续执行
{
//循环
x = ready_to_run_time();//查找优先级最高的任务
cout << "-------------------------- 从第 " << cs << " 个时间片开始,执行 " << x << " 个时间片 --------------------------" << endl;
cs += x;
show_all();
run_time_pcb();//对进入执行队列的任务进行操作
}
cout << "----------------------------------- 执行结束 -----------------------------------" << endl;
show_all();
break;
default:
cout << "输入不正确,请重试" << endl;
}
return 0;
}
/*
测试数据一:
1
5
A
5
B
8
C
10
D
1
E
8
测试数据二:
2
5
A
5
B
6
C
5
D
1
E
8
1
2
2
F
4
G
7
后为全1
测试数据三:
3
5
A
5
B
6
C
5
D
1
E
8
*/
运行截图(测试样例见代码末):
1、抢占式优先数调度算法:
2、非抢占式优先数调度算法:
3、时间片轮转法: