想象一所幼儿园,滑梯是教室里唯一的 CPU。老师(Linux 内核的调度器)要让每个孩子(进程)都有机会滑,又不能有人霸占到底。CFS 调度器给出的答案是:不按“排队先后”,也不按“谁哭得响”,而是每个人胸前挂一本“滑行账本”——谁账本上的“已滑次数”最少,谁就先去滑。这本账有魔法:重要孩子的账本走得慢,普通孩子的账本走得快,于是重要的孩子自然多滑几次,但谁也不会永远轮不到。老师手里的账本还自动保持排序,翻到首页永远是“最该滑的孩子”。这套“公平哲学”——权重、vruntime、红黑树、调度周期——就是 Linux 完全公平调度器(CFS)的全部秘密。

岗位关联

理解 CFS 的 vruntime 与红黑树机制,是深入 Linux 性能调优、容器 CPU 限额和云原生资源调度面试的必备内核直觉。

目录
  1. 可视化演示
  2. 公平调度理念
  3. 调度实体与权重
  4. vruntime计算
  5. 红黑树组织
  6. 调度延迟参数
  7. 组调度公平性
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示vruntime公平调度、红黑树组织、权重换算与调度延迟调优。

本节配有交互动效(进入视口后自动加载,约 3KB)。

vruntime虚拟运行时间 vruntime最小者获得CPU实现按权重公平 高权重进程vruntime增长慢获得更多CPU vruntime += delta * 1024 / weight vruntime归一化权重使不同nice进程可比

公平调度理念

回到幼儿园的滑梯。假如老师规定“按名单循环,每人滑一次就换人”,动作慢的孩子会拖垮整条队伍;假如只看谁哭得最凶,场面迟早失控。老师真正需要的,是一把让所有人都服气的“公平尺子”——它不搞平均主义,而是承认每个孩子的不同,同时保证没人被饿着。

Linux 的 CFS(完全公平调度器,Completely Fair Scheduler)面对的是同一个难题:CPU 只有那么一两个核,可运行进程却有几十上百个。CFS 给出的公平定义是:在一段足够长的时间里,每个进程获得的 CPU 时间,应当与其权重(weight)成正比。权重越高的进程,理应分得越多的计算资源。

这个“按权重分时间”的理想,最早来自“理想多任务处理器”(Ideal Multi-Processing):假设有无限核,每个进程都能同时以自己应有的速度前进。CFS 在单核上做不到“同时”,却可以用时间上的轮换来逼近这种公平。每个进程应得的 CPU 份额,写出来就是:

份额公式是老师心里的分配蓝图,但蓝图不能直接拿给孩子看——老师需要的是“实地记账”:每滑一次记一笔,账本上数字最小的孩子优先。从蓝图到账本,中间还差一枚决定“记多少墨水”的徽章,也就是下一节的权重。

CPU 份额i=wi∑j=1nwj\displaystyle \text{CPU 份额}_i = \frac{w_i}{\displaystyle\sum_{j=1}^{n} w_j}
分子是某个进程的权重,分母是所有人权重之和,比值就是它在 CPU 时间里的“股份”。
  1. 进程权重 $w_i$进程 i 在调度器眼中的“股份”,值越大,期望占用的 CPU 越多。
    wiw_i
  2. 权重总和 $\displaystyle\sum_{j=1}^{n} w_j$把当前所有可运行进程的权重加在一起,作为归一化比例的分母。
    ∑j=1nwj\sum_{j=1}^{n} w_j
  3. 份额比值两者相除,得到的是进程 i 在总 CPU 时间中应当分到的比例。
    wi∑j=1nwj\frac{w_i}{\sum_{j=1}^{n} w_j}

调度实体与权重

幼儿园里,每个孩子胸前都别着一枚徽章,徽章亮度由“nice 老师”评定:越是被照顾的孩子,徽章越亮,滑一次在账本上“抵扣”的墨水越多。在 CFS 的世界里,徽章就是权重(weight),而发放规则来自一个叫 nice 值的数字。

