这是我参与11月更文挑战的第二十天,活动详情查看:2021最后一次更文挑战
一、实验目的和要求
目的:对操作系统中使用的进程调度算法进行改进性设计。
要求:对教材中所讲述的几种进程调度算法进行深入的分析,然后选取其中的一种算法进行改进,并编程实现此算法。
二、实验环境
Windows 10系统
三、实验预习
实验原理
先来先服务调度算法:按进程进入就绪队列的先后次序选择可以占用处理器的进程。
优先级调度算法:对每个进程确定一个优先数,该算法总是让优先数最高的进程先使用处理器。对具有相同优先数的进程,再采用先来先服务的次序分配处理器。系统常以任务的紧迫性和系统效率等因素确定进程的优先数。进程的优先数可以固定的,也可随进程执行过程动态变化。 一个高优先数的进程占用处理器后,系统处理该进程时有两种方法,一是"非抢占式",另一种是"可抢占式"。前者是此进程占用处理器后一直运行到结束,除非本身主动让出处理器,后者则是严格保证任何时刻总是让优先数最高的进程在处理器上运行(本实验采用“可抢占式”)。
实验准备
实验内容
(1)设计进程控制块PCB表结构,分别适用于优先数调度算法和先来先服务调度算法。
(2)建立进程就绪队列。对两种不同算法编制入链子程序。
(3)编制两种进程调度算法:1)优先数调度;2)先来先服务
实验设计
(1)用两种算法对多个进程进行调度,每个进程可有三个状态,并假设初始状态为就绪状态。
(2)为了便于处理,程序中的某进程运行时间以时间片为单位计算。各进程的优先数及进程需运行的时间片数的初始值均由用户给定。
(3)在优先数算法中,优先数可以先取值为n,进程每执行一次,优先数减3,进程还需要的cpu时间片数减1。在先来先服务算法中,采用固定时间片(即:每执行一次进程,该进程的执行时间片数为已执行了2个单位),这时进程还需要的时间片数减2,并排列到就绪队列的尾上。
(4)对于遇到优先数一致的情况,采用FIFO策略解决。
(5)流程图
代码实现
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define TIME 1 //定义一个时间片
//进程控制块pcb结构
typedef struct pcb
{
char name[10];//进程名称
int status;//运行态(1)、就绪态(0)
int priority;//优先级
int times;//运行时间
}pcb;
typedef pcb ElemType;
//定义pcb节点
typedef struct node
{
ElemType data;//data表示pcb结构体
struct node* next;
}PCB;
//定义队列
typedef struct linkquene
{
PCB *front;
PCB *rear;
}LinkQuene;
//初始化pcb队列
void initPcbQuene(LinkQuene *pcbquene){
pcbquene->front = pcbquene->rear = (PCB*)malloc(sizeof(PCB));
if(!pcbquene->front){
printf("申请失败");
}
pcbquene->front->next = NULL;
}
//录入进程信息
void InputProInfo(int n,LinkQuene *pcbquene){//要录入的进程数
PCB *pt = NULL;
int i=0;
for(i =0;i<n;i++){
pt = (PCB*)malloc(sizeof(PCB));
printf("请输入%d号进程的相关信息\n",i+1);
printf("名称:");
scanf("%s",pt->data.name);
pt->data.status = 0;//初始化都为就绪态
printf("优先数:");
scanf("%d",&pt->data.priority);
printf("运行时间:");
scanf("%d",&pt->data.times);
//进队列操作
pt->next = NULL;
pcbquene->rear->next = pt;
pcbquene->rear = pt;
}
}
//根据优先级排序
void sortProcess(LinkQuene *pcbquene){
PCB *head = pcbquene->front;
PCB *temp=NULL;
PCB *p = NULL;
ElemType t;//中间变量
for(temp = head->next;temp->next != NULL;temp =temp->next){
for(p = head->next;p->next != NULL;p=p->next){
if(p->data.priority < p->next->data.priority){
t= p->data;
p->data = p->next->data;
p->next->data = t;
}
}
}
}
//打印进程相关信息
void printProcess(LinkQuene *pcbquene){
PCB *pt = pcbquene->front;
//if(pcbquene->front->next == pcbquene->rear){
//pcbquene->front == pcbquene->rear;
//}
if(pcbquene->front == pcbquene->rear){
printf("就绪队列为空");
}else{
printf("就绪队列:\n");
printf("-------------------------------------\n");
printf("名称 运行状态 优先级 剩余运行时间\n");
while(pt->next != NULL){
if(pt->next->data.status == 1){//正在运行的进程
printf("%3s 运行态 %7d %7d\n",pt->next->data.name,pt->next->data.priority,pt->next->data.times);
}else if(pt->next->data.status == 0){//就绪队列中等待着的进程
printf("%3s 就绪态 %7d %7d\n",pt->next->data.name,pt->next->data.priority,pt->next->data.times);
}
pt = pt->next;
}
printf("-------------------------------------\n");
}
}
//遍历队列
int Traversal(LinkQuene *pcbquene){
int i=0;
PCB *pt = pcbquene->front;
while(pt->next!=NULL){
// printf("%s",pt->next->data.name);
i++;
pt = pt->next;
}
return i;
}
//运行函数(修改优先级,将队首元素插入至队尾)
void run(LinkQuene *pcbquene){
PCB *pt = pcbquene->front->next;
//队首进程出队列
ElemType tmp;
if(pcbquene->front == pcbquene->rear){
return;
}
tmp = pt->data;
pcbquene->front->next = pt->next;
//如果是最后一个元素出队
if(pcbquene->front->next == NULL)
{
pcbquene->front = pcbquene->rear;
}
//修改状态为运行态
tmp.status = 1;
tmp.priority-=3;
if(tmp.times-1 <= 0){
printf("进程名称:%s 状态:运行态 优先级:%d 剩余运行时间:%d\n\n",tmp.name,tmp.priority,0);
}else{
printf("进程名称:%s 状态:运行态 优先级:%d 剩余运行时间:%d\n\n",tmp.name,tmp.priority,tmp.times-1);
}
if(tmp.times <= 1){//如果当前进程的剩余运行时间为0
printf("%s运行结束!\n\n",tmp.name);
if(Traversal(pcbquene) != 0){
printProcess(pcbquene);
}
}else{//大于时间片
tmp.times-=TIME;
tmp.status = 0;
//出去的进程入队列至队尾
PCB* curpt = (PCB*)malloc(sizeof(PCB));
if(curpt){
curpt->data = tmp;
curpt->next = NULL;
pcbquene->rear->next = curpt;
pcbquene->rear = curpt;
}else{
exit(0);
}
printProcess(pcbquene);
sortProcess(pcbquene);
}
}
int main()
{
LinkQuene pcbquene;
initPcbQuene(&pcbquene);
int n;
printf("输入进程数:");
scanf("%d",&n);
InputProInfo(n,&pcbquene);
sortProcess(&pcbquene);
// Traversal(&pcbquene);
printProcess(&pcbquene);
while(pcbquene.rear != pcbquene.front){
run(&pcbquene);
}
return 0;
}
运行结果示例
四、结论
本次实验用优先级调度算法设计了一种改进性的可抢占式优先级调度算法,对多个进程进行调度。
程序设计了进程控制块pcb结构包括进程的名称、状态、优先级和运行时间。另外用到了队列结构,将就绪状态的进程存储在队列中,按照优先级从大到小对进程排序,优先级一样的进程按照入队的时间顺序排序。取队首进程投入运行,若进程未运行结束但时间片已到,则将进程添加至队尾,重新对队列排序,再取队首进程投入运行...以此循环直至队列中所有进程运行结束。
算法使用了动态优先权,进程每执行一次,优先数减3,进程还需要的cpu时间片数减1(本实验设计的一个时间片数是一个单位的运行时间),并排列到就绪队列的队尾上。进程的优先权可以随进程的推进而改变,系统总是优先处理优先级高的紧急的进程,并且也不会出现高优先级进程一直占用资源的情况。可以获得更好的调度性能。
五、过程中所遇问题
1、过程中遇到了许多技术上的小问题,列举其中一个:
因为时间片到而终止的进程如果优先级高于就绪队列中的所有进程时,它会被再次被投入运行。因此,每次一个进程因为时间片到而终止时,都要将它重新放回队列后再进行排序。但当就绪队列中只剩最后一个进程时,进程出队运行,如果进程未运行完但时间片数已到,进程再次入队就绪,这时输出的就绪队列却是空的。进程入队失败了。
解决:队列只允许在一端进行插入操作,而在另一端进行删除操作。
初始化时front=rear
插入新节点:rear->next=新节点,rear=新节点
删除节点:temp=front->next,front->next=temp->next。
当队列只剩最后一个元素时,此时front->next=rear,而要删除最后一个元素:front->next=rear->next=NULL,这时front是不等于rear的。再次添加元素,front->next依然为空,就不能通过队列的头指针遍历打印输出元素。
发现了问题之后,直接将删除节点的方式改为:
temp=front->next,front=front->next就可以了;或者通过if语句判断,当出队的元素是最后一个元素时,出队后令front=rear,问题也解决了。