计算机操作系统 - 多级反馈队列比例份额
计算机操作系统
- 机制和策略
- 多级反馈队列
- 概述
- 基本规则
- 比例份额
- 彩票调度
- 示例
- 代码分析
- 输出结果
- 彩票机制
- 彩票货币
- 示例
- 总结
- 其他机制
- 步长调度
- 示例
- 输出结果
- 对比测试
- 应用场景
- 往期推荐
大家好呀!我是小笙,本章我主要分享计算机操作系统 - 多级反馈队列&比例份额学习总结,希望内容对你有所帮助!!
机制和策略
上述提到的进程调度策略,更多是从理论/理想的角度去思考方式,但是实际往往要复杂的多,以下几个调度方式更多的是结合多个调度策略理论以及实际来展开的叙述
多级反馈队列
概述
多级反馈队列(MLFQ)是一种用于优化调度的著名方法,首次由Corbato于1962年提出,应用于CTSS系统
MLFQ旨在解决两个主要问题
- 优化周转时间:通过优先执行短作业,类似于SJF算法。然而,操作系统通常不知晓每个作业的运行时间,因此不能直接应用SJF
- 改善交互用户体验:通过减少响应时间,尤其适合交互式任务。虽然轮转算法能够改善响应时间,但会牺牲周转时间
结构
MLFQ 中有许多独立的队列(queue),每个队列有不同的优先级(priority level)。任何时刻,一个工作只能存在于一个队列中。MLFQ 总是优先执行较高优先级的工作(即在较高级队列中的工作)

基本规则
多级反馈队列的基本规则里面融合了先进先出、最短任务时间优先以及轮转等调度策略的思想
首先,多级反馈队列的核心逻辑就是高优先级优先执行策略以及相同优先级以轮转的公平方式运行,所以提出规则 1、2

- 规则 1:如果A的优先级 > C的优先级,运行 A(不运行C)
- 规则 2:如果A的优先级 = B的优先级,轮转运行高优先级 A 和 B
但是上述存在一个问题就是,高优先级如果能够永远抢占运行,是否会导致饥饿?如何通过调整优先级的方式来解决该问题
默认工作到达系统的时候放在最高优先级,然后通过运行完一个时间片来判断是否需要降低优先级
- 规则 3:工作进入系统时,放在最高优先级(最上层队列)
- 规则 4a:工作用完整个时间片后,降低其优先级(移入下一个队列)
- 规则 4b:如果工作在其时间片以内主动释放 CPU,则优先级不变
在上述规则下,长任务会随着时间片的运行,优先级逐级降低(动态分析出长短任务,不需要提前预测任务的执行时长,更合理)

这里存在一个问题,如果主动释放 CPU 就可以保证其优先级,那是否可以设计出对应的程序主动在时间片内都释放其 CPU 呢,规则 4c 是对规则 4a、4b 的优化调整
- 规则 4c:一旦工作用完了其在某一层中的时间配额(无论中间主动放弃了多少次CPU),就降低其优先级(移入低一级队列)
图中直观的对比了规则4c 是否采取的区别

如果长任务长时间处于低优先级,就会导致出现“饥饿”的问题,所以提出规则 5
- 规则 5:经过一段时间 S,就将系统中所有工作重新加入最高优先级队列
图中直观的对比了规则 5 是否采取的区别(假设 S 为 50)

关于该调度算法还有一些问题。其中一个大问题是如何配置一个调度程序
- 配置多少队列?
- 每一层队列的时间片配置多大?
- 为了避免饥饿问题以及进程行为改变,应该多久提升一次进程的优先级?
这些问题都没有显而易见的答案,因此只有利用对工作负载的经验,以及后续对调度程序的调优,才会导致令人满意的平衡
比例份额
比例份额算法的最终目标,是确保每个工作获得一定比例的CPU时间,而不是优化周转时间和响应时间
本章主要介绍比例份额调度的两种实现方式:彩票调度和步长调度
彩票调度
核心思想是通过概率随机性实现动态比例资源分配,将资源管理抽象为"彩票数量管理",以彩票数量反映进程对 CPU 时间的占有权重
示例
假设彩票数量一共为 100 张有,两个进程 A 和 B,A 拥有 35 张彩票,B 拥有 65 张彩票
彩票调度过程
- 彩票分配:调度程序知道总共的彩票数是 100 张
- 进程 A 拥有的彩票编号是 0 到 34(共 35 张)
- 进程 B 拥有的彩票编号是 35 到 99(共 65 张)
- 抽取彩票:调度程序从 0 到 99 之间的一个数字中随机抽取
- 如果抽取的数字在 0 到 34 之间,表示进程 A 中标,进程 A 将运行
- 如果抽取的数字在 35 到 99 之间,表示进程 B 中标,进程 B 将运行
- 概率分配:通过反复执行调度,彩票调度机制确保进程 A 和 B 大约会按照 75% 和 25% 的比例占用 CPU 时间
代码分析
-
定义进程结构体
#include <stdio.h> #include <stdlib.h> #include <time.h>// 定义进程结构体 typedef struct {char name[2]; // 进程名:'A' 或 'B'int lottery_count; // 该进程持有的彩票数int range_start; // 彩票编号的开始范围int range_end; // 彩票编号的结束范围 } Process;
-
模拟彩票调度的函数,随机抽取一张彩票并执行
// 函数:模拟彩票调度 void lottery_scheduling(Process *A, Process *B) {// 总彩票数int total_lottery = A->lottery_count + B->lottery_count;// 随机抽取一个数字(从 0 到 total_lottery-1 之间的数字)int draw = rand() % total_lottery;printf("抽取的数字是: %d\n", draw);// 判断是哪个进程中奖if (draw >= A -> range_start && draw <= A -> range_end) {printf("进程 %s 被选中运行\n", A->name);} else {printf("进程 %s 被选中运行\n", B->name);} }
-
主函数用于初始化进程以及调用彩票调度算法
int main() {// 初始化随机种子srand(time(NULL));// 创建两个进程Process A = {"A", 35, 0, 34}; // 进程 A 拥有 35 张彩票,彩票编号范围 0 到 34Process B = {"B", 65, 35, 99}; // 进程 B 拥有 65 张彩票,彩票编号范围 35 到 99// 进行 10 次调度for (int i = 0; i < 10; i++) {printf("\n第 %d 次抽取:\n", i + 1);lottery_scheduling(&A, &B); // 调用彩票调度函数}return 0; }
</