nice 值范围是 -20 到 19,默认 0。数值越大,代表进程越“谦让”,权重越低;数值越小,权重越高。需要注意的是,权重不直接挂在进程上,而是挂在“调度实体”(sched_entity)上——调度实体可以是线程,也可以是一整组进程,只要是内核需要单独排队、单独记账的对象都算。这个抽象,为后面的组调度埋下了伏笔。

nice 值到权重的换算,内核用一张预计算好的表(sched_prio_to_weight)查出来,相邻 nice 等级之间大约相差 1.25 倍。写成近似公式就是:

把徽章亮度和份额公式合在一起,老师就能算出每个孩子在一次轮换中该滑多久。但记账本上的数字怎么记,才能让 VIP 孩子“滑得多却记得少”?这就需要一本会“变魔术”的账——vruntime。

w(nice)=1024×1.25−nice\displaystyle w(nice) = 1024 \times 1.25^{-nice}
nice 每降低 1 级,权重约乘以 1.25;每升高 1 级,权重约除以 1.25。nice=0 的权重基准是 1024。
  1. 基准 1024($NICE\_0\_LOAD$)nice=0 时的权重,所有份额计算的锚点。
    10241024
  2. 底数 1.25相邻两级 nice 之间的权重比例,体现在内核权重表中相邻两项之比。
    1.25−nice1.25^{-nice}
  3. 指数的负号nice 越小幂越大。比如 nice=-1 时权重约为 $1024 \times 1.25 = 1280$,与表里的 1277 几乎一致。
    1024×1.2511024 \times 1.25^{1}

vruntime计算

老师发下了记账本:每个孩子一页,每次滑完就记一笔“已滑次数”。但记法有讲究——徽章越亮的孩子,记一笔用的墨水越少。普通孩子滑 1 秒记 1 格,而徽章亮度 2048 的孩子滑 1 秒只记 0.5 格。这样 VIP 孩子即使滑得久,账本数字也可能不高,下次依然轮得到他。

这本记账就是 vruntime(虚拟运行时间)。它衡量的是进程“在公平标尺下已经消耗的 CPU”,而不是墙上的钟表时间。“虚拟”二字是灵魂:同样的真实运行时长,经过权重修正后,记到不同进程账上就变成不同的数字。

CFS 在时钟 tick、进程入队、出队、唤醒等时机调用 update_curr() 累计 vruntime,核心公式是:

有个反直觉但极其重要的细节:睡眠中的进程不累计 vruntime。一个进程睡 1 秒再醒来,账本数字还停在原处,而运行队列里其他进程的账本已经往前走了一段,于是它一醒来就是“账上最少的人”,会立刻被选中运行。这正是交互式程序(比如编辑器、聊天框)能保持丝滑响应的原因。

记账哲学一句话:账上数字越小,代表在公平尺度下“欠”得越多,下一次就该优先补偿。CFS 每时每刻都要找到账上最小的那个孩子——几十个孩子还好说,几百上千个孩子时,老师怎么保证翻账本不花时间?答案是一本神奇的活页夹:红黑树。

Δvruntimei=Δti×1024wi\displaystyle \Delta vruntime_i = \frac{\Delta t_i \times 1024}{w_i}
进程 i 每运行 $\Delta t_i$ 秒,就在账上加一笔“真实时间 × 1024 除以权重”的虚拟时间增量。
  1. 真实运行时间 $\Delta t_i$进程 i 本次在 CPU 上实际跑了多久,由内核时钟直接测得。
    Δti\Delta t_i
  2. 基准负载 1024nice=0 进程的权重,放在分子作为统一的参照系。
    10241024
  3. 分母权重 $w_i$权重翻一倍,同样的运行时间对应的 vruntime 增量就减半。
    Δvruntimei=Δti×1024wi\Delta vruntime_i = \frac{\Delta t_i \times 1024}{w_i}

