操作系统实验1—基于优先数的时间片轮转调度算法调度处理模拟程序设计

一、实验目的

  1. 对进程调度的工作做进一步的理解。
  2. 了解进程调度的任务。
  3. 通过编程掌握基于优先数的时间片轮转调度算法具体实现过程。

二、实验内容

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、最后输出每个进程的周转时间。

三、再学轮转调度算法

操作系统第三章—处理机调度及调度算法_每天八杯水的博客-CSDN博客

四、编程

运行环境: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");
}

运行结果:

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值