操作系统实验——进程调度

实验目的

  • 进一步理解进程调度的过程
  • 理解先来先服务FCFS、简单时间片轮转、动态优先级调度算法的基本原理

实验内容

  • 编写一个C或C++代码,实现以上要求的算法(至少完成2个算法),每进行一次调度,打印一次运行进程、就绪队列,以及每个进程的PCB信息。
  • 实现环境不限

基本思路及要求

输入及输出

  • 程序开始时,输入进程的个数,每个进程的名称、到达时间、服务时间、初始优先级(值越大,优先级越高)
  • 选择一个调度算法,开始运行;
  • 每进行一次调度,显示当前正在运行的进程名称、就绪队列中的进程名称(按顺序),以及当前所有进程的信息。
  • 每个进程用一个进程PCB来表示,一般包括进程名(10个字符)、到达时间、服务时间、已用时间、优先数、进程状态(运行、就绪、完成)
  • 进程到达、服务、已用时间均为时间片
  • FCFS调度算法:按照进程到达时间,依次调度,每个进程结束后,调度下一个进程。
  • 时间片轮转算法:每个进程完成一个时间片后,放弃处理机,转到就绪队列队尾(不考虑优先级)
  • 动态优先级:每个进程完成一个时间片后,优先级减1,插入到就绪队列相关位置(队列头是优先级最高的进程),每次调度,选择优先级最高的队列运行。

实验报告

  • 描述重要的数据结构以及算法流程;
  • 贴出试验结果
  • 总结从试验观察到的结果以及心得体会
  • 附示例代码:按照顺序拷贝,粘贴到ProcSchedule.cpp中;本代码在linux、windows环境下测试通过。
cpp 复制代码
#include <iostream>
#include <queue>
using namespace std;
//进程三种状态,这里增加一种,表示虽然输入,但是还没有到达进入系统时刻typedef enum ProcessState{Executing, Ready, Finish, Unarrive}STATE;
typedef struct process_pcb
{
	int ID;		//进程标识	
	int priority;	//进程优先数,值越大,优先级越高	
	int arrive_time;	//进程到达时间,以时间片为单位	
	int service_time;	//进程需要总的服务时间	
	int start_time;	//进程开始执行时间	
	int end_time;	//进程结束时间	
	int all_time;	//进程仍然需要运行时间	
	int cpu_time;	//进程已占用cpu时间 	
	const char* state;	//进程状态
}PCB;
//用于打印进程三种状态
const char* StateString[] = {"Executing", "Ready", "Finish", "--"};
//排序比较函数:按照进程到达时间升序排列
bool cmp_arrive_time(const PCB a, const PCB b);
//排序比较函数:按照进程优先数降序排序
bool cmp_priority(const PCB a, const PCB b);

void input_process(); //输入进程信息
int select_policy(); //选择进程调度策略
void print_all(int current); //打印所有进程信息
void FCFS(); //先来先服务算法
void round_robin(); //时间片轮转算法
void dynamic_prio(); //动态优先级算法