红黑树组织

老师的记账本不是普通本子,而是一个“永远按已滑次数从小到大排列”的魔法活页夹。孩子滑完一次,老师涂改数字,再把那页插回正确位置。无论什么时候翻开第一页,看到的永远是“最该滑的孩子”,不用从头一页页比较。

这个魔法活页夹,在 CFS 里就是红黑树(Red-Black Tree)。每个 CPU 的运行队列(cfs_rq)维护一棵红黑树,以每个进程的 vruntime 为键值。进程该运行时,直接取最左叶子——它就是 vruntime 最小的进程。

红黑树保证插入、删除、查找都在对数时间内完成:

为了更快,内核还专门缓存了最左节点指针(rb_leftmost),让“找下一个运行进程”在绝大多数情况下几乎是 O(1) 的;只有插入、删除新节点后,才需要沿着树做几次旋转来维持平衡。左旋、右旋、变色这些动作听起来玄,本质就是老师整理活页夹时做的“厚度调整”,保证翻书永远不慢。

有了这本活页夹,老师可以瞬间找到最该滑的孩子。但新的麻烦来了:如果每次都只滑 0.1 秒就换人,孩子们光爬上爬下就耗光了时间。老师必须定两条班规:多久必须轮到一次,一次最少滑多久——这就是调度延迟与最小粒度。

T(n)=O(log⁡n)\displaystyle T(n) = O(\log n)
红黑树是一种自平衡二叉搜索树,任何操作都沿着树走,复杂度与树高相关。
  1. $T(n)$一次插入、删除或查找所需时间,其中 n 为可运行进程数。
    T(n)T(n)
  2. 树高上限 $h \le 2\log_2(n+1)$红黑树的平衡性质保证树高不会失控,最坏路径不超过最短路径的两倍。
    h≤2log⁡2(n+1)h \le 2\log_2(n+1)
  3. $O(\log n)$即便有 100 万个可运行进程,也只需约 20 次比较就能完成一次操作。
    O(log⁡n)O(\log n)

调度延迟参数

老师宣布两条班规。第一条:“任何等待的孩子,最多等一轮,就一定能滑上一次。”这个承诺的时间窗口,就叫调度延迟。第二条:“一次滑行最短也得满 0.75 秒。”因为爬上滑梯、系好坐垫都要开销,换人太勤反而浪费,这一条叫最小粒度。

内核里这两个参数分别是 sysctl_sched_latency(默认 6ms)和 sysctl_sched_min_granularity(默认 0.75ms)。当就绪进程很少时,老师可以直接兑现 6ms 的承诺;可一旦等待的孩子超过 8 个(6ms ÷ 0.75ms),再坚持 6ms 就没人能滑满 0.75 秒,周期只好随人数线性增长:

周期定了,再套用份额公式,每个进程在一个周期内能分到的运行时间就是:

班规的本质是在两个目标之间走钢丝:等待时间短(响应性好)与切换开销小(吞吐量大)。6ms 和 0.75ms 不是拍脑袋定的,而是“每轮最多等 6ms、每人至少滑 0.75ms”这两条朴素承诺的平衡点。听懂这两条班规,就听懂了 CFS 一切调参行为的底层逻辑。

Tsched=max⁡(sysctl_sched_latency,  n×sysctl_sched_min_granularity)\displaystyle T_{sched} = \max\left(sysctl\_sched\_latency,\; n \times sysctl\_sched\_min\_granularity\right)
每个进程至少滑 0.75ms,所以 n 个进程跑完一轮至少需要 $n \times 0.75$ms;若这比 6ms 小,就维持 6ms 的延迟承诺。
  1. 进程数 $n$当前 CPU 上处于就绪态的进程数量。
    nn
  2. $n \times sysctl\_sched\_min\_granularity$让每个进程都至少滑一次的最短总时长。
    n×0.75msn \times 0.75\text{ms}
  3. $\max(\cdot)$在 6ms 承诺与线性增长之间取较大值,进程少时保持低延迟,进程多时保证最小粒度不破。
    max⁡(6ms,  n×0.75ms)\max\left(6\text{ms},\; n \times 0.75\text{ms}\right)
