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

两个跑不完的活儿在 100 拍里分 CPU:权重 1:1 时各 50;2:1 时 67:33;4:1 时 80:20。这是按比例分账,不是按先后排序——低权重的不会被饿死,只是拿得少。nice 越小越不客气,这个方向最容易记反。

📝 本节和前面几节的关系

4.19 教你读懂 ps 里的 NI 列,这一节讲改它会怎样——这是调度器留给你的唯一一个可调旋钮。

一、nice 值:一个容易记反的数

范围 −20 到 +19。越小越不客气(−20 最凶),越大越谦让(+19 最让)。 为什么叫 nice:它是【你对别人有多客气】。值越大越客气,越愿意让别人先跑。 普通用户只能调高自己的 nice(变得更客气),调低(变得更凶)需要特权——否则谁都能给自己 −20,等于没有优先级。 这条设计说明一件事:优先级是一种资源,不能让程序自己随便抢。

二、它是按比例分账,不是排队

实测:两个都跑不完的活儿,在 100 拍里——1:1 各拿 50/50,2:1 拿 67/33,4:1 拿 80/20。 注意 2:1 时是 67 不是 100——它不是【高权重先跑完】,是【按比例分】。 这条区别非常重要:调高 A 的权重,A 变快,B 变慢,总产出几乎不变(CPU 就那么多)。 所以【调优先级】不是【提升性能】,是【重新分配】。它解决的是【谁更重要】,不是【怎么更快】。

三、什么时候该调它

该调:有一个必须及时响应的任务(比如心跳、监控、音视频),宁可让批处理慢一点。 该调:一个跑得飞快的批处理在抢交互式任务的 CPU,给它 +10,它自己会变客气。 不该调:想让程序变快的时候。那是算法和并行度的事。 不该调:瓶颈在 IO 的时候。nice 只管 CPU 分配,iowait 里一点都不管。 判据:先看瓶颈在不在 CPU。不在的话,调 nice 是白费。

四、现代系统的做法

nice 只是个粗调旋钮。真正的控制在两个地方: 一、cgroup 的 cpu.weight:成比例分配,且是分组的——一个组里十个进程,组的总份额不变。 二、实时调度策略(SCHED_FIFO/SCHED_RR):优先级高于所有普通进程,用不好会把系统锁死(它不让出的话,普通进程永远轮不到)。 所以生产环境的正确做法:用 cgroup 分组限流,用 nice 做单进程微调,实时策略只在真有硬实时需求时用,而且要留余量。

五、动手 Demo:变更优先级——让某个进程多分一点

两个都跑不完的活儿,在同样的 100 拍里各分到多少?权重取 1:1、2:1、4:1 三组。 看最后一列的比例——它应该正好等于权重比。

for w1, w2 in ((1, 1), (2, 1), (4, 1)):
    ps = [Proc("A", 100000, weight=w1), Proc("B", 100000, weight=w2)]
    done, sw, _tl, total = run(ps, ncpu=1, weighted=True, horizon=100)
    print(w1, w2, ps[0].ran, ps[1].ran)

# 真系统上改权重:
# nice -n 10 ./批处理        (启动时设)
# renice -n 10 -p <pid>       (运行中改)

实跑输出:

--------------------------------------------------------------------
变更优先级——让某个进程多分一点
--------------------------------------------------------------------
① 两个进程抢一个 CPU,权重不同

  A 权重     B 权重     A 拿到     B 拿到     比例
  -----------------------------------------------------
  1          1          50         50         1.00 : 1
  2          1          67         33         2.03 : 1
  4          1          80         20         4.00 : 1

  **两个都跑不完的活儿,在同样的 100 拍里,权重 2 比 1 时 A 拿到的正好是 B 的两倍。**
  **注意这不是【优先级高的先跑完】,而是【按比例分账】**——
  **B 不会饿死,它只是拿得少。**

② nice 值怎么对应到权重

  nice 值     含义                     大约拿到
  ------------------------------------------------------
  -20         最高优先级(要特权)     远超普通进程
  0           默认                     基准
  +10         主动让一让               约基准的三分之一
  +19         最低                     约基准的百分之几

  **nice 越小越【不客气】**(-20 最凶,19 最谦让)。这个方向常记反。
  **改 nice 只影响【抢 CPU 时怎么分】**,不改变进程本身的效率——
  **如果你把 A 调高,B 并不会变快,只会变慢。**

三行分别是 50:50、67:33、80:20——比例正好等于权重比。这就是【按比例分账】的实测。 注意 2:1 时不是【A 跑完才轮到 B】——B 一直在拿到三分之一。这是【不会饿死】的含义。 所以调 nice 不会让总产出变多,只是重新分配。想让总产出变多,只能加算力或改算法。 这个旋钮最实用的场景:一个批处理在抢交互式任务的 CPU,nice -n 10 启动它,交互立刻恢复顺畅。

📋 在真 Linux 上验证:起两个死循环进程,一个 nice -n 19,top 里会看到一个吃 95%、一个吃 5% 左右——这就是 19 与 0 的差距。

30 秒实验

  1. nice -n 19 sleep 100 & 然后 ps -o ni,cmd -p $!——看 NI 列变成 19。
  2. renice -n 5 -p <pid> 改运行中的进程,看 top 里它的份额有没有变。
  3. top 里按 r 可以直接 renice——不用记命令,交互式改更快。

常见误区

  1. 【nice 越大优先级越高】。反了。越小越凶,越大越客气。
  2. 【调高 nice 能让程序变快】。它只是【抢的时候多拿一点】。总产能不变,只是重新分配。
  3. 【优先级能解决 IO 慢】。nice 只管 CPU 分配。卡在 IO 上时,调它一点用都没有。
自测三问(点开对答案) 1. nice 值的范围与方向?为什么普通用户只能调高? —— −20 到 +19,**越小越不客气**。普通用户只能调高(变客气),**否则谁都能给自己 −20,优先级就没意义了。** 2. 2:1 的权重为什么是 67:33 而不是 100:0? —— 它是**按比例分账**,不是按先后排队。**低权重的不会被饿死,只是拿得少。** 3. 什么情况下不该调 nice? —— 想【让程序变快】的时候(那是算法与并行度的事),以及**瓶颈在 IO** 的时候(nice 管不到)。

相关笔记