操作系统课程设计:设计一个按照时间片轮转法实现处理机调度的程序

操作系统课程设计:设计一个按照时间片轮转法实现处理机调度的程序

一:时间片轮转法实现处理机调度的程序设计提示如下:

(1)
假设系统有n个进程,每个进程用一个进程控制块(PCB)来代表。进程控制块的格式如下表所示,且参数意义也相同。

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

二:程序中使用的数据结构以及主要符号说明;

(1)程序中时间片轮转的实现采取了链式队列的数据结构。需要分别创建一个节点结
构 QueueNode 以及一个队列结构Queue。QueueNode中有数据以及指针(题目
中的链接指针).队列中包括队头head以及队尾指针tail。
(2)一个进程结构 process 结构里有:进程名字:processname、到达时间:arrivetime
运行时间:runtime、进程状态:status,将所有的进程放入一个节点结构数组中
(sturct process a[])
(3)程序中的主要符号说明:
n:进程个数 num:运行的次数 m:队列中的实时进程数量

三:程序流程图和带有注释的源程序

操作系统课程设计:设计一个按照时间片轮转法实现处理机调度的程序_第2张图片

源程序:

#include 
#include
#include
#define N 100
using namespace std;
typedef string datatype;//进程的名字类型
typedef int intt;
typedef struct process//进程结构
{
	datatype processname;//进程名字
	intt arrivetime;//到达时间
	intt runtime;//运行时间
	datatype status;//进程状态
};
typedef struct QueueNode//定义一个节点	
{
    process data;
	QueueNode* next;
}QueueNode;
typedef struct Queue//定义队列
{
	QueueNode* head;//队头	
	QueueNode* tail;//队尾
	size_t size;//记录当前队列里的元素个数
}Queue;
void QueueInit(Queue *&q)//队列初始化
{
	q = (Queue *)malloc(sizeof(Queue));
	q->head = NULL;
	q->tail = NULL;
	q->size = 0;
}
void QueuePush(Queue *&q, process data)//入队
{
	QueueNode* newnode = new QueueNode;//创建新的节点
	newnode->data= data;//节点数据等于要入队的数据
	newnode->next = NULL;//节点的链接指针设为空
	if (q->size == 0)//将节点送入队列
	{
		q->head = newnode;
		q->tail = newnode;
		q->size++;
	}
	else
	{
		q->tail->next = newnode;
		q->tail = newnode;
		q->size++;
	}
}
bool QueuePop(Queue *&q)//出队
{
	if (q->size == 0)
	{
		exit(1);
	}
	else if (q->size == 1)
	{
		q->head = NULL;
		q->tail = NULL;
		q->size--;
	}
	else
	{
		QueueNode* p = q->head;//领队列的头指针指向一个新的节点
		p->data= q->head->data;//该节点的数据等于要出队的数据
		q->head = p->next;//另头指针指向下一个节点
		q->size--;
		free(p);
	}
}
int Queuesize(Queue*& q)//返回队列长度
{
	return q->size;
}
void ShowQueue(Queue*& q)//展示队列中的顺序(即执行顺序)
{
	QueueNode* p = q->head;
	for (int i = 0; i < Queuesize(q); i++)
	{
		if (i == 0)
		{
			cout << "进程" << q->head->data.processname << "执行  ";
		}
		else
		{
			p = p->next;
			cout << "进程" << p->data.processname << "等待  ";
		}
	}
}
void bubbleinsert(struct process  a[] ,int len)//把进程的到达时间按照冒泡排序。
{
	struct process b[N] ;
	for (int i = 0; i < len; i++)
	{
		for (int j = i + 1; j < len; j++)
		{
			if (a[j].arrivetime < a[i].arrivetime)
			{
				b[1]= a[i];
				a[i] = a[j];
				a[j] = b[1];
			}
		}
	}
}
void change(struct process a[],int n)//将已经运行完成的进程移出data
{
	int i = 0;
	int j = 0;
	while (j < n)//去除a[]中 a[].runtime=0的进程。
	{
		if (a[j].runtime != 0)
		{
			a[i] = a[j];
			i++;
		}
		j++;
	}
}
int main()
{
	srand((unsigned)time(NULL));
	int n = 0, m = 0, pnum = 0;
	int  flag;
	struct process p[N] ;
	Queue *q;
	QueueInit(q);
	n = rand() % 10 + 1;
	cout << "进程个数:" << n << endl;
	cout << "\n进程初始化(输入各个进程的数据):\n";
	for (int i = 0; i < n; i++)//进程初始化
	{
		p[i].processname='a'+i;
		p[i].arrivetime=rand()%15;
		p[i].runtime=rand()%6+1;
		p[i].status = "P";
	}
	bubbleinsert(p, n);//把进程按照到达时间顺序排序
	cout << "进程名字" << "\t" << "到达时间" << "\t" << "需运行时间" << "\t" << "进程状态" << endl;
	for (int i = 0; i < n; i++)//进程初始化
	{
		cout << "   " << p[i].processname << " \t\t"
			<< "   " << p[i].arrivetime << " \t\t"
			<< "   " << p[i].runtime << " \t\t"
			<< "   " << p[i].status << endl;
	}
	m = n;
	for (int i = 0;; i++)//时间
	{
		flag =0;//表示队列中的个数没有变化
		cout << "\n时间" << i << ": " << endl;
		n = m;
        for (int j = 0; j < n; j++)
		{
			if (p[j].arrivetime == i)//如果到达时间与时间相同
			{
				QueuePush(q, p[j]);//入队
				cout << "进程" << p[j].processname << "到达并开始执行" << endl;
				pnum++;
			}
		}
		if (Queuesize(q) != 0)//更新process中的数据
		{
			for (int k = 0; k < n; k++)
			{
				if (p[k].processname == q->head->data.processname)
				{
					p[k].runtime--;
					if (p[k].runtime == 0)
					{
						p[k].status = 'C';
						m--;
						flag = 1;
					}
				}
			}
			//处理队列中的数据
			ShowQueue(q);//展示执行、等待顺序
			q->head->data.runtime--;//头指针指向的进程处理时间-1
			if (q->head->data.runtime == 0)
			{
				q->head->data.status = "C";//表示进程完成
				cout << "进程" << q->head->data.processname << "处理完毕,移出队列!" << endl;
				QueuePop(q);//出队
			}
			else
			{
				process p1;
				p1 = q->head->data;
				QueuePop(q);//出队
				QueuePush(q, p1);//重新入队
			}
			cout << "\n运行" << i << "次后队列中的情况" << endl;
			for (int t2 = 0; t2 < pnum; t2++)
			{
				cout << "第" << t2 + 1 << "个进程:" << "进程名:" << p[t2].processname
					<< "\t剩余处理时间:" << p[t2].runtime << "\t进程状态:" << p[t2].status << endl;

			}
			if (flag == 1)
			{
				pnum--;//队列中的进程数量减1
			}

			change(p, n);
			if (m == 0)// 所有进程都运行完毕
			{
				cout << "\n全部进程都已处理完毕,队列中无元素,结束程序";
				break;
			}
		}
		else
		{
			cout << "\n无进程到达、无进程运行" << endl;
		}
	}
}