Ti=Tsched×wi∑j=1nwj\displaystyle T_i = \frac{T_{sched} \times w_i}{\displaystyle\sum_{j=1}^{n} w_j}
把调度周期当作一块蛋糕,按权重比例切开,$T_i$ 就是进程 i 的理想运行片段。
  1. 分子 $T_{sched} \times w_i$周期总时长乘以该进程权重,权重越高,分到的片段越长。
    Tsched×wiT_{sched} \times w_i
  2. 分母权重和所有可运行进程权重之和,作为归一化尺度。
    ∑j=1nwj\sum_{j=1}^{n} w_j
  3. $T_i$进程 i 的“虚拟时间片”,每次 tick 时若当前进程运行超过它,就会触发抢占。
    TiT_i

组调度公平性

幼儿园里还有班级。如果只盯着每个孩子记账,一个班 40 个孩子就天然比 10 个孩子的班“人均吃亏”。老师说:先保证每个班的总滑行时间公平,再在班里按徽章公平分配。于是记账本不再只有孩子这一层,班级本身也开了一本账。

这正对应内核的组调度(CONFIG_FAIR_GROUP_SCHED):cgroup 把进程组织成树形层级,每一层都是一个调度实体。组先作为整体参与上一层 CFS 竞争,赢得的 CPU 时间再在组内按 vruntime 重新分配。公平因此有了递归结构——组间公平套着组内公平,层层嵌套。

组调度还带了“限流”功能:管理员可以给某个 cgroup 设定 CPU 带宽上限,参数是一对微秒值:

这套机制离 AI 和云原生一点都不远:Kubernetes 里给 Pod 设 CPU limit=2,底层写的就是 cfs_quota_us=200000、cfs_period_us=100000。你写下的每个资源请求,最终都变成 CFS 教室里的一次记账。幼儿园老师当年那本活页夹,如今撑起了整个容器世界的资源公平。

CPU 带宽上限=cfs_quota_uscfs_period_us\displaystyle \text{CPU 带宽上限} = \frac{cfs\_quota\_us}{cfs\_period\_us}
每过 period 微秒结算一次,该组累计运行时间不得超过 quota 微秒。
  1. $cfs\_quota\_us$一个结算周期内,该组最多可累计运行的 CPU 时间(微秒)。
    cfs_quota_uscfs\_quota\_us
  2. $cfs\_period\_us$结算周期,惯例取 100000 微秒(100ms)。
    cfs_period_uscfs\_period\_us
  3. 两者比值就是该组占用 CPU 的硬上限。例如 50000/100000 = 50%,即使 CPU 全空闲也不能超过。
    50000100000=50%\frac{50000}{100000} = 50\%

AI 应用场景

多租户 ML 训练平台的 CPU 限流

阶段:基础设施资源调度

解决的问题:分布式训练中 GPU 只负责张量计算,数据加载、图像解码、DataLoader 的 CPU 预处理同样吃紧。传统 nice/ulimit 无法按租户精细约束,容易互相抢占,导致训练吞吐抖动。AI 平台用 CFS 组调度把每个租户放入独立 cgroup,用 cfs_quota 设定 CPU 上限,再调权保障交互任务。

具体案例:某 MLOps 平台为离线训练任务建 cgroup 权重 1024,日志采集与模型打包后台任务 nice=15(权重约 110),GPU 节点 CPU 利用率从 42% 提升到 76%,训练 ETA 波动明显收窄。

Kubernetes 的 CPU limit 究竟怎么生效

阶段:容器编排与资源隔离

