🐧 第四章 进程调度器
📄 一句话概括

时间片只改一个数,两个指标就往反方向走:时间片 2 时平均周转最好,时间片 32 时切换最少——它们不是同一个最优。总耗时几乎不变,因为活儿的总量决定了它。

📝 本节和前面几节的关系

4.1~4.9 把实验、状态、切换都铺完了,这一节收口:调度里最根本的那对矛盾。后面十节(多核、优先级、现实系统)都是它的延伸。

一、实验:只改时间片

五个长短不一的进程:32/16/8/4/2 拍,长的排前面、短的排后面——这个顺序很重要,它让【短作业等多久】变得可观察。 只改时间片:1、2、4、8、16、32。 量三个数:总耗时、平均周转、切换次数。

二、结果:两个指标真的反着走

总耗时全是 62 拍,一个数都不变——活儿就那么多,一个 CPU 就那么多时间。 平均周转最短是时间片 2,切换最少是时间片 32。不是同一个最优,这就是矛盾。 原因很具体:时间片一大,短作业(2 拍那个)就得排在长作业后面等一整片——明明只要 2 拍,却要等几十拍。 时间片一小,短作业能很快轮到自己,但每拍都在换人,切换次数顶上去。

三、所以现实里怎么选

看你这台机器是给谁用的: 交互式系统(桌面、手机、Web 服务):时间片小一点,让人感觉不到卡。切换税换来的是【每次点击都有响应】。 批处理系统(跑数据、编译、渲染):时间片大一点,把切换税省下来换成吞吐量。没人在乎单个任务等多久。 这两类系统的内核参数确实不一样——这就是为什么服务器与桌面机会有不同的调优建议。

四、一句话收这一节

调度的本质是【在固定的产能下分配等待】。 产能改不了(那是硬件与并行度的事),能改的只有【谁先等、等多久、换人换多勤】。 所以看到任何【调度优化】,都要问两句:它优化的是吞吐还是延迟?代价落在谁身上? 答不出这两句的优化方案,多半只是换了个地方疼。

五、动手 Demo:吞吐量与延迟——时间片的取舍

五个长短不一的作业,时间片从 1 拉到 32,看平均周转与切换次数怎么分道扬镳。 注意总耗时那一列:它一个数都不会变。

rows = []
for q in (1, 2, 4, 8, 16, 32):
    ps = [Proc("P%d" % i, n) for i, n in enumerate((32, 16, 8, 4, 2), 1)]
    done, sw, _tl, total = run(ps, quantum=q, ncpu=1)
    n, avg, mx = summary(ps, done)
    rows.append((q, total, avg, sw))

best_lat = min(rows, key=lambda r: r[2])   # 周转最好
best_sw = min(rows, key=lambda r: r[3])    # 切换最少

实跑输出:

--------------------------------------------------------------------
吞吐量与延迟——时间片的取舍
--------------------------------------------------------------------
① 五个长短不一的进程,只改时间片

      作业长度分别是 32/16/8/4/2 拍——**长作业排在前面,短作业排在后面**。

  时间片     总耗时     平均周转     切换次数
  --------------------------------------------
  1          62         31.8         44
  2          62         31.2         22
  4          62         36.0         11
  8          62         39.2         7
  16         62         44.8         6
  32         62         44.8         4

② 两个指标,反着走

      平均周转最短               时间片 2
      切换最少                   时间片 32
      两者是同一个吗             False

  **平均周转最短的是时间片 2,切换最少的是时间片 32。**
  **短作业排在长作业后面时,时间片越大它等得越久**——明明只要 2 拍,却要排在后面等几十拍。
  **总耗时几乎不变**——活儿就那么多,一个 CPU 就那么多时间。
  **变的是【平均等多久】与【换人多少次】。**
  **这就是调度里最根本的一对矛盾:**
  **响应快 ↔ 开销小,你只能选一个平衡点,选不到两个都好。**

  **现实中的选法很朴素**:交互式系统(桌面、手机)时间片小一点,
  让人感觉不到卡;批处理系统(跑数据)时间片大一点,把切换税省下来。

平均周转最优的是时间片 2,切换最少的是时间片 32——两者不是同一个。这就是本节要你记住的那对矛盾。 总耗时 62 拍在所有行里都一样:短作业 2 拍那个也在里面,它的等待被算进平均周转,却改不了总量。 这张表是全书最实用的一张:你以后调任何【并发度、批大小、线程数】,都会遇到同一对指标——吞吐与延迟,只能选平衡点。

📋 在真 Linux 上看这对矛盾:sysctl kernel.sched_min_granularity_ns 与 sched_latency_ns 就是时间片相关的参数;chrt -f 50 ./程序 切到实时策略,立刻能看到延迟变好、吞吐变差。

30 秒实验

  1. 把作业顺序改成短的在前面,看平均周转还随不随时间片变化——这就是【作业顺序也影响延迟】的证明。
  2. sysctl kernel.sched_min_granularity_ns kernel.sched_latency_ns 看当前值。
  3. 用 taskset -c 0 把两个程序绑到一个核上,人为制造抢核,再看它们的耗时。

常见误区

  1. 【存在一个既快又不卡的配置】。不存在。吞吐与延迟是同一笔账的两面,只能按场景取舍。
  2. 【调小时间片能提高吞吐】。恰恰相反,切换变多,吞吐通常变差,换来的是延迟变好。
  3. 【总耗时能靠调度缩短】。总耗时由总工作量与产能决定。调度只影响分配,不影响产能。
自测三问(点开对答案) 1. 为什么总耗时在所有时间片下都一样? —— 五个作业的 CPU 总量固定,一个 CPU 的时间也固定,**活儿就那么多**。调度改的是分配方式,不是产能。 2. 为什么平均周转最优的时间片不是最小的 1? —— 太小会让切换变多、每次只推进一点点;**时间片 2 在【短作业能挤进来】与【切换不过多】之间刚好平衡**。 3. 交互式系统与批处理系统为什么选不同的时间片? —— 交互式在乎**响应**,宁可交切换税也要让人感觉不到卡;批处理在乎**吞吐**,把切换税省下来。

相关笔记