模拟进程状态转换

模拟进程状态转换

一、实验目的

通过实验理解进程基本概念、状态转换及其控制。

二、实验内容

利用高级语言编写程序,模拟进程状态转换的过程。
进程的主要状态:就绪、运行、阻塞、终止,状态之间的转换如下图所示:
在这里插入图片描述

实现提示:
(1)采用进程控制块(PCB)描述一个进程的所有信息包括:进程标识符、处理机状态、进程调度信息以及控制信息,它是系统感知进程存在的唯一标志,进程与 PCB是一一对应的。
(2)对PCB 中的内容在此可做一些简化处理,只包括:进程标识符(内部标识符、外部标识符)、当前状态(“就绪”,“阻塞”,“执行”和“终止”)、要求运行的时间片数、优先级等。
(3)模拟过程
 按优先级高低对进程进行调度,每个进程可有四个状态,即:“就绪”,“阻塞”,“执行”和“终止”(假设优先数越大优先级越高,以及进程的初始状态为就绪状态)。
 为便于处理,应该随机地赋予所有进程各自的“优先级”和“要求运行的时间片数”,由用户给定。
 程序中共设 3 个队列:就绪队列,阻塞队列和终止队列。
 按优先级调度时,进程每运行一次,优先级降低一位,要求的时间片数减 1,如果要求的时间片数为零了,则将其加入终止队列。否则,将其按照优先级的次序重新加入就绪队列。
 要求的时间片数为零了,则将其加入终止队列。否则,将其按照优先级的次序重新加入就绪队列。
 进程运行过程中,遇到阻塞时,将该进程加入阻塞队列。(通过随机函数得到随机数,满足某种情况则使之加入到阻塞队列,或人为设置是否阻塞)。
 重复调度,直到就绪队列中所有进程全部运行结束。
(4)主要功能模块 (参考)
 初始化进程:初始化进程的基本数据
 新进程插入就绪队列:向就绪队列插入进程(按优先级排序)
 调度:从就绪队列中选取一进程运行一个时间片
 阻塞:将被阻塞的执行进程放入阻塞队列
 唤醒:唤醒阻塞队列中的进程
 终止:完成的进程放入消亡队列
 显示:输出各队列中进程的所有信息,包括正在执行的进程
(5)输入/输出
 输入要求:进程数以及每个进程的标识符(按顺序数字编号)、当前状态(默认为“就绪”)、要求运行的时间片数、初始优先级。(要能随时添加新进程)
 输出要求:每次进程状态发生转换时,输出各队列进程的信息。

三、代码:

/*
* 作者:
功能:模拟进程状态转换(进程数范围:1-10)
4个PCB状态标识常量:1.就绪Ready、2.运行Running、3.阻塞Block、4.终止Terminate
用Run()函数去模拟的CPU的run:所以Run函数应包含以下情况的处理(即,4种队列对应的处理情况):
		(1)就绪队列(为了方便找最大优先级,当然用priority_queue更方便,但我采用更熟悉的链表结构):
			就绪_1->就绪_1
			就绪_1->运行_2

		(2)运行队列
			运行_2->就绪_1
			运行_2->运行_2
			运行_2->阻塞_3
			运行_2->终止_4
		
		(3)阻塞队列(唤醒概率:1/8、被阻塞概率:1/4)
			阻塞_3->唤醒->就绪_1
			阻塞_3->唤醒->运行_1,(当就绪、运行队列为空时的情况,跳过进入就绪,直接唤醒到运行)
			阻塞_3->阻塞_3//唤醒概率没有满足的时候

		(4)终止队列:
			对终止队列里的进程不必要进行处理
*/

#include <iostream>
#include <cstdlib>
#include<queue>
#include <windows.h>
using namespace std;
enum State {
	Ready,Running,BlockForIO,Terminate
};
struct PCB {
	int id = 0;
	int priority = 0;//初始化进程时,随机分配,抢占式优先级分配
	int timeslice = 0;//初始化进程时,随机分配
	State state = Ready;//假设各进程满足除CPU外一切资源,初始状态为就绪
	PCB* next=NULL;
};
PCB* head=NULL;
queue<PCB*> runningQueue, blockQueueForIO, terminateQueue;
int blockedProbability;
void InitPCB();
PCB* FindMaxPriority();
void DeletePCB(int id);
void Run();
void	PrintPCB();
void Display_TerminateQueue();
void Display_BlockQueueForIO();
void Display_RunningQueue();
void Display_Ready();
void InsertHead(PCB* p);