解决的问题:开发经常困惑“明明 limit=2,容器怎么还是跑满多核”。了解 CFS quota/period 后就知道:limit 是周期累计上限而非并发核数。

具体案例:Pod 配置 limits.cpu=2 时,kubelet 写 cgroup cpu.max=200000 100000。推理服务平时闲置,突发时若超过 200ms/100ms 的配额就会被 CFS 限流节流。结合容器 CPU 使用画像,能精确规划副本数。

在线推理服务 QoS 的权重调优

阶段:模型服务部署与运行时保障

解决的问题:同一节点上跑着高价值在线推理与批量离线推理,希望优先保障在线 P99 延迟。

具体案例:将在线服务容器的进程 nice 调成 -5(权重约 3100),离线批处理 nice=10(权重约 110),CFS 自动把约 96% 的争抢机会分给在线服务,无需手动绑核。

Python 演示

python58 行
class SchedEntity:    """一个可被调度的实体: 线程或进程组."""    def __init__(self, name, weight):        self.name = name        self.weight = weight        self.vruntime = 0.0        self.actual_runtime = 0.0     def __repr__(self):        return "{}: vr={:.2f} runtime={:.1f}ms".format(            self.name, self.vruntime, self.actual_runtime)  def pick_next(entities):    """红黑树最左节点: 返回 vruntime 最小的实体."""    return min(entities, key=lambda e: e.vruntime)  def run_one_tick(entities, current, tick_ms):    """当前进程运行 tick_ms 毫秒, 并按权重更新 vruntime."""    base_load = 1024.0    current.actual_runtime += tick_ms    current.vruntime += tick_ms * base_load / current.weight    return current  def simulate(entities, total_ms=60.0, tick_ms=1.0):    timeline = []    now = 0.0    while now < total_ms - 1e-9:        current = pick_next(entities)        run_one_tick(entities, current, tick_ms)        now += tick_ms        timeline.append((round(now, 1), current.name, round(current.vruntime, 3)))    return timeline  def main():    kids = [        SchedEntity("P0", 1024),        SchedEntity("P1", 2048),        SchedEntity("P2", 512),        SchedEntity("P3", 3072),    ]    total_ms = 60.0    timeline = simulate(kids, total_ms=total_ms, tick_ms=1.0)    for t, name, vr in timeline:        print("t={:>4.1f}ms  run={}  vruntime={:.3f}".format(t, name, vr))    print("\n===== 公平性统计 (60ms) =====")    weight_sum = sum(k.weight for k in kids)    for k in kids:        ideal = total_ms * k.weight / weight_sum        print("{}: 实际={:.1f}ms 理论={:.1f}ms 偏差={:+.1f}ms".format(            k.name, k.actual_runtime, ideal, k.actual_runtime - ideal))  if __name__ == "__main__":    main()
目的:用 Python 模拟 CFS 的 vruntime 公平调度:每次选出 vruntime 最小的进程运行 1ms,统计各进程实际获得的 CPU 时间并与理论份额对比。
  1. base_load = 1024.0nice=0 的基准权重,对应 CFS 公式里的 1024。
  2. current.vruntime += tick_ms * base_load / current.weight核心的 vruntime 累计公式,权重越高增量越小。
  3. min(entities, key=lambda e: e.vruntime)模拟红黑树“取最左节点”,选出 vruntime 最小的进程。
  4. ideal = total_ms * k.weight / weight_sum用份额公式算出理论公平时间,用来衡量模拟是否公平。
  5. timeline = simulate(kids, total_ms=total_ms, tick_ms=1.0)以 1ms 为最小粒度跑完 60ms,输出每一步的调度日志。

常见误区

误区我觉得 vruntime 就是进程在 CPU 上跑了多少秒的计时器,数字大说明这个进程跑得久。

