操作系统之c语言模拟时间片轮转法课程设计(附源码及测试程序)

700 阅读9分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

以下是大二操作系统课程设计的题目,写这篇博客为了记录通过了紧张的向老师答辩,分享此实验编译后的程序,帮助其他人学习时间片轮转法,避免陷入理解的误区。本人自我感觉输出界面和相关信息已经很简明,功能相对齐全,可以用于对自己写的算法进行测试!有什么建议,欢迎留言!

正文

一、实验题目 设计一个按照时间片轮转法实现处理机调度的程序 二、实验内容 (1) 假设系统有n个进程,每个进程用一个进程控制块(PCB)来代表。进程控制块的格式如下表所示,且参数意义也相同。

PCB
进程名
链接指针
到达时间
估计运行时间
进程状态

(2) 按照进程到达的先后顺序排成一个循环队列,设一个队首指针指向第一个到达进程的首址。另外再设一个当前运行进程指针,指向当前正运行的进程。 (3) 执行处理机调度时,首先选择队首的第一个进程运行。 (4) 由于本题目是模拟实验,所以对被选中的进程并不实际启动运行,而只是执行如下操作:1)估计运行时间减1; 2)输出当前运行进程的名字。 用这两个操作来模拟进程的一次运行。 (5) 进程运行一次后,以后的调度则将当前指针依次下移一个位置,指向下一个进程,即调整当前运行指针指向该进程的链接指针所指进程,以指示应运行进程,同时还应判断该进程的剩余运行时间是否为0,若不为0,则等待下一轮的运行,若该进程的剩余运行时间为0,则将该进程的状态置为完成状态“ C”,并退出循环队列。 (6) 若就绪队列不为空,则重复上述的步骤(4)和(5)直到所有进程都运行完为止。 (7) 在所设计的调度程序中,应包含显示或打印语句,以便显示或打印每次选中进程的名称及运行一次后队列的变化情况。 三、算法流程图 在这里插入图片描述

重点!!!

测试代码:时间片轮转法测试程序.exe

  • 程序截图 在这里插入图片描述在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

说明

  • 排序算法:此程序对PCB排序采用的是冒泡排序算法,因为它是稳定的排序算法(排序前后,大小相等的元素的相对位置保持不变)。
  • 进程队列:无头结点的循环队列QLink

完整代码

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

int n = -1;  //进程数,应随机初始化
int runTimeMax = -1;  //运行时间上限
int arrivalTimeMax = -1;  //到达时间上限


struct PCB
{
	int name;  //进程名称
	int arrival_time;  //到达时间,运行到到达时间才进入队列
	int run_time;  //估计运行时间(剩余运行时间)
	char status;  //进程状态:R(ready):就绪(可运行);C(complete):结束(退出队列)
	struct PCB *next;  //下一个进程的指针
};


struct QLink  //循环链队(无头结点)
{
	struct PCB *front;  //队首指针
	struct PCB *current;  //当前执行的进程位置
	struct PCB *inserPos;  //新到达进程插入位置【插入到insertPos结点之后】
};

//插入比进程转换快!!!不是直接插到队尾
void insert(struct QLink* q, struct PCB* pcb)  //队尾插入新结点pcb
{
	if (q->front == NULL)  //执行队列为空,取第一个到达的进程作为当前运行的进程
	{
		q->front = pcb;
		q->front->next = q->front;  //构成循环队列
		q->current = pcb;
		q->inserPos = q->front;
	}
	else  //不为空时,插入到inserPos之后
	{
		pcb->next = q->inserPos->next;  //插入到insertPos和current之间
		q->inserPos->next = pcb;
		q->inserPos = pcb;  //插入位置后移,保持在current之前
	}
	//printf("链队各指针状态:\ncurrent:%d\tfront:%d\tinsertPos:%d\n", q->current->name, q->front->name, q->inserPos->name);
}


void print(struct QLink *q)  //打印循环队列
{
	struct PCB*start = q->current;
	if (start != NULL)
	{
		struct PCB *p = start;
		printf(">>>>>循环队列:");
		printf("%d[%d]  ", p->name, p->run_time);
		p = p->next;
		while (p != NULL && p != start)
		{
			printf("%d[%d]  ", p->name, p->run_time);
			p = p->next;
		}
		printf("\n");
	}
}

struct PCB* pcb;  //PCB一维动态数组指针