PCB* running_process = NULL;//当前运行任务
//进程到达队列,如进程还没到到达时间,则该进程仍然在到达队列中
vector<PCB> arrive_queue;
vector<PCB> ready_queue;//就绪队列
vector<PCB> finish_queue;//完成队列
int main()
{
	printf("===================================================\n");
	printf("              操作系统进程调度模拟实验            \n");
	printf("===================================================\n");
	printf("\n");
	input_process();
	print_all(-1); //打印进程的初始状态
	int policy = select_policy();
	switch (policy)
	{
	case 1:
		FCFS();
		break;
	case 2:
		round_robin();
		break;
	case 3:
		dynamic_prio();
		break;
	default:
		FCFS();
		break;
	}
}
//按进程到达时间升序排列,先到达的排在队首
bool cmp_arrive_time(const PCB a, const PCB b)
{
	return a.arrive_time < b.arrive_time;
}
//按进程优先级降序排列,优先级高的排在队首
//如优先级相同,先到的进程排在前面
bool cmp_priority(const PCB a, const PCB b)
{
	if (a.priority != b.priority) {
		return a.priority > b.priority;
	}
	else {
		return a.arrive_time < b.arrive_time;
	}
}
//选择进程调度策略
int select_policy()
{
	printf("\n请选择调度算法(输入序号选择):\n");
	printf("1.先来先服务调度(FCFS)              \n");
	printf("2.时间片轮转调度(Round-Robin)       \n");
	printf("3.动态优先级调度(DynamicPriority)   \n");
	int n;
	printf("请输入调度算法序号:");
	while (scanf_s("%d", &n)) {
		if (n > 3 || n < 1) {
			printf("对不起,输入有误,请重新输入!\n");
		}
		else {
			break;
		}
	}
	return n;
}
//输入进程信息
void input_process()
{
	int num;
	printf("请输入进程数量:");
	scanf_s("%d", &num);
	PCB	pro;
	for (int i = 1; i <= num; i++) {
		printf("\n请输入第%d个进程的到达时间、服务时间及优先级(以空格隔开):\n", i);
		scanf_s("%d%d%d", &pro.arrive_time, &pro.service_time, &pro.priority);
		pro.ID = i;
		pro.all_time = pro.service_time;
		pro.cpu_time = 0;
		pro.start_time = -1;//开始时间、结束时间默认为-1,表示尚未被调度过
		pro.end_time = -1;
		pro.state = "Unarrive";//初始化为尚未进入到达
		arrive_queue.push_back(pro);
	}
	//按照到达时间升序排队
	sort(arrive_queue.begin(), arrive_queue.end(), cmp_arrive_time);
}
//打印单个进程的信息
void print_process(PCB* pro)
{
	if (pro == NULL) {
		return;
	}
	printf("%4d%10d%10d%8d%10s", pro->ID, pro->arrive_time, pro->service_time, pro->priority, pro->state);
	if (pro->start_time == -1) {//开始时间,结束时间,剩余时间
		printf("%10s%10s%10s", "--", "--", "--");
	}
	else {
		if (pro->end_time == -1) {
			printf("%10d%10s%10d", pro->start_time, "--", pro->all_time);
		}
		else {
			printf("%10d%10d%10d", pro->start_time, pro->end_time, pro->all_time);
		}
	}
	if (pro->state == "Finish")//周转时间及带权周转时间
	{
		printf("%10d%10.2lf\n", pro->end_time - pro->arrive_time, (float)(pro->end_time - pro->arrive_time) / (float)pro->service_time);
	}
	else {
		printf("%10s%10s\n", "--", "--");
	}
}
//打印所有进程的信息
void print_all(int current)
{
	if (current == -1) {
		printf("\n进程初始状态:%d\n", current);
	}
	else {
		printf("\n当前时刻为:%d\n", current);
	}
	printf("进程号 到达时间 服务时间  优先级   状态		开始时间 结束时间 剩余时间 周转时间 带权周转时间\n");
	//首先打印正在运行的进程
	if (running_process != NULL) {
		print_process(running_process);
	}
	vector<PCB>::iterator it;
	//然后打印就绪队列中的进程
	for (it = ready_queue.begin(); it != ready_queue.end(); it++) {
		print_process(&(*it));
	}
	//然后打印完成队列中的进程
	for (it = finish_queue.begin(); it != finish_queue.end(); it++) {
		print_process(&(*it));
	}
	//然后打印仍然在到达队列中的进程
	for (it = arrive_queue.begin(); it != arrive_queue.end(); it++) {
		print_process(&(*it));
	}
}
//先来先服务算法
void FCFS()
{
	int chip = 0;//初始的时间片为0
	bool need_schedule = true;
	while (1)
	{
		//如果到达队列和就绪队列都为空,则所有进程完成
		if (!running_process && arrive_queue.empty() && ready_queue.empty()) {
			break;
		}
		//将到达队列中,到达时间为当前时间片的进程放入就绪队列中,并从到达队列中删除
		while (!arrive_queue.empty()) {
			PCB pro = arrive_queue[0];
			if (pro.arrive_time <= chip) {
				pro.state = "Ready";
				ready_queue.push_back(pro);
				arrive_queue.erase(arrive_queue.begin() + 0);
			}
			else {
				break;
			}
		}
		//判断是否需要调度,如需要就从就绪队列中拿出一个进行调度
		if (need_schedule && !ready_queue.empty())
		{
			running_process = new PCB;
			*running_process = ready_queue[0];//从就绪队首中取出一个
			ready_queue.erase(ready_queue.begin() + 0);//从就绪队列中删除之
			//调度一个程序开始运行
			running_process->start_time = chip;
			running_process->state = "Executing";
			need_schedule = false;
			print_all(chip);//打印当前所有进程的信息
		}
		//FCFS当前任务运行到结束,才可能调度下一个任务
		if (running_process) {
			running_process->end_time = running_process->start_time + running_process->service_time;
			running_process->state = "Finish";
			running_process->cpu_time = running_process->service_time;
			running_process->all_time = 0;
			chip += running_process->service_time;
			finish_queue.push_back(*running_process);//将完成任务放入完成队列中
			delete running_process;
			running_process = NULL;
			need_schedule = true;
		}
		else {
			chip += 1;
		}
	}
	//所有任务全部完成后,打印一次
	print_all(chip);
}
//时间片轮转算法
void round_robin()
{
	int chip = 0;//初始的时间片为0
	bool need_schedule = true;
	while (1)
	{
		//如果到达队列和就绪队列都为空,则所有进程完成
		if (!running_process && arrive_queue.empty() && ready_queue.empty()) {
			break;
		}
		//将到达队列中,到达时间为当前时间片的进程放入就绪队列中,并从到达队列中删除
		while (!arrive_queue.empty()) {
			PCB pro = arrive_queue[0];
			if (pro.arrive_time <= chip) {
				pro.state = "Ready";
				ready_queue.push_back(pro);
				arrive_queue.erase(arrive_queue.begin() + 0);
			}
			else {
				break;
			}
		}
		//判断是否需要调度,如需要就从就绪队列中拿出一个进行调度
		if (need_schedule && !ready_queue.empty())
		{
			running_process = new PCB;
			*running_process = ready_queue[0];//从就绪队首中取出一个
			ready_queue.erase(ready_queue.begin() + 0);//从就绪队列中删除之
			//调度一个程序开始运行
			if (running_process->start_time == -1) {//首次运行
				running_process->start_time = chip;
			}
			running_process->state = "Executing";
			need_schedule = false;
			print_all(chip);//打印当前所有进程的信息
		}
		//当前运行任务完成个时间片,判断该任务是否已经完成
		if (running_process) {
			running_process->cpu_time += 1;
			running_process->all_time -= 1;
			if (running_process->all_time == 0) {//任务运行结束
				running_process->end_time = chip + 1;
				running_process->state = "Finish";
				finish_queue.push_back(*running_process);//将其放入完成队列中
				delete running_process;
				running_process = NULL;
				need_schedule = true;
			}
			else {//任务没有完成,如果就绪队列中仍有任务,则轮转调度,否则不调度(这里是一种调度的优化,避免重新调度开销)
				if (!ready_queue.empty()) {
					running_process->state = "Ready";
					ready_queue.push_back(*running_process);//将其放回就绪队列中
					delete running_process;
					running_process = NULL;
					need_schedule = true;
				}
				else {
					need_schedule = false;
				}
			}
		}
		chip += 1;
	}
	//所有任务全部完成后,打印一次
	print_all(chip);
}
// 动态优先级算法
void dynamic_prio()
{
	int chip = 0;//初始的时间片为0
	bool need_schedule = true;
	while (1)
	{
		//如果到达队列和就绪队列都为空,则所有进程完成
		if (!running_process && arrive_queue.empty() && ready_queue.empty()) {
			break;
		}
		//将到达队列中,到达时间为当前时间片的进程放入就绪队列中,并从到达队列中删除
		while (!arrive_queue.empty()) {
			PCB pro = arrive_queue[0];
			if (pro.arrive_time <= chip) {
				pro.state = "Ready";
				ready_queue.push_back(pro);
				arrive_queue.erase(arrive_queue.begin() + 0);
			}
			else {
				break;
			}
		}
		if (!ready_queue.empty()) {
			//将就绪进程按照优先级降序排列
			sort(ready_queue.begin(), ready_queue.end(), cmp_priority);
		}
		//判断是否需要调度,如需要就从就绪队列中拿出一个进行调度
		if (need_schedule && !ready_queue.empty())
		{
			running_process = new PCB;
			*running_process = ready_queue[0];//从就绪队首中取出一个
			ready_queue.erase(ready_queue.begin() + 0);//从就绪队列中删除之
			//调度一个程序开始运行
			if (running_process->start_time == -1) {//首次运行
				running_process->start_time = chip;
			}
			running_process->state = "Executing";
			need_schedule = false;
			print_all(chip);//打印当前所有进程的信息
		}
		//当前运行任务完成个时间片,判断该任务是否已经完成
		if (running_process) {
			running_process->cpu_time += 1;
			running_process->all_time -= 1;
			if (running_process->all_time == 0) {//任务运行结束
				running_process->end_time = chip + 1;
				running_process->state = "Finish";
				finish_queue.push_back(*running_process);//将其放入完成队列中
				delete running_process;
				running_process = NULL;
				need_schedule = true;
			}
			else {//任务没有完成,如果就绪队列中仍有任务,且优先级大于或等于本任务的优先级,则轮转调度,否则不调度
				if (running_process->priority > 1) {
					running_process->priority -= 1;//优先级最小为1
				}
				if (!ready_queue.empty() && ready_queue[0].priority >= running_process->priority) {
					running_process->state = "Ready";
					ready_queue.push_back(*running_process);//将其放回就绪队列中
					delete running_process;
					running_process = NULL;
					need_schedule = true;
				}
				else {
					need_schedule = false;
				}
			}
		}
		chip += 1;
	}
	//所有任务全部完成后,打印一次
	print_all(chip);
}
相关推荐
Camellia-Echo4 分钟前
【Linux从青铜到王者】Linux进程间通信(一)——待完善
linux·运维·服务器
TheITSea5 分钟前
云服务器宝塔安装静态网页 WordPress、VuePress流程记录
java·服务器·数据库
Linux运维日记15 分钟前
k8s1.31版本最新版本集群使用容器镜像仓库Harbor
linux·docker·云原生·容器·kubernetes
数据小爬虫@17 分钟前
利用Python爬虫获取淘宝店铺详情
开发语言·爬虫·python
嚯——哈哈22 分钟前
轻量云服务器:入门级云计算的最佳选择
运维·服务器·云计算
我是唐青枫24 分钟前
Linux dnf 包管理工具使用教程
linux·运维·服务器
高 朗28 分钟前
【GO基础学习】基础语法(2)切片slice
开发语言·学习·golang·slice
飞升不如收破烂~33 分钟前
redis的map底层数据结构 分别什么时候使用哈希表(Hash Table)和压缩列表(ZipList)
算法·哈希算法
九圣残炎37 分钟前
【从零开始的LeetCode-算法】3354. 使数组元素等于零
java·算法·leetcode
寒笙LED43 分钟前
C++详细笔记(六)string库
开发语言·c++·笔记