正解vruntime 是“虚拟运行时间”,经过权重修正。权重 2048 的进程跑 2ms,账上只记 1ms;权重 512 的进程跑 0.5ms,账上就记 1ms。它是一把公平标尺,不是秒表。就像幼儿园的记账本,VIP 孩子滑 1 秒只记半格墨水。判断谁该运行时,看的是这本“被调过墨水的账”,而不是墙上的钟。

为何常见:名字里带 time,而且确实在 tick 中断里累加,容易被人当成普通计时器。

误区nice 是“好人卡”,nice 值大的进程应该被系统优待,分到更多 CPU。

正解恰恰相反。nice 范围 -20~19,数值越大越“谦让”,权重越低,分到的 CPU 越少。nice=19 的意思是“我很 nice,最后再轮到我”;nice=-20 才是“我最要紧,请先照顾我”。类比徽章:数字越小越亮。

为何常见:英文单词 nice 的日常语义产生了直觉误导,和内核里的含义正好相反。

误区CFS 就是把所有进程排成一队,轮流各跑一段固定时间。

正解CFS 不是固定时间片轮流。第一,每个进程的时间片由调度周期和权重动态算出;第二,选择依据是 vruntime 最小者,而“最小者”由红黑树实时维护;第三,权重不同的进程根本不会轮流——高权重进程的 vruntime 增长慢,会连续被选中多次。只有所有进程权重相同时,才退化成近似轮流。

为何常见:教科书用“时间片”示意调度,容易把 CFS 和简单轮转画等号。

误区系统里几万个线程,要找到 vruntime 最小的那个,肯定得遍历整棵树,性能会线性下降。

正解红黑树保证插入、删除、查找都是 O(log n),而且内核缓存了最左节点指针,取下一个进程几乎是 O(1)。更关键的是,每个 CPU 的就绪队列通常只有几十个进程,树高不过 10 层左右,性能完全不是瓶颈。

为何常见:把链表的“线性遍历”思维带到了二叉树上,低估了平衡树的高度保证。

误区sched_latency 是响应时间,调成 1ms 是不是所有程序都变快了?

正解调度延迟越小,每个进程分到的运行片段就越短,上下文切换、缓存失效的开销占比急剧上升,整体吞吐反而下降。6ms 是“响应性 vs 吞吐量”的平衡点,而且进程超过 8 个时周期会自动变成 n×0.75ms——保证最小粒度优先于延迟承诺。

为何常见:只盯着“等多久”这一个指标,忽略了“切换一次要花多少钱”。

面试题

Q1. CFS 调度器选择下一个运行进程的依据是?

单选题 · easy

  • A.nice 值最大的进程
  • B.vruntime 最小的进程← 正确答案
  • C.等待时间最长的进程
  • D.权重最高的进程

答案:B。CFS 用 vruntime 衡量“在公平标尺下已消耗的 CPU”,总是选择最小的进程运行。就像老师翻开活页夹永远看第一页。权重最高的进程只是因为 vruntime 增长慢而更常被选中,但选择判据本身是 vruntime 最小,而非权重最大。

Q2. 关于 vruntime,下列说法正确的是?

单选题 · easy

  • A.它是进程的真实运行时间
  • B.它是经过权重修正的虚拟时间← 正确答案
  • C.它只在进程创建时计算一次
  • D.它与权重完全无关

答案:B。vruntime 是“虚拟运行时间”,按 Δt×1024/weight 累计,同一秒真实时间在不同权重进程账上记的数值不同。它是动态更新的公平标尺,与权重强相关。A、C、D 都是把虚拟时间当成普通计时的错误理解。

Q3. 以下哪种 nice 值的进程最容易获得 CPU?

单选题 · easy

  • A.19
  • B.10
  • C.0
  • D.-20← 正确答案

答案:D。换算公式 w=1024×1.25^(-nice),nice 越小权重越大。nice=-20 时权重约 88761,是所有值里最高的。就像徽章数字越小越亮,最亮的徽章最容易优先滑梯。

Q4. 进程 A 权重 1024,进程 B 权重 2048,二者实际运行时间相同时,谁的 vruntime 增长更快?