四:执行程序名,并打印程序运行时的初值和运算结果

操作系统课程设计:设计一个按照时间片轮转法实现处理机调度的程序_第3张图片
操作系统课程设计:设计一个按照时间片轮转法实现处理机调度的程序_第4张图片

五:实验结果分析,实验收获和体会

由最终的实验结果得:
输入进程数据后,会自动按照进程的到达顺序将进程排好序并且输出;
每个时间片运行一次后,当前运行的进程剩余时间-1,并且会将运行的结果输出(如**进程处理完毕,移出队列!),并且现实该次运行以后,队列中进程的信息;直到所有进程全部运行完毕。
实验总结:
一开始的时候,我把进程结构process单独写出来了,没有与QueueNode中的data进行关联,导致在进程入队出队的时候用到process结构数组的过于多,使队列存存在感很低,滥用了内存。所以做的第一个改进就是把process和QueueNode中的data进行关联(process *data),这样就能使所有的数据直接全部压入队列中,方便出入队列,减少了内存的开销。

实验中我使用了一个for循环代表时间的增加,每一次时间片内运行队列中的第一个进程(运行完之后如进程运行完毕则移出队列,反之则将该进程放到队尾)并且接收该时间到达的新进程将其排到队尾从而实现“时间片轮转算法”,然后用一个结构数组来表示所有进程的数据。但是结构数组和队列没有关联,导致运行结束的进程出队之后,相应的数组中的数据没有进行更新反而还留着。就出现了在下一次轮转的时候原本结束的进程依然出现在队列中,而且runtime=-1并且无限循环。所以我设置了一个删除数组元素的函数change(struct process a[],int n),这样运行完毕的进程相应的数据也会从process的结构数组中删除。

这次实验让我温习了结构数组、以及链式队列的相关知识点以及操作。并让我很深刻的体会了时间片轮转算法的运行过程。实验中的时间复杂度还有待改善:在“轮转的时候时间复杂度为:O(n),但是对进程的到达时间排序的时候本实验用的是冒泡排序,时间复杂度为:o(n^2),.可以采取同样稳定的时间复杂度为nlog(n)的归并排序

你可能感兴趣的:(操作系统)