本文已参与「新人创作礼」活动,一起开启掘金创作之路。
前言
以下是大二操作系统课程设计的题目,写这篇博客为了记录通过了紧张的向老师答辩,分享此实验编译后的程序,帮助其他人学习时间片轮转法,避免陷入理解的误区。本人自我感觉输出界面和相关信息已经很简明,功能相对齐全,可以用于对自己写的算法进行测试!有什么建议,欢迎留言!
正文
一、实验题目 设计一个按照时间片轮转法实现处理机调度的程序 二、实验内容 (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;
}
}
}
}