目录
1.短作业优先调度算法(SJF)
【算法思想】
SJF(Shortest Job First)算法是一种基于执行时间的调度算法,其核心思想是通过不断选择剩余执行时间最短的进程来进行调度,以尽可能地减少作业的等待时间和周转时间,提高系统的响应速度和效率。
下面是算法是实现的具体思想:
①选择最短作业优先执行:
- 在每次调度时,系统会从就绪队列中选择剩余执行时间最短的进程执行。
- 这个选择是基于进程的剩余执行时间来进行的,因此算法更倾向于执行短作业,以减少平均等待时间和周转时间。
②动态调整:
- 每次执行一个进程后,系统时间会更新,而剩余进程的执行时间可能会发生变化。
- 因此,在每次调度时,系统都会重新评估剩余进程的执行时间,并选择当前最短的作业来执行。
- 这种动态性确保了系统在每次调度时都能够做出最优的选择,以适应进程执行时间的变化。
③等待时间计算:
- 在执行进程之前,系统会计算当前进程的等待时间。
- 等待时间是指进程从就绪状态到开始执行的时间间隔。通过减少等待时间,SJF算法可以提高系统的效率和响应速度。
④周转时间和带权周转时间计算:
- 在进程执行完成后,系统会计算每个进程的周转时间和带权周转时间。
- 周转时间是指进程从提交到完成所经历的总时间,包括等待时间和执行时间。
- 带权周转时间是周转时间除以执行时间,用于评估进程的相对性能。
- 计算公式如下
平均周转时间
(其中n是进程总数,Ti是第i个进程完成执行的时间,Ai是第i个进程到达系统的时间)
平均带权周转时间
(其中,Bi是第i个进程的执行时间)
⑤实现细节:
- 实现SJF算法需要维护当前系统时间、每个进程的到达时间和执行时间等信息。
- 算法通过迭代遍历进程列表来选择最短作业,并在调度过程中更新系统时间和进程信息。
- 每次调度都需要动态地选择最短作业,以确保系统能够充分利用资源并提高整体效率。
【算法实现】
//短作业优先调度算法
Status SJFScheduling(PCB *pHead) {
// 如果进程列表为空,则返回错误码
if (pHead == NULL) return ERROR;
// 初始化当前时间、等待时间、开始时间、计时器以及平均周转时间和平均带权周转时间
int currentTime = CURRENT_TIME; // 当前系统时间
int waitTime = 0; // 等待时间
int startTime = currentTime; // 进程开始执行的时间
int timer = 0; // 计时器,记录调度次数
float ave_tat = 0.0; // 平均周转时间
float ave_w_tat = 0.0; // 平均带权周转时间
// 打印即将被调度的进程列表
printf(" ## 短作业优先算法 ## \n");
displayProcesses(pHead); // 显示进程列表
// 开始调度循环
for (PCB *p = pHead; p != NULL;) {
int min_remaining_time=INT_MAX; // 初始化最短执行时间为一个较大的值
PCB *t = p; // 保存当前进程指针,以便释放内存
PCB *selected = p, *preSelected = NULL, *prev = NULL;
// 寻找可执行的最短作业
for (PCB *q = p; q; q = q->next) {
if (q->arrivingTime <= currentTime && q->neededRunningTime <= min_remaining_time) {
selected = q; // 选定当前最短作业
min_remaining_time = q->neededRunningTime; // 更新最短作业执行时间
preSelected = prev; // 保存选定作业的前一个作业
}
prev = q; // 更新前一个作业指针
}
// 执行选定的进程
printf("\n\n");
timer++;
printf("第%d次调度 ", timer); // 输出调度次数
printf("当前正在执行的作业:%s\n", selected->name); // 输出当前执行的作业名称
currentTime += selected->neededRunningTime; // 更新系统时间
printf("本轮调度完成,当前时间是%d\n", currentTime); // 输出当前时间
// 更新统计信息
waitTime = startTime - selected->arrivingTime; // 计算等待时间
ave_tat += currentTime - startTime + waitTime; // 更新平均周转时间
ave_w_tat += (currentTime - startTime + waitTime) / (float)selected->neededRunningTime; // 更新平均带权周转时间
startTime = currentTime; // 更新开始时间
// 从进程列表中移除选定的进程
if (selected != p) {
preSelected->next = selected->next; // 移除选定进程的前一个进程的链接
p = t; // 重置循环中的当前进程指针
} else {
p = t->next; // 移除首个进程时,更新进程列表头指针
}
// 显示更新后的进程列表
displayProcesses(p); // 显示更新后的进程列表
// 释放被执行的进程所占用的内存
free(selected); // 释放当前执行的进程的内存空间
}
// 计算并打印平均周转时间和平均带权周转时间
ave_tat /= timer; // 计算平均周转时间
ave_w_tat /= timer; // 计算平均带权周转时间
printf("平均周转时间=%2.3f 平均带权周转时间=%2.3f\n", ave_tat, ave_w_tat); // 打印平均周转时间和平均带权周转时间
return OK; // 返回执行成功状态
}
2.先来先服务调度算法
【算法思想】
FCFS(First Come, First Served)即先来先服务调度算法,其实现思想非常简单,按照进程到达的顺序进行调度。
①初始化: 初始化当前系统时间为零,设置等待时间、开始时间、计时器以及平均周转时间和平均带权周转时间为零。
②遍历进程链表: 从进程链表的头部开始,依次处理每个进程。
③选择下一个要执行的进程: 由于先来先服务算法按照到达顺序执行进程,因此选择链表中的第一个进程即可。
④执行进程:
- 更新系统时间:将当前系统时间增加上当前进程所需的执行时间。
- 计算等待时间:等待时间等于当前进程开始执行的时间减去该进程到达的时间。
- 更新平均周转时间和平均带权周转时间:分别累加上当前进程的周转时间和带权周转时间,这里周转时间是当前系统时间减去当前进程开始执行的时间加上等待时间,带权周转时间是周转时间除以当前进程所需的执行时间。
- 更新开始时间:将开始时间更新为当前系统时间,为下一个进程的执行做准备。
⑤释放当前进程节点: 释放当前进程节点的内存,以防止内存泄漏。
⑥显示更新后的进程链表状态: 在每次执行完一个进程后,显示更新后的进程链表状态,以便观察执行情况。
⑦重复步骤2~6,直到所有进程都执行完毕。
⑧计算平均周转时间和平均带权周转时间: 最后,计算平均周转时间和平均带权周转时间,分别是所有进程的周转时间之和除以进程数量,和所有进程的带权周转时间之和除以进程数量。
⑨返回执行成功状态: 返回执行成功状态以结束函数。
【算法实现】
//先来先服务调度算法
Status FCFSScheduling(PCB *pHead){
if(pHead==NULL) return ERROR;
PCB *p=pHead,*prev=NULL;
int currentTime = CURRENT_TIME; // 当前系统时间
int waitTime = 0; // 等待时间
int startTime = currentTime; // 进程开始执行的时间
int timer = 0; // 计时器,记录调度次数
float ave_tat = 0.0; // 平均周转时间
float ave_w_tat = 0.0; // 平均带权周转时间
printf(" ## 先来先服务算法 ## \n");
displayProcesses(p);
while(p!=NULL)
{
prev=p;
timer++;
printf("\n\n");
printf("第%d次调度 ", timer); // 输出调度次数
printf("当前正在执行的作业:%s\n", p->name); // 输出当前执行的作业名称
currentTime +=p->neededRunningTime; // 更新系统时间
printf("本轮调度完成,当前时间是 %d\n", currentTime); // 输出当前时间
// 更新统计信息
waitTime = startTime - p->arrivingTime; // 计算等待时间
ave_tat += currentTime - startTime + waitTime; // 更新平均周转时间
ave_w_tat += (currentTime - startTime + waitTime) / (float)p->neededRunningTime; // 更新平均带权周转时间
startTime = currentTime; // 更新开始时间
p=prev->next;
free(prev);
displayProcesses(p);
}
// 计算并打印平均周转时间和平均带权周转时间
ave_tat /= timer; // 计算平均周转时间
ave_w_tat /= timer; // 计算平均带权周转时间
printf("平均周转时间=%2.3f 平均带权周转时间=%2.3f\n", ave_tat, ave_w_tat); // 打印平均周转时间和平均带权周转时间
return OK; // 返回执行成功状态
}
3.最高优先级调度算法
【算法思想】
本算法是一种非抢占式的最高优先级调度算法,其核心思想是在每次调度时选择具有最高优先级的就绪进程进行执行,以确保优先级高的任务能够尽快得到执行。下面是该算法的具体步骤:
①准备就绪队列:进程根据到达时间加入到就绪队列中。
②选择最高优先级进程:遍历就绪队列,找到具有最高优先级的进程。优先级通常是一个整数值,数值越大表示优先级越高。
③执行选定进程:将选定的进程从就绪队列中移出,并将其执行,直到进程完成或发生阻塞事件。
④更新等待时间:记录选定进程的等待时间,即进程开始执行的时间减去到达时间。
⑤更新统计信息:累加平均周转时间和平均加权周转时间,以便后续计算。
⑥释放资源:执行完的进程释放其占用的资源。
⑦重复调度:重复上述步骤,直到所有进程执行完毕。
⑧计算平均周转时间和平均加权周转时间:在所有进程执行完毕后,计算平均周转时间和平均加权周转时间,这两个指标用于评估调度算法的性能。
在非抢占式最高优先级调度算法中,一旦一个进程开始执行,它会一直执行直到完成或者发生某种阻塞事件,如等待 I/O 完成。在这种情况下,系统不会中断正在执行的进程,即使有更高优先级的新进程加入。这种方式可能会导致低优先级进程长时间等待,但可以减少上下文切换的开销。
【算法实现】
// 最高优先级调度算法
Status HPFScheduling(PCB *pHead) {
// 如果进程列表为空,则返回错误代码
if (pHead == NULL) return ERROR;
// 初始化当前时间、等待时间、开始时间、计时器、平均周转时间和平均加权周转时间
int currentTime = CURRENT_TIME; // 当前时间
int waitTime = 0; // 等待时间
int startTime = currentTime; // 开始时间
int timer = 0; // 计时器
float ave_tat = 0.0; // 平均周转时间
float ave_w_tat = 0.0; // 平均加权周转时间
printf(" ## 最高优先级调度算法 ## \n");
// 打印即将调度的进程
printf("将要进行调度的进程如下:\n");
displayProcesses(pHead);
for (PCB *p = pHead; p != NULL;) {
int max_priority = INT_MIN; // 将最大优先级初始化为一个极小值
PCB *t = p;
PCB *selected = p, *preSelected = NULL, *prev = NULL;
// 找到准备执行的具有最高优先级的进程
for (PCB *q = p; q; q = q->next) {
if (q->arrivingTime <= currentTime && q->priority >= max_priority) {
selected = q;
max_priority = q->priority;
preSelected = prev;
}
prev = q;
}
timer++;
// 执行选定的进程
printf("\n\n");
printf("当前时间:%d\n", currentTime); // 当前时间
printf("第%d次调度\n", timer); // 计时器
printf("当前执行的进程:%s\n", selected->name); // 当前执行的进程名称
printf("%s 进程开始执行,时间:%d\n", selected->name, currentTime); // 进程开始执行的时间
waitTime = currentTime - selected->arrivingTime; // 等待时间
printf("%s 进程的等待时间:%d\n", selected->name, waitTime); // 进程的等待时间
currentTime += selected->neededRunningTime; // 当前时间增加执行时间
printf("%s 进程执行结束,时间:%d\n", selected->name, currentTime); // 进程执行结束时间
// 更新统计信息
ave_tat += currentTime - selected->arrivingTime; // 更新平均周转时间
ave_w_tat += (currentTime - selected->arrivingTime) / (float)selected->neededRunningTime; // 更新平均加权周转时间
// 从进程列表中删除选定的进程
if (selected != p) {
preSelected->next = selected->next; // 删除选定进程的链接
p = t; // 更新当前进程
} else {
p = t->next; // 更新当前进程
}
// 显示更新后的进程列表
printf("更新后的进程列表:\n");
displayProcesses(p); // 显示更新后的进程列表
// 释放执行过的进程所占用的内存
free(selected);
}
// 计算并打印平均周转时间和平均加权周转时间
ave_tat /= timer; // 计算平均周转时间
ave_w_tat /= timer; // 计算平均加权周转时间
printf("平均周转时间 = %2.3f 平均加权周转时间 = %2.3f\n", ave_tat, ave_w_tat);
return OK; // 返回执行成功代码
}
4.多级反馈队列调度算法
【算法思想】
(1)调度机制
①设置多个就绪队列。在系统中设置多个就绪队列,并为每个队列赋予不同的优先级。第一个队列的优先级最高,第二个次之,其余队列的优先级逐个降低。该算法为不同队列中的进程所赋予的执行时间片的大小也各不相同,在优先级愈高的队列中,其时间就愈小。例如第二个队列的时间片要比第一个的时间片长一倍,……,第i+1个队列的间片要比第i个的时间片长一倍。
②每个队列都采用FCFS算法。当新进程进入内存后,首先将它放入第一队列的末尾,按FCFS原则等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可撤离系统否则,即它在一个时间片结束时尚未完成,调度程序将其转入第二队列的末尾等待调度,如果它在第二队列中运行一个时间片后仍未完成,再依次将它放入第三队列,.,依此类推。当进程最后被降到第卫队列后,在第n队列中便采取按RR方式运行。
在RR 调度算法中,应在何时进行进程的切换,可分为两种情况:
- 若一个时间片尚未用完,正在运行的进程便已经完成,就立即激活调度程序,将它从就绪队列中删除,再调度就绪队列中队首的进程运行,并启动一个新的时间片。
- 在一个时间片用完时,计时器中断处理程序被激活。如果进程尚未运行完毕,调度度程序将把它送往就绪队列的末尾。
③按队列优先级调度。调度程序首先调度最高优先级队列中的诸进程运行,仅当第一队列空闲时才调度第二队列中的进程运行;换言之,仅当第1~(i-1)所有队列均空时,才会调度第i队列中的进程运行。如果处理机正在第i队列中为某进程服务时又有新进程进入任一优先级较高的队列,此时须立即把正在运行的进程放回到第i队列的末尾,而把处理机分配给新到的高优先级进程。
(2)调度算法的性能
在多级反馈队列调度算法中,如果规定第一个队列的时间片略大于多数人机交互所需之处理时间时,便能较好地满足各种类型用户的需要。
①终端型用户。由于终端型用户提交的作业多属于交互型作业,通常较小,系统只要能使这些作业在第一队列规定的时间片内完成,便可使终端型用户感到满意。
②短批处理作业用户。对于这类作业,如果可在第一队列中执行完成,便获得与终端型作业一样的响应时间。对于稍长的短作业,也只需在第二和第三队列各执行一时间完成,其周转时间仍然较短。
③长批处理作业用户。对于长作业,它将依次在第1,2,.n个队列中运行,然后再按轮转方式运行,用户不必担心其作业长期得不到处理。
(3)对本算法相关函数的说明
①insertProcess: 这个函数用于将进程插入到就绪队列的尾部。如果队列为空,则直接将进程作为队列的第一个元素;否则遍历队列找到队尾,并将进程插入到队尾。
②deleteProcess: 从队列中删除指定的进程。如果队列为空,则直接返回;如果要删除的进程是队列的第一个元素,则将队列头指针指向下一个元素;否则遍历队列找到要删除的进程并删除。
③executeProcess: 这个函数用于执行进程。根据给定的时间片大小,执行进程的CPU时间。如果进程需要的CPU时间小于一个时间片,则执行完进程剩余的CPU时间;否则执行当前时间片大小的CPU时间。执行完毕后,更新进程状态,并根据时间片大小将进程返回到相应的就绪队列或删除队列中。
④MLFQScheduling: 这个函数是多级反馈队列调度的主函数。它使用了三个优先级队列,根据优先级执行进程,直到所有队列都为空。在每次执行进程后,统计已完成的进程数量,并计算总周转时间和总带权周转时间。最后打印平均周转时间和带权周转时间。
【算法实现】
// 插入进程到队列尾部
void insertProcess(PCB** readyQueue, PCB* process) {
// 如果队列为空,则直接将进程作为队列的第一个元素
if (*(readyQueue) == NULL) {
*readyQueue = process;
} else {
// 否则遍历队列,找到队尾,并将进程插入到队尾
PCB* temp = *(readyQueue);
while (temp->next != NULL) {
temp = temp->next;
}
temp->next = process;
}
}
// 从队列中删除进程
void deleteProcess(PCB** readyQueue, PCB* process) {
// 如果队列为空,则直接返回
if (*readyQueue == NULL) {
return;
}
// 如果要删除的进程是队列的第一个元素
if (*readyQueue == process) {
*readyQueue = (*readyQueue)->next; // 将队列头指针指向下一个元素
process->next = NULL; // 将要删除的进程的下一个指针置空
return;
}
// 否则遍历队列,找到要删除的进程并删除
PCB* temp = *readyQueue;
while (temp->next != NULL && temp->next != process) {
temp = temp->next;
}
if (temp->next == process) {
temp->next = process->next;
process->next = NULL;
}
}
// 执行进程
void executeProcess(PCB** readyQueue, PCB* process, int timeSlice, float *at, float *awt, int *timer) {
static int currentTime = CURRENT_TIME; // 维护当前时间
static int count = 0; // 统计已完成的进程数量
printf("\n当前时间:%d,", currentTime);
printf("执行进程:%s\n", process->name);
strcpy(process->currentState, "Run"); // 更新进程状态为运行态
// 根据时间片大小执行进程
if (timeSlice == 3) {
// 如果进程需要的 CPU 时间小于一个时间片,则执行完进程剩余的 CPU 时间
if (process->neededRunningTime - process->usedCpuTime < timeSlice) {
currentTime += process->neededRunningTime - process->usedCpuTime;
process->usedCpuTime += process->neededRunningTime - process->usedCpuTime;
} else {
// 否则执行当前时间片大小的 CPU 时间
process->usedCpuTime += timeSlice;
currentTime += timeSlice;
}
} else {
// 执行当前时间片大小的 CPU 时间
process->usedCpuTime += timeSlice;
currentTime += timeSlice;
}
// 打印当前队列中的进程情况
switch (timeSlice) {
case 1: printf("ReadyQueue1: "); displayProcesses(readyQueue[0]); break;
case 2: printf("ReadyQueue2: "); displayProcesses(readyQueue[1]); break;
case 3: printf("ReadyQueue3:"); displayProcesses(readyQueue[2]); break;
}
// 判断进程是否执行完毕
if (process->usedCpuTime >= process->neededRunningTime) {
printf("%s 在 %d 时刻执行完毕\n", process->name, currentTime);
*timer = ++count; // 更新已完成进程数量
*at += currentTime - process->arrivingTime; // 更新总周转时间
*awt += (currentTime - process->arrivingTime) / (float)process->neededRunningTime; // 更新总带权周转时间
strcpy(process->currentState, "Finish"); // 更新进程状态为完成态
// 根据时间片大小删除对应队列中的进程
switch (timeSlice) {
case 1: deleteProcess(readyQueue, process); break;
case 2: deleteProcess(readyQueue + 1, process); break;
case 3: deleteProcess(readyQueue + 2, process); break;
}
free(process); // 释放进程内存
} else {
// 如果进程未执行完毕,则将其返回到相应的就绪队列
printf("%s 时间片用完,返回就绪队列\n", process->name);
strcpy(process->currentState, "Ready"); // 更新进程状态为就绪态
// 根据时间片大小选择插入到相应队列的尾部
if (timeSlice == 1) {
deleteProcess(readyQueue, process);
insertProcess(&readyQueue[1], process);
} else if (timeSlice == 2) {
deleteProcess(readyQueue + 1, process);
insertProcess(&readyQueue[2], process);
} else if (timeSlice == 3) {
deleteProcess(readyQueue + 2, process);
insertProcess(&readyQueue[2], process);
}
}
}
// 多级反馈队列调度
Status MLFQScheduling(PCB *readyQueue[], int timeSlice[]) {
// 如果最高优先级队列为空,则直接返回错误
if (readyQueue[0] == NULL) return ERROR;
int currentTime = CURRENT_TIME; // 初始化当前时间
int timer = 0; // 统计已完成的进程数量
float ave_tat = 0.0; // 平均周转时间
float ave_w_tat = 0.0; // 平均带权周转时间
int *point_timer = &timer; // 指向计时器
float *at = &ave_tat; // 指向总周转时间
float *awt = &ave_w_tat; // 指向总带权周转时间
// 循环执行进程,直到所有队列都为空
while (readyQueue[0] || readyQueue[1] || readyQueue[2]) {
if (readyQueue[0]) {
executeProcess(readyQueue, readyQueue[0], timeSlice[0], at, awt, point_timer); // 执行最高优先级队列中的进程
} else if (readyQueue[1]) {
executeProcess(readyQueue, readyQueue[1], timeSlice[1], at, awt, point_timer); // 执行第二优先级队列中的进程
} else if (readyQueue[2]) {
executeProcess(readyQueue, readyQueue[2], timeSlice[2], at, awt, point_timer); // 执行第三优先级队列中的进程
}
}
printf("\n");
// 计算和打印平均周转时间和带权周转时间
*at /= *point_timer;
*awt /= *point_timer;
printf("平均周转时间 = %2.3f 平均带权周转时间 = %2.3f\n", *at, *awt);
return 0;
}
实验数据、分析与实验结果
测试数据:
系统有5个进程,其就绪时刻、服务时间和优先级(优先级数值越大优先级越高)如下图所示。
(注:在多级反馈队列调度算法中设3个就绪队列,时间片分别为1、2、3。)
实验结果一: 短作业优先算法
实验结果二:先来先服务算法
实验结果三:最高优先级调度算法
续 最高优先级调度算法
实验结果四:多级反馈队列调度算法
实验分析:以上是实验要求模拟的所有调度算法的执行结果。经过手绘演算,算法的效果达到了要求。
完整代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define ERROR -1
#define OK 1
#define Status int
#define INT_MAX 998
#define INT_MIN -1
#define CURRENT_TIME 0
typedef struct pcb{
char name[5];
int priority;//优先级
int arrivingTime;//到达时间。就绪时刻
int neededRunningTime;//需要运行时间
int usedCpuTime;//已运行时间
char currentState[10];//当前状态
// int turnAroundTime;
// int weightTurnAroundTime;
struct pcb *next;
} PCB;
// 周转时间=结束时间-开始时间+等待时间
PCB* New_Process(char name[], int priority, int arrivingTime, int neededRunningTime,int usedCpuTime) {
PCB* process = (PCB*)malloc(sizeof(PCB));
strcpy(process->name, name);
process->priority = priority;
process->arrivingTime = arrivingTime;
process->neededRunningTime = neededRunningTime;
process->usedCpuTime=usedCpuTime;
strcpy(process->currentState, "Ready");
process->next = NULL;
return process;
}
// 显示进程队列信息
Status displayProcesses(PCB *pHead){
if(pHead==NULL){
printf("当前进程队列为空\n");
return ERROR;
}
PCB *p=pHead;
for(;p!=NULL;p=p->next){
printf("%s ",p->name);
}
printf("\nPCB State:\n");
printf(" 进程 优先级 到达时间 所需执行时间 已使用CPU时间 当前状态\n");
for(p=pHead;p!=NULL;p=p->next) {
printf(" %s %2d %2d %2d %2d %s\n",
p->name, p->priority, p->arrivingTime, p->neededRunningTime, p->usedCpuTime, p->currentState);
}
printf("\n");
}
//短作业优先调度算法
Status SJFScheduling(PCB *pHead) {
// 如果进程列表为空,则返回错误码
if (pHead == NULL) return ERROR;
// 初始化当前时间、等待时间、开始时间、计时器以及平均周转时间和平均带权周转时间
int currentTime = CURRENT_TIME; // 当前系统时间
int waitTime = 0; // 等待时间
int startTime = currentTime; // 进程开始执行的时间
int timer = 0; // 计时器,记录调度次数
float ave_tat = 0.0; // 平均周转时间
float ave_w_tat = 0.0; // 平均带权周转时间
// 打印即将被调度的进程列表
printf(" ## 短作业优先算法 ## \n");
displayProcesses(pHead); // 显示进程列表
// 开始调度循环
for (PCB *p = pHead; p != NULL;) {
int min_remaining_time=INT_MAX; // 初始化最短执行时间为一个较大的值
PCB *t = p; // 保存当前进程指针,以便释放内存
PCB *selected = p, *preSelected = NULL, *prev = NULL;
// 寻找可执行的最短作业
for (PCB *q = p; q; q = q->next) {
if (q->arrivingTime <= currentTime && q->neededRunningTime <= min_remaining_time) {
selected = q; // 选定当前最短作业
min_remaining_time = q->neededRunningTime; // 更新最短作业执行时间
preSelected = prev; // 保存选定作业的前一个作业
}
prev = q; // 更新前一个作业指针
}
// 执行选定的进程
printf("\n\n");
timer++;
printf("第%d次调度 ", timer); // 输出调度次数
printf("当前正在执行的作业:%s\n", selected->name); // 输出当前执行的作业名称
currentTime += selected->neededRunningTime; // 更新系统时间
printf("本轮调度完成,当前时间是%d\n", currentTime); // 输出当前时间
// 更新统计信息
waitTime = startTime - selected->arrivingTime; // 计算等待时间
ave_tat += currentTime - startTime + waitTime; // 更新平均周转时间
ave_w_tat += (currentTime - startTime + waitTime) / (float)selected->neededRunningTime; // 更新平均带权周转时间
startTime = currentTime; // 更新开始时间
// 从进程列表中移除选定的进程
if (selected != p) {
preSelected->next = selected->next; // 移除选定进程的前一个进程的链接
p = t; // 重置循环中的当前进程指针
} else {
p = t->next; // 移除首个进程时,更新进程列表头指针
}
// 显示更新后的进程列表
displayProcesses(p); // 显示更新后的进程列表
// 释放被执行的进程所占用的内存
free(selected); // 释放当前执行的进程的内存空间
}
// 计算并打印平均周转时间和平均带权周转时间
ave_tat /= timer; // 计算平均周转时间
ave_w_tat /= timer; // 计算平均带权周转时间
printf("平均周转时间=%2.3f 平均带权周转时间=%2.3f\n", ave_tat, ave_w_tat); // 打印平均周转时间和平均带权周转时间
return OK; // 返回执行成功状态
}
//先来先服务调度算法
Status FCFSScheduling(PCB *pHead){
if(pHead==NULL) return ERROR;
PCB *p=pHead,*prev=NULL;
int currentTime = CURRENT_TIME; // 当前系统时间
int waitTime = 0; // 等待时间
int startTime = currentTime; // 进程开始执行的时间
int timer = 0; // 计时器,记录调度次数
float ave_tat = 0.0; // 平均周转时间
float ave_w_tat = 0.0; // 平均带权周转时间
printf(" ## 先来先服务算法 ## \n");
displayProcesses(p);
while(p!=NULL)
{
prev=p;
timer++;
printf("\n\n");
printf("第%d次调度 ", timer); // 输出调度次数
printf("当前正在执行的作业:%s\n", p->name); // 输出当前执行的作业名称
currentTime +=p->neededRunningTime; // 更新系统时间
printf("本轮调度完成,当前时间是 %d\n", currentTime); // 输出当前时间
// 更新统计信息
waitTime = startTime - p->arrivingTime; // 计算等待时间
ave_tat += currentTime - startTime + waitTime; // 更新平均周转时间
ave_w_tat += (currentTime - startTime + waitTime) / (float)p->neededRunningTime; // 更新平均带权周转时间
startTime = currentTime; // 更新开始时间
p=prev->next;
free(prev);
displayProcesses(p);
}
// 计算并打印平均周转时间和平均带权周转时间
ave_tat /= timer; // 计算平均周转时间
ave_w_tat /= timer; // 计算平均带权周转时间
printf("平均周转时间=%2.3f 平均带权周转时间=%2.3f\n", ave_tat, ave_w_tat); // 打印平均周转时间和平均带权周转时间
return OK; // 返回执行成功状态
}
// 高响应比优先(HRRN)调度算法
Status HRRNScheduling(PCB *pHead) {
// 如果进程列表为空,则返回错误码
if (pHead == NULL) return ERROR;
// 初始化变量
int currentTime = CURRENT_TIME;
int timer = 0;
float ave_tat = 0.0;
float ave_w_tat = 0.0;
printf(" ## 高响应比优先调度算法 ## \n");
// 打印即将被调度的进程列表
printf("即将被调度的进程如下:\n");
displayProcesses(pHead);
// 开始调度循环
for (PCB *p = pHead; p != NULL;) {
float max_response_ratio = 0.0;
PCB *selected = p, *prev = NULL;
// 寻找响应比最高的进程
for (PCB *q = p; q; q = q->next) {
float response_ratio = (float)(currentTime - q->arrivingTime + q->neededRunningTime) / q->neededRunningTime;
if (response_ratio > max_response_ratio) {
max_response_ratio = response_ratio;
selected = q;
}
}
// 执行选定的进程
printf("\n\n");
timer++;
printf("第%d次调度 ", timer);
printf("当前正在执行的作业:%s\n", selected->name);
printf("当前时间:%d\n", currentTime);
currentTime += selected->neededRunningTime;
// 更新统计信息
ave_tat += currentTime - selected->arrivingTime;
ave_w_tat += (currentTime - selected->arrivingTime) / (float)selected->neededRunningTime;
// 从进程列表中移除选定的进程
if (selected != p) {
for (PCB *q = p; q != selected; q = q->next) {
prev = q;
}
prev->next = selected->next;
} else {
p = p->next;
}
// 显示更新后的进程列表
displayProcesses(p);
// 释放被执行的进程所占用的内存
free(selected);
}
// 计算并打印平均周转时间和平均带权周转时间
ave_tat /= timer;
ave_w_tat /= timer;
printf("平均周转时间=%2.3f 平均带权周转时间=%2.3f\n", ave_tat, ave_w_tat);
return OK;
}
// 最高优先级调度算法
Status HPFScheduling(PCB *pHead) {
// 如果进程列表为空,则返回错误代码
if (pHead == NULL) return ERROR;
// 初始化当前时间、等待时间、开始时间、计时器、平均周转时间和平均加权周转时间
int currentTime = CURRENT_TIME; // 当前时间
int waitTime = 0; // 等待时间
int startTime = currentTime; // 开始时间
int timer = 0; // 计时器
float ave_tat = 0.0; // 平均周转时间
float ave_w_tat = 0.0; // 平均加权周转时间
printf(" ## 最高优先级调度算法 ## \n");
// 打印即将调度的进程
printf("将要进行调度的进程如下:\n");
displayProcesses(pHead);
for (PCB *p = pHead; p != NULL;) {
int max_priority = INT_MIN; // 将最大优先级初始化为一个极小值
PCB *t = p;
PCB *selected = p, *preSelected = NULL, *prev = NULL;
// 找到准备执行的具有最高优先级的进程
for (PCB *q = p; q; q = q->next) {
if (q->arrivingTime <= currentTime && q->priority >= max_priority) {
selected = q;
max_priority = q->priority;
preSelected = prev;
}
prev = q;
}
timer++;
// 执行选定的进程
printf("\n\n");
printf("当前时间:%d\n", currentTime); // 当前时间
printf("第%d次调度\n", timer); // 计时器
printf("当前执行的进程:%s\n", selected->name); // 当前执行的进程名称
printf("%s 进程开始执行,时间:%d\n", selected->name, currentTime); // 进程开始执行的时间
waitTime = currentTime - selected->arrivingTime; // 等待时间
printf("%s 进程的等待时间:%d\n", selected->name, waitTime); // 进程的等待时间
currentTime += selected->neededRunningTime; // 当前时间增加执行时间
printf("%s 进程执行结束,时间:%d\n", selected->name, currentTime); // 进程执行结束时间
// 更新统计信息
ave_tat += currentTime - selected->arrivingTime; // 更新平均周转时间
ave_w_tat += (currentTime - selected->arrivingTime) / (float)selected->neededRunningTime; // 更新平均加权周转时间
// 从进程列表中删除选定的进程
if (selected != p) {
preSelected->next = selected->next; // 删除选定进程的链接
p = t; // 更新当前进程
} else {
p = t->next; // 更新当前进程
}
// 显示更新后的进程列表
printf("更新后的进程列表:\n");
displayProcesses(p); // 显示更新后的进程列表
// 释放执行过的进程所占用的内存
free(selected);
}
// 计算并打印平均周转时间和平均加权周转时间
ave_tat /= timer; // 计算平均周转时间
ave_w_tat /= timer; // 计算平均加权周转时间
printf("平均周转时间 = %2.3f 平均加权周转时间 = %2.3f\n", ave_tat, ave_w_tat);
return OK; // 返回执行成功代码
}
// 插入进程到队列尾部
void insertProcess(PCB** readyQueue, PCB* process) {
// 如果队列为空,则直接将进程作为队列的第一个元素
if (*(readyQueue) == NULL) {
*readyQueue = process;
} else {
// 否则遍历队列,找到队尾,并将进程插入到队尾
PCB* temp = *(readyQueue);
while (temp->next != NULL) {
temp = temp->next;
}
temp->next = process;
}
}
// 从队列中删除进程
void deleteProcess(PCB** readyQueue, PCB* process) {
// 如果队列为空,则直接返回
if (*readyQueue == NULL) {
return;
}
// 如果要删除的进程是队列的第一个元素
if (*readyQueue == process) {
*readyQueue = (*readyQueue)->next; // 将队列头指针指向下一个元素
process->next = NULL; // 将要删除的进程的下一个指针置空
return;
}
// 否则遍历队列,找到要删除的进程并删除
PCB* temp = *readyQueue;
while (temp->next != NULL && temp->next != process) {
temp = temp->next;
}
if (temp->next == process) {
temp->next = process->next;
process->next = NULL;
}
}
// 执行进程
void executeProcess(PCB** readyQueue, PCB* process, int timeSlice, float *at, float *awt, int *timer) {
static int currentTime = CURRENT_TIME; // 维护当前时间
static int count = 0; // 统计已完成的进程数量
printf("\n当前时间:%d,", currentTime);
printf("执行进程:%s\n", process->name);
strcpy(process->currentState, "Run"); // 更新进程状态为运行态
// 根据时间片大小执行进程
if (timeSlice == 3) {
// 如果进程需要的 CPU 时间小于一个时间片,则执行完进程剩余的 CPU 时间
if (process->neededRunningTime - process->usedCpuTime < timeSlice) {
currentTime += process->neededRunningTime - process->usedCpuTime;
process->usedCpuTime += process->neededRunningTime - process->usedCpuTime;
} else {
// 否则执行当前时间片大小的 CPU 时间
process->usedCpuTime += timeSlice;
currentTime += timeSlice;
}
} else {
// 执行当前时间片大小的 CPU 时间
process->usedCpuTime += timeSlice;
currentTime += timeSlice;
}
// 打印当前队列中的进程情况
switch (timeSlice) {
case 1: printf("ReadyQueue1: "); displayProcesses(readyQueue[0]); break;
case 2: printf("ReadyQueue2: "); displayProcesses(readyQueue[1]); break;
case 3: printf("ReadyQueue3:"); displayProcesses(readyQueue[2]); break;
}
// 判断进程是否执行完毕
if (process->usedCpuTime >= process->neededRunningTime) {
printf("%s 在 %d 时刻执行完毕\n", process->name, currentTime);
*timer = ++count; // 更新已完成进程数量
*at += currentTime - process->arrivingTime; // 更新总周转时间
*awt += (currentTime - process->arrivingTime) / (float)process->neededRunningTime; // 更新总带权周转时间
strcpy(process->currentState, "Finish"); // 更新进程状态为完成态
// 根据时间片大小删除对应队列中的进程
switch (timeSlice) {
case 1: deleteProcess(readyQueue, process); break;
case 2: deleteProcess(readyQueue + 1, process); break;
case 3: deleteProcess(readyQueue + 2, process); break;
}
free(process); // 释放进程内存
} else {
// 如果进程未执行完毕,则将其返回到相应的就绪队列
printf("%s 时间片用完,返回就绪队列\n", process->name);
strcpy(process->currentState, "Ready"); // 更新进程状态为就绪态
// 根据时间片大小选择插入到相应队列的尾部
if (timeSlice == 1) {
deleteProcess(readyQueue, process);
insertProcess(&readyQueue[1], process);
} else if (timeSlice == 2) {
deleteProcess(readyQueue + 1, process);
insertProcess(&readyQueue[2], process);
} else if (timeSlice == 3) {
deleteProcess(readyQueue + 2, process);
insertProcess(&readyQueue[2], process);
}
}
}
// 多级反馈队列调度
Status MLFQScheduling(PCB *readyQueue[], int timeSlice[]) {
// 如果最高优先级队列为空,则直接返回错误
if (readyQueue[0] == NULL) return ERROR;
int currentTime = CURRENT_TIME; // 初始化当前时间
int timer = 0; // 统计已完成的进程数量
float ave_tat = 0.0; // 平均周转时间
float ave_w_tat = 0.0; // 平均带权周转时间
int *point_timer = &timer; // 指向计时器
float *at = &ave_tat; // 指向总周转时间
float *awt = &ave_w_tat; // 指向总带权周转时间
// 循环执行进程,直到所有队列都为空
while (readyQueue[0] || readyQueue[1] || readyQueue[2]) {
if (readyQueue[0]) {
executeProcess(readyQueue, readyQueue[0], timeSlice[0], at, awt, point_timer); // 执行最高优先级队列中的进程
} else if (readyQueue[1]) {
executeProcess(readyQueue, readyQueue[1], timeSlice[1], at, awt, point_timer); // 执行第二优先级队列中的进程
} else if (readyQueue[2]) {
executeProcess(readyQueue, readyQueue[2], timeSlice[2], at, awt, point_timer); // 执行第三优先级队列中的进程
}
}
printf("\n");
// 计算和打印平均周转时间和带权周转时间
*at /= *point_timer;
*awt /= *point_timer;
printf("平均周转时间 = %2.3f 平均带权周转时间 = %2.3f\n", *at, *awt);
return 0;
}
int main(){
PCB *p1=New_Process("P1",2,0,3,1);
PCB *p2=New_Process("P2",3,2,6,2);
PCB *p3=New_Process("P3",2,4,4,0);
PCB *p4=New_Process("P4",4,6,5,3);
PCB *p5=New_Process("P5",5,8,2,1);
p1->next=p2;
p2->next=p3;
p3->next=p4;
p4->next=p5;
p5->next=NULL;
// SJFScheduling(p1);
// printf("\n\n");
// FCFSScheduling(p1);
// printf("\n\n");
// HRRNScheduling(p1);
// printf("\n\n");
// HPFScheduling(p1);
p2->next=NULL;
p4->next=NULL;
int timeSlice[3]={1,2,3};
PCB *readyQueue[3]={p1,p3,p5};
printf("初始进程队列状态\n");
printf("ReadyQueue1: ");
displayProcesses(readyQueue[0]);
printf("ReadyQueue2: ");
displayProcesses(readyQueue[1]);
printf("ReadyQueue3: ");
displayProcesses(readyQueue[2]);
printf("\n开始多级反馈队列调度...\n");
MLFQScheduling(readyQueue, timeSlice);
printf("\n多级反馈队列调度结束。\n");
}