进程调度(第7、8、9、10章)
工作负载假设
- 每个工作运行相同的时间
- 每个工作同时到达
- 一旦开始, 每个工作保持运行保持运行
- 每个工作只是使用CPU(不执行IO操作)
- 每个工作运行的时间是已知的
调度指标
T(周转时间)= T(完成时间)- T(到达时间)
先进先出(FIFO)
如果消耗时间较小的任务排在消耗时间较长的任务之后, 平均周转时间会很大
A:100s B:10s C:10s
周转时间 = (100 + 110 + 120)/ 3 = 110
最短任务优先(SJF)
考虑平均周转时间, SJF调度策略更好
A:100s B:10s C:10s
周转时间 = (10 + 20 + 120)/ 3 = 50
但是如果假设任务随机到达, B 和 C 在 A 开始后不就就到达了, 平均周转时间依然很大
最短完成时间优先(STCF)
假设任务随机到达
向SJF添加抢占, 每次来新任务, 都比较剩余任务和新任务的完成时间, 调度剩余时间最小的任务
平均周转时间大大提高了
新度量指标:响应时间
T(响应时间)= T(首次运行)- T(到达时间)
STCF响应时间是很长的, 交互性很糟糕
轮转(Round-Robin,RR)
每个任务运行一个时间片, 然后切换到下一个任务, 时间片的长度是时钟中断的倍数。
A:5s B:5s C:5s 时间片:1s
响应时间 = (0 + 1 + 2)/ 3 = 1
时间片长度对RR的影响很关键, 越短, RR在响应时间上的表现越好
但是时间片太短, 经常切换上下文影响性能, 需要权衡
RR的周转时间非常大
周转时间 :A 13, B 14, C 15
多级反馈队列
优化周转时间, 降低响应时间
MFLQ
MFLQ中有许多独立的队列, 每个队列有不同的优先级
任意时刻, 一个工作只能存在在一个队列中, MLFQ总是优先执行优先级较高的工作
如何设置优先级?
规则
- 如果A的优先级 > B的优先级, 运行A(不运行B)
- 如果A的优先级 = B的优先级, 轮转运行A和B(RR)
- 工作进入系统时, 放入最高优先级
- 用完整个时间片后, 降低一个优先级
- 如果在时间片内主动放弃了CPU, 优先级不变
- 工作在每个优先级的时间是有限的, 如果时间用完了, 无论如何都会降级
不知道工作时间的长短, 先假设是短工作, 然后再不断降低优先级, 确认是否为长工作, 使得MFLQ近似SJF
如果系统中有大量的交互工作(主动放弃CPU的工作), 会导致长工作无法获取CPU
7. 经过一段时间s, 将系统中所有工作重新加入最高优先级队列
时间s如何确定?如果设置过高, 长工作会饥饿; 如果设置过低, 交互型时间得不到合适的CPU时间比例
配置多少队列?每一层队列的时间片多长?这些问题没有显而易见的答案... ...
大多数MLFQ高优先级队列通常只有较短的时间片, 低优先级时间片更长
采用MFLQ的操作系统:类BSD UNIX系统、Solaris、WindowsNT、Windows系列
比例份额调度
确保每个工作获得一定比例的CPU时间, 而不是优化周转时间和响应时间。
进程A:25, 进程B:75, 代表每次, A有25%的可能占用CPU, B有75%的可能占用CPU
运行时间越长, 得到的CPU时间比例就越接近期望值
- 兑换机制:利用货币的概念, 允许拥有一组票的用户将票按照比例分给不同的工作
- 转让机制:一个进程可以将自己的票临时交给另一个进程
- 通胀机制:进程可以临时提升自己的票数(只适用于进程间相互信任的环境)
实现
1. 随机数生成器
2. 记录系统中所有进程的数据结构
3. 所有的票数
系统的运行严重依赖于票分配, 票的分配依然没有最佳答案。
不确定性
虽然随机方式实现简单, 但偶尔并不能产生正确的比例, 尤其是在工作运行时间短的情况下。
Waldspurger提出了步长调度:一个确定性的公平分配算法。
票数:A 100 B 50 C 250
步长:A 100 B 200 C 40 (例如:用10000分别除以票数)
行程值:每次程序运行, 都会让计数器增加一个步长, 行程值记录总的进展
当需要进行调度时, 选择目前拥有最小行程值的进程, 并将该进程的行程值增加一个步长
优缺点对比:
如果有一个新加入的进程, 步长调度如何设置行程值?如果设置成0, 就会独占CPU, 需要记录全局状态, 加入新的进程清零所有进程的行程值
而随机方案不需要, 只需要用新进程的票数更新全局票数即可
多处理器调度
多处理器有多个缓存, 存在缓存一致性问题(原子性问题), 如果CPU发现它缓存中的数据更新了, 会将数据从缓存中移除, 或者更新它。
跨CPU访问共享数据或数据结构时, 需要使用互斥原语(锁), 存在性能方面的问题, 随着CPU的增加, 访问同步共享数据结构会变慢。
缓存亲和度
一个进程在某个CPU上运行时, 会在该CPU缓存上维护许多状态, 下次在相同CPU上运行时, 将会更快。
如果在不同的CPU上运行, 由于需要重新加载数据, 反而执行更慢。
单队列调度(SQMS)
将所有需要调度的工作放入一个队列中, 简单地复用单处理器调度的基本架构。
优点:不需要太多修改, 可以将原有的策略用于多CPU, 选择合适的工作运行
缺点:
1. 缺乏可扩展性, 为了保证原子性, 需要加锁, 可能带来巨大的性能损失。
2. 缓存亲和性, 没有利用缓存亲和性, 为了解决这个问题, 大多数SQMS调度引入了一些亲和度机制, 尽可能让进程在同一个CPU上运行。
多队列调度(MQMS)
每个CPU都有自己的调度队列, 操作系统需要决定每个工作放入哪个队列。
1. 有更强的可扩展性, 队列的数量会随着CPU的数量增加
2. 具有良好的缓存亲和性
3. 负载不均:迁移(工作窃取)
Linux多处理器调度
O(1)调度程序:多队列, 基于优先级(类似MFLQ), 交互性得到了关注
完全公平调度程序(CFS):确定的比例调度算法(类似步长调度)
BF调度程序(BFS):单队列, 最早最合适虚拟截止时间算法(EEVEF)

浙公网安备 33010602011771号