void InitPCB() {
	srand((unsigned)time(NULL));
	int processNumber=rand()%10+1;//随机进程个数范围:1-10
	PCB* p=NULL;
	PCB* q=NULL;
	for (int i = 0; i < processNumber; i++) {
		q=new PCB;
		q->id = i+1;
		q->priority = rand() % 30 + 1;//优先级范围,为了显示效果更好,使优先级范围小一点
		q->state = Ready;
		q->timeslice = rand() % 10+1;//同理
		if (i == 0) {
			head = q;
			p= q;
		}
		else {
			p->next=q;
			p = p->next;
		}
	}
}
PCB* FindMaxPriority() {
	PCB* p = head;
	PCB* q =NULL;
	int max_priority = 0;
	if (head == NULL)return NULL;
	while (p) {
		if (p->priority > max_priority) {
			q = p;
			max_priority = p->priority;
		}
		p = p->next;
	}
	return q;
}
void DeletePCB( int id) {
	PCB* p=head;
	PCB* q=p->next;
	if (p->id == id) {
		p->next = NULL;
		head = q;
	}
	else {
		while (q->id!=id) {
			p = p->next;
			q = p->next;
		}
		p->next = q->next;
		q->next = NULL;
	}
}

void Run() {
	srand((unsigned)time(NULL));
	PCB* p=NULL;
	PCB* q= FindMaxPriority();
	/*假设:当上一次运行完成时时,cpu才会有空去(找可运行性的就绪状态进程)
		或者(检查唤醒条件是否满足也可以说去检查是否有唤醒信号,所以应放在此位置)
		注意:之所以先去检查唤醒而不是去找可运行性的就绪状态进程(即以下代码的顺序问题),
		是为了避免当所有可执行进程执行完了才去唤醒操作,不符合现实
		*/
	if (!blockQueueForIO.empty()) {
		//理论上我认为,因为在多次频繁检查IO占用情况时,IO一段时间都是被占用的,被唤醒的概率会比阻塞概率小
		int awakenedProbability = rand() % 8 + 1;//唤醒概率:1/8
		if (awakenedProbability == 4) {
			p = blockQueueForIO.front();
			blockQueueForIO.pop();
			p->state = Ready;
			InsertHead(p);
		}
	}
	if (runningQueue.empty()) {
		if (head!=NULL) {
			q->state = Running;
			DeletePCB(q->id);
			runningQueue.push(q);
		}else {
			//当运行队列、就绪队列为空时,此时资源一定不会占用,这也是唤醒概率的弊端,直接进入运行队列
			if (!blockQueueForIO.empty()) {
				p = blockQueueForIO.front();
				blockQueueForIO.pop();
				p->state = Running;
				runningQueue.push(p);
				//此时阻塞也不可能发生
				blockedProbability = 0;
			}
			else {
				return;
			}
		}
	}else {
		/*运行队列里的进程一定是可以执行的,而且在函数开头,此进程一定打印过,
		不会出现刚调入到运行队列还没有打印就被放入阻塞队列的的情况,
		由于当运行队列里的进程下个时间段可以执行时,再考虑它会不会阻塞
		*/
		blockedProbability = rand() % 4 + 1;//被阻塞概率:1/4
		//如果运行队列为空,即使达到阻塞概率也不可能会阻塞
		if (blockedProbability == 2&&head!=NULL) {
			p=runningQueue.front();
			runningQueue.pop();
			p->state = BlockForIO;
			blockQueueForIO.push(p);
			q->state = Running;
			DeletePCB(q->id);
			runningQueue.push(q);
		}
		p=runningQueue.front();
		p->timeslice -= 1;
		p->priority -= 1;
		if (p->timeslice < 1) {
			runningQueue.pop();
			p->state = Terminate;
			terminateQueue.push(p);
			if (q) {
				DeletePCB(q->id);
				q->state = Running;
				runningQueue.push(q);
			}
		}
		else if (q) {//若就绪队列没进程,一定不会阻塞并且当前进程也不会进入就绪队列,所以当就绪队列有进程是时,凭概率设置阻塞
			//当优先级不是最高时,当前进程一定会下台,所以下次也一定没有它运行的机会,阻塞的意义:当下次还有运行的机会时,给他阻塞,剥夺他运行的机会
			if (p->priority < q->priority) {
				runningQueue.pop();
				p->state = Ready;
				InsertHead(p);
				q->state = Running;
				DeletePCB(q->id);
				runningQueue.push(q);
			}
		}
	}
	PrintPCB();
	cout << endl << endl<<endl<<endl;
	Run();
}
void InsertHead(PCB* p) {
	p->next = head->next;
	head->next = p;
}
void Display_Ready() {
	PCB* p = head;
	while (p) {
		cout << "ID:	" << p->id << "		Priority:	" << p->priority << "		Timeslice:		" << p->timeslice << endl;
		p = p->next;
	}
}
void Display_RunningQueue() {
	if (!runningQueue.empty()) {
		PCB* t = runningQueue.front();
		cout << "ID:	" << t->id << "		Priority:	" << t->priority << "		Timeslice:		" << t->timeslice << endl;
	}
}
void Display_BlockQueueForIO() {
	PCB* tempPCBArray[10];
	int n = 0;
	PCB* t;
	if (!blockQueueForIO.empty()) {
		while (!blockQueueForIO.empty()) {
			t = blockQueueForIO.front();
			tempPCBArray[n] = t;
			n++;
			cout << "ID:	" << t->id << "		Priority:	" << t->priority << "		Timeslice:		" << t->timeslice << endl;
			blockQueueForIO.pop();
		}
		for (int i = 0; i < n; i++) {
			blockQueueForIO.push(tempPCBArray[i]);
		}
	}
}
void Display_TerminateQueue() {
	PCB* tempPCBArray[10];
	int n = 0;
	PCB* t;
	if (!terminateQueue.empty()) {
		while (!terminateQueue.empty()) {
			t = terminateQueue.front();
			tempPCBArray[n] = t;
			n++;
			cout << "ID:	" << t->id << "		Priority:	" << t->priority << "		Timeslice:		" << t->timeslice << endl;
			terminateQueue.pop();
		}
		for (int i = 0; i < n; i++) {
			terminateQueue.push(tempPCBArray[i]);
		}
	}
}
void	PrintPCB() {
	cout << "就绪队列:" << endl; Display_Ready(); cout << endl;
	cout << "运行队列:"<<endl; Display_RunningQueue(); cout << endl;
	cout << "IO阻塞队列:" << endl; Display_BlockQueueForIO(); cout << endl;
	cout << "终止队列:" << endl; Display_TerminateQueue(); cout << endl;
}