int cmp(const void *a, const void *b) {  //快排的回调函数
	struct PCB c = *(struct PCB*)a;
	struct PCB d = *(struct PCB*)b;
	return c.arrival_time - d.arrival_time;  //根据到达时间,由小到大排序
}

void BubbleSort(struct PCB *pcb)
{
	for (int i = 0; i < n; ++i)
	{
		int flag = 1;  //是否排序完成,可以提前结束循环
		for (int k = 0; k < n - i - 1; ++k)
		{
			if (pcb[k].arrival_time > pcb[k + 1].arrival_time)  //把到达时间晚的移到后面(对指针进程操作)
			{
				struct PCB tmp = pcb[k];
				pcb[k] = pcb[k + 1];
				pcb[k + 1] = tmp;
				flag = 0;
			}
		}
		if (flag == 1) break;
	}
}

void testInit(struct QLink* q)  //手工初始化PCB用于测试
{
	//==========初始化循环链队==============
	q->front = NULL;
	q->current = NULL;
	//==========生成PCB(就绪)==============
	n = 5;
	printf("进程数:%d\n", n);
	pcb = (struct PCB*)calloc(n, sizeof(struct PCB));
	//pcb[0].name = 1; pcb[0].arrival_time = 0; pcb[0].run_time = 3; pcb[0].status = 'R'; pcb[0].next = NULL;
	//pcb[1].name = 2; pcb[1].arrival_time = 2; pcb[1].run_time = 6; pcb[1].status = 'R'; pcb[1].next = NULL;
	//pcb[2].name = 3; pcb[2].arrival_time = 4; pcb[2].run_time = 4; pcb[2].status = 'R'; pcb[2].next = NULL;
	//pcb[3].name = 4; pcb[3].arrival_time = 6; pcb[3].run_time = 5; pcb[3].status = 'R'; pcb[3].next = NULL;
	//pcb[4].name = 5; pcb[4].arrival_time = 8; pcb[4].run_time = 2; pcb[4].status = 'R'; pcb[4].next = NULL;
	pcb[0].name = 5; pcb[0].arrival_time = 1; pcb[0].run_time = 4; pcb[0].status = 'R'; pcb[0].next = NULL;
	pcb[1].name = 3; pcb[1].arrival_time = 2; pcb[1].run_time = 3; pcb[1].status = 'R'; pcb[1].next = NULL;
	pcb[2].name = 2; pcb[2].arrival_time = 3; pcb[2].run_time = 3; pcb[2].status = 'R'; pcb[2].next = NULL;
	pcb[3].name = 1; pcb[3].arrival_time = 4; pcb[3].run_time = 3; pcb[3].status = 'R'; pcb[3].next = NULL;
	pcb[4].name = 4; pcb[4].arrival_time = 4; pcb[4].run_time = 3; pcb[4].status = 'R'; pcb[4].next = NULL;

	for (int i = 0; i < n; ++i) printf("进程%d\t到达时间:%d\t估计运行时间:%d\t状态:%c\n", pcb[i].name, pcb[i].arrival_time, pcb[i].run_time, pcb[i].status);
	//按到达时间进行快速排序
	//qsort(pcb, n, sizeof(struct PCB), cmp);  //调用C库函数排序
	BubbleSort(pcb);
	printf("\nPCB按到达时间排序:\n");
	for (int i = 0; i < n; ++i) printf("进程%d\t到达时间:%d\t估计运行时间:%d\t状态:%c\n", pcb[i].name, pcb[i].arrival_time, pcb[i].run_time, pcb[i].status);
	printf("\n");
}

void manualInit(struct QLink* q)  //手工初始化PCB用于测试
{
	//==========初始化循环链队==============
	q->front = NULL;
	q->current = NULL;
	//==========生成PCB(就绪)==============
	printf("请输入进程数:");
	scanf("%d", &n);
	pcb = (struct PCB*)calloc(n, sizeof(struct PCB));
	printf("请输入进程信息(进程名自动初始化为进程序号):\n");
	printf("进程名\t到达时间\t运行时间\n");
	for (int i = 0; i < n; ++i)
	{
		pcb[i].name = i;
		printf("进程%d:\t", pcb[i].name);
		scanf("%d%d", &pcb[i].arrival_time, &pcb[i].run_time);
		pcb[i].status = 'R';
		pcb[i].next = NULL;
	}
	for (int i = 0; i < n; ++i) printf("进程%d\t到达时间:%d\t估计运行时间:%d\t状态:%c\n", pcb[i].name, pcb[i].arrival_time, pcb[i].run_time, pcb[i].status);
	//按到达时间进行快速排序
	//qsort(pcb, n, sizeof(struct PCB), cmp);  //调用C库函数排序
	BubbleSort(pcb);
	printf("\nPCB按到达时间排序:\n");
	for (int i = 0; i < n; ++i) printf("进程%d\t到达时间:%d\t估计运行时间:%d\t状态:%c\n", pcb[i].name, pcb[i].arrival_time, pcb[i].run_time, pcb[i].status);
	printf("\n");
}