单选题 · mid

  • A.A← 正确答案
  • B.B
  • C.一样快
  • D.都不增长

答案:A。Δvr = Δt×1024/w。A 的增量 = Δt×1,B 的增量 = Δt×0.5,权重小的 A 增长更快。因此当 A 被多分配时,其 vruntime 会迅速追上并超过 B 从而让出 CPU——这正是权重高者获得更多运行时间的机制。

Q5. nice 值从 0 降低到 -1,进程权重约如何变化?

单选题 · mid

  • A.减半
  • B.不变
  • C.变为原来的 1.25 倍← 正确答案
  • D.变为原来的 1/1.25

答案:C。w=1024×1.25^(-nice),nice 从 0 变成 -1 相当于乘以 1.25,约为 1280,与内核权重表中 -1 对应的 1277 接近。徽章变亮了一档。

Q6. 关于 CFS 红黑树的说法,正确的有?

多选题 · mid

  • A.以 vruntime 为键值排序← 正确答案
  • B.最左叶子即下一个调度对象← 正确答案
  • C.插入、删除复杂度均为 O(log n)← 正确答案
  • D.每个叶子节点对应一个 CPU 核心

答案:ABC。红黑树以 vruntime 为键排序,最左叶子就是最小节点,插入删除都 O(log n)。叶子与 CPU 核数无关——每个 CPU 各维护一棵红黑树,而不是每个叶子对应一个核。D 属于概念混淆。

Q7. cgroup 中 cfs_quota_us=50000、cfs_period_us=100000 表示?

单选题 · mid

  • A.该组最多使用 50% CPU← 正确答案
  • B.该组最少使用 50% CPU
  • C.该组只能运行 50 个进程
  • D.每 50ms 强制杀死一次进程

答案:A。带宽上限 = quota/period = 50000/100000 = 50%,是硬上限而非保底。像老师给一个班发的“游玩代金券”,一个周期内最多花这么多时间,花完就得等下一个周期。

Q8. 系统中有 4 个权重均为 1024 的普通进程,sysctl_sched_latency=6ms,min_granularity=0.75ms,则调度周期约为?

单选题 · hard

  • A.6ms← 正确答案
  • B.3ms
  • C.4.5ms
  • D.0.75ms

答案:A。进程数 4 ≤ 8(=6ms/0.75ms),套用 max 公式:max(6, 4×0.75) = 6ms,周期保持 6ms 的延迟承诺,不放大。每个等权进程理想运行时间 = 6×1024/(4×1024) = 1.5ms。

Q9. 关于 CFS 组调度的说法,正确的有?

多选题 · hard

  • A.先保证组间公平,再在组内递归公平← 正确答案
  • B.组调度使进程树形组织,组也是一个调度实体← 正确答案
  • C.组调度只在单核 CPU 上生效
  • D.组调度无法限制一个组占用 CPU 的上限

答案:AB。组调度把进程组织成树,组作为 sched_entity 参与上层竞争,再在组内递归分配,A、B 正确。组调度在多核上同样生效,且 cfs_quota 正是用来限制 CPU 上限的,C、D 错误。

Q10. 当就绪进程数从 10 个增加到 40 个时(sysctl_sched_latency=6ms,min_granularity=0.75ms),关于调度周期的说法正确的有?

多选题 · extreme

  • A.周期从 10×0.75=7.5ms 增大到 30ms← 正确答案
  • B.每个进程的理想运行时间始终 ≥ 0.75ms← 正确答案
  • C.6ms 的延迟承诺只在进程少时成立← 正确答案
  • D.周期始终保持 6ms 不变

答案:ABC。n>8 后,周期 = max(6, n×0.75),分别取 7.5ms 与 30ms;每个人分到的片段 = 周期/n = 0.75ms 不变,这正是最小粒度的含义。6ms 本身退化为 max 公式里的下界,所以 C 正确、D 错误。