int main() {
	InitPCB();
	Run();
	return 0;
}

总结

以上代码仍然有好多改进之处(代码冗余部分我就懒得改了),出于老师的要求程度和时间考虑,就这么写吧。转载请注明出处。

  • 7
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.目的: 自行编制模拟程序,通过形象化的状态显示,深入理解进程的概念、进程之间的状态转换及其所带来的PCB内容 、组织的变化,理解进程与其PCB间的一一对应关系。 2. 内容及要求: 1) 设计并实现一个模拟进程状态转换及其相应PCB内容、组织结构变化的程序。 2) 独立编写、调试程序。进程的数目、进程状态模型(三状态、五状态、七状态或其它)以及PCB的组织形式可自行选择。 3) 合理设计与进程PCB相对应的数据结构。PCB的内容要涵盖进程的基本信息、控制信息、资源需求及现场信息。 4) 设计出可视性较好的界面,应能反映出进程状态的变化引起的对应PCB内容、组织结构的变化。 5) 代码书写要规范,要适当地加入注释。 6) 认真进行预习,完成预习报告。 7) 实验完成后,要认真总结,完成实验报告。 3.使用的数据结构及说明: 在本实验中,主要用到的数据结构是PCB的结构,其中PCB的数据结构如下: struct PCB { int P_Id; //PCB的ID号 char P_Name[10]; //PCB的名称 char P_State[10]; //PCB状态 int P_Runtime; //PCB的所需要的运行时间 int P_Requiry; //PCB所需要的资源要求 struct PCB * next ; //PCB块的下一个指针 } ; 其中,P_Id,和P_Name用来标示一个进程,而P_State用来标示进程的五种状态:Create_state,Ready_state,Block_state,Run_state,Exit_state。P_Runtime标示要完成一个进程所需要的时间。P_Requiry标示一个进程的执行所需要的其他条件,当其他的条件满足,则P_Requiry置1,否则置0。Struct PCB * next 用来指向同一队列中的下一个PCB块。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值