void init(struct QLink* q)
{
	//==========初始化循环链队==============
	q->front = NULL;
	q->current = NULL;
	//==========生成PCB(就绪)==============
	if (n == -1) n = rand() % 10 + 10;  //随机生成10~20个进程,没有指定数量,则随机生成n
	if (arrivalTimeMax == -1) arrivalTimeMax = n;
	if (runTimeMax == -1) runTimeMax = n;
	pcb = (struct PCB*)calloc(n, sizeof(struct PCB));  //PCB动态数组分配内存,元素个数为n
	printf("初始化:\n进程数:%d\n", n);
	printf("进程\t到达时间\t估计运行时间\t状态\n");
	for (int i = 0; i < n; ++i)
	{
		pcb[i].name = i;  //进程名:初始化为序号
		pcb[i].status = 'R';  //进程初始状态位就绪状态
		pcb[i].arrival_time = rand() % arrivalTimeMax;  //到达时间的范围0~n(使得在一个时间点内可能有多个进程或没有进程)
		pcb[i].run_time = rand() % runTimeMax + 1;  //估计运行时间(避免时间为0)
		pcb[i].next = NULL;  //链接下一个进程
		printf("%d\t%d\t\t%d\t\t%c\n", pcb[i].name, pcb[i].arrival_time, pcb[i].run_time, pcb[i].status);
	}
	//按到达时间进行快速排序
	//qsort(pcb, n, sizeof(struct PCB), cmp);  //调用C库函数排序
	BubbleSort(pcb);
	printf("\nPCB按到达时间排序:\n");
	printf("进程\t到达时间\t估计运行时间\t状态\n");
	for (int i = 0; i < n; ++i) 
		printf("%d\t%d\t\t%d\t\t%c\n", pcb[i].name, pcb[i].arrival_time, pcb[i].run_time, pcb[i].status);
	printf("\n");
}

void printPtrStatus(struct QLink *q)
{
	printf("链队各指针状态:\ncurrent:%d\tfront:%d\tinsertPos:%d\n", q->current->name, q->front->name, q->inserPos->name);
}

void algorithm(struct QLink* q)
{
	int pos = 0;  //从PCB数组中取元素的指针【pos是指向排序完的PCB存储队列的指针】
	int time = 0;  //当前时间time【经过了多少时间片】

	int insertPosMove = 1;  //插入位置指针是否需要后移【保持在current前面】【当当前指针移除后下一轮不需要后移】
	int currentPosMove = 1;  //上一轮若删除进程后只剩一个进程,则本轮无需将当前指针后移
	while (1)
	{
		//>>>>>>>>>>>>>>>>输出当前信息<<<<<<<<<<<<<<<<
		printf(">>>>>>>>>当前时刻:%d<<<<<<<<<\n", time);
		int currentMove = 1;  //当前指针是否需要后移【插入新进程之前q.front!=NULL则需要】
		if (q->front == NULL) currentMove = 0;
		while (pcb[pos].arrival_time == time)  //到达时间==当前时间:进程插入队列【插入到上一时刻运行的进程的之前】
		{
			insert(q, &pcb[pos]);  //到达的pcb插入到链队
			printf(">进程%d到达\n", pcb[pos].name);
			pos++;
		}
		//print(q);
		//printPtrStatus(q);
		if (q->front != NULL && currentMove)  //队列中有PCB可以运行
		{
			if (currentPosMove) q->current = q->current->next;  //当前运行的进程指针后移
			if (insertPosMove) {
				q->inserPos = q->inserPos->next;  //没有新的进程到达,插入位置需要跟随当前运行进程位置后移
			}
			currentPosMove = 1; //指针标志复位
			insertPosMove = 1;  //指针标志复位
			//printPtrStatus(q);
		}
		if (q->front == NULL)
		{
			printf("没有进程正在运行...\n");
		}
		else
		{
			printf("# 进程%d正在运行...\t估计运行时间:%d\n", q->current->name, q->current->run_time);
		}
		//printPtrStatus(q);
		print(q);  //打印循环队列
		//当前时间片结束时:
		if (q->front != NULL) q->current->run_time--;  //估计运行时间-1
		if (q->front != NULL && q->current->run_time == 0)  //当前进程运行结束
		{
			printf("--进程%d运行完成,移出队列!\n", q->current->name);
			q->current->status = 'C';
			if (q->current == q->current->next)  //只剩一个进程
			{
				q->current->next = NULL;
				q->front = NULL;
				q->current = NULL;
				q->inserPos = NULL;
			}
			else  //删除当前进程后,下一次insertPos不能后移
			{
				q->inserPos->next = q->current->next;
				q->current->next = NULL;
				q->current = q->inserPos;  //删除正在执行的进程后,当前进程指针应指向上一个进程,在下一轮再往后移
				//printPtrStatus(q);
				//print(q);  //打印循环队列
				insertPosMove = 0;  //插入指针下一轮不需要后移
				q->current = q->current->next;  //当前指针后移(即移到下一个时间片运行的进程)
				currentPosMove = 0;  //删除结点后,下一轮当前指针无需后移
				//printPtrStatus(q);
				//print(q);
				//if(q->current->name==q->current->next->name) currentPosMove = 0;  //当删除指针后,若当前队列中只剩下一个进程,则下一次当前指针不需要后移
			}
		}
		time++;  //时间+1
		//if (q->front != NULL)
		//{
		//	printPtrStatus(q);
		//	print(q);  //打印循环队列
		//}
		if (pos == n && q->front == NULL)  //结束条件:所有进程都执行完
		{
			printf("########所有进程运行结束!########\n");
			break;
		}
		printf("\n");
	}

}

