进程调度(第7、8、9、10章)

工作负载假设

  1. 每个工作运行相同的时间
  2. 每个工作同时到达
  3. 一旦开始, 每个工作保持运行保持运行
  4. 每个工作只是使用CPU(不执行IO操作)
  5. 每个工作运行的时间是已知的

调度指标

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总是优先执行优先级较高的工作

如何设置优先级?

规则

  1. 如果A的优先级 > B的优先级, 运行A(不运行B)
  2. 如果A的优先级 = B的优先级, 轮转运行A和B(RR)
  3. 工作进入系统时, 放入最高优先级
  4. 用完整个时间片后, 降低一个优先级
  5. 如果在时间片内主动放弃了CPU, 优先级不变
  6. 工作在每个优先级的时间是有限的, 如果时间用完了, 无论如何都会降级

不知道工作时间的长短, 先假设是短工作, 然后再不断降低优先级, 确认是否为长工作, 使得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. 通胀机制:进程可以临时提升自己的票数(只适用于进程间相互信任的环境)

实现

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)

 

posted @ 2020-08-25 18:49  x_Aaron  阅读(353)  评论(0)    收藏  举报