void printWorkedStatus()
{
	printf("\n******所有进程运行结束后,各PCB的状态如下:******\n");
	printf("进程名\t到达时间\t运行时间\t状态\n");
	for (int i = 0; i < n; ++i)
	{
		printf("%d\t%d\t\t%d\t\t%c\n", pcb[i].name, pcb[i].arrival_time, pcb[i].run_time, pcb[i].status);
	}
}

void drop()  //释放内存
{
	free(pcb);
}

void main()
{

	printf(">>>>>>>>>>>>>>>>>>>>时间片轮转法实现处理机调度模拟程序<<<<<<<<<<<<<<<<<<<<<\n");
	srand((unsigned)time(NULL));
	while (1)
	{
		printf("\n");
		//菜单
		printf("*********************************************************\n");
		printf("*\t\t\t菜单\t\t\t\t*\n");
		printf("* 0、退出\t\t\t\t\t\t*\n");
		printf("* 1、设置进程信息\t\t\t\t\t*\n");
		printf("* 2、运行时间片轮转法程序(随机初始化)\t\t\t*\n");
		printf("* 3、运行时间片轮转法程序(手工初始化)\t\t\t*\n");
		printf("* 4、运行时间片轮转法程序(测试案例)\t\t\t*\n");
		printf("*\t\t\t\t\t\t\t*\n");
		printf("* 说明:\t\t\t\t\t\t*\n*\t默认进程数量n:10~20,\t\t\t\t*\n*\t进程到达时间:0~n,进程估计运行时间:1~n+1\t*\n");
		printf("*********************************************************\n\n");
		//功能选择
		int menu;
		printf("请输入命令:");
		scanf("%d", &menu);
		switch (menu)
		{
		case 0: return;
		case 1: {  //修改配置
			printf("请输入进程数\t进程到达时间的上限\t进程估计运行时间的上限:");
			scanf("%d", &n);
			scanf("%d", &arrivalTimeMax);
			scanf("%d", &runTimeMax);
			printf("设置结果:\n进程数:%d\t到达时间上限:%d\t运行时间上限:%d\n", n, arrivalTimeMax, runTimeMax);
			break;
		}
		case 2: {  //随机初始化
			struct QLink q;  //链队
			init(&q);
			algorithm(&q);
			printWorkedStatus();
			drop();
			break;
		}
		case 3: { //手工初始化
			struct QLink q;  //链队
			manualInit(&q);
			algorithm(&q);
			printWorkedStatus();
			drop();
			break;
		}
		case 4: {  //测试案例
			struct QLink q;  //链队
			testInit(&q);
			algorithm(&q);
			printWorkedStatus();
			drop();
			break;
		}
		}
	}
}