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

让进程一边算一边等,CPU 就有大半时间在空转:单进程利用率只有一半多;加两个同样爱等的进程,利用率直接顶到九成以上。这就是多道程序设计的全部收益。

📝 本节和前面几节的关系

4.2 把模拟器跑通了,这一节做第一个真实验:让进程带上 IO 等待,看 CPU 什么时候闲、闲多久。

一、实验设置:一个爱等的进程

进程 P1 要跑 12 拍,但每跑 3 拍就要等 4 拍 IO。 这个比例很贴近真实:一个程序读文件、算一会儿、再读——算与等交替,而等往往比算长。 观察窗口取 20 拍,够看清两个完整周期。

二、看到了什么:一半时间在等

时间线是【跑 3 拍、等 4 拍】的循环。等的那 4 拍里,CPU 上没有别人,只能空转。 实测利用率五成多一点——不是 100%,也不是 0。这个数很重要:它说明【单进程系统里,CPU 的天花板由 IO 比例决定】。 要记住这条:你觉得机器【很慢】,可能不是 CPU 不够,是它在等,而没人在它等的时候干活。

三、加人:三个同样爱等的进程

再放两个同样的进程进去。活儿多了两倍,但总耗时没到三倍。 因为 A 在等 IO 的时候,B 或 C 正好在跑——空转被填上了。 实测利用率从五成多顶到九成以上。这就是【多道程序设计】的全部收益:不是让单件事变快,是让机器不闲着。 注意措辞:单件事的周转时间反而可能变长(要跟别人抢),但整机吞吐量上去了。这正是吞吐与延迟打架的第一个例子。

四、这件事在现实里长什么样

一台机器上跑着几百个进程,绝大多数时候只有几个在真跑,其余都在等 IO、等锁、等定时器。 所以【进程数很多】不等于【机器很忙】——ps aux 里一堆 S 状态,说的是它们都在等。 反过来:一台机器 CPU 占用一直 100%,说明瓶颈在 CPU;占用很低但响应很慢,说明瓶颈在 IO 或锁。 这条判断法的用处:出问题时先看 CPU 占用率,就知道该往哪个方向查。

五、动手 Demo:做一次实验——让进程一边算一边等

先跑一个进程(每 3 拍算、4 拍等),看时间线上那些【等 IO】的格子;再放三个进去,看同样的格子有没有被填上。

# ① 一个进程:每跑 3 拍,等 4 拍
ps = [Proc("P1", 12, io_each=3, io_len=4)]
done, sw, tl, total = run(ps, quantum=4, ncpu=1)
# 时间线里 names[0] == "-" 的那些拍,就是 CPU 空转

# ② 三个同样的进程
ps = [Proc("P1", 12, io_each=3, io_len=4),
      Proc("P2", 12, io_each=3, io_len=4),
      Proc("P3", 12, io_each=3, io_len=4)]
done, sw, tl, total = run(ps, quantum=2, ncpu=1)
busy = sum(p.ran for p in ps)      # 真正占 CPU 的拍数

实跑输出:

--------------------------------------------------------------------
做一次实验——让进程一边算一边等
--------------------------------------------------------------------
① 只有一个进程,但它每跑 3 拍就要等 4 拍

      tick    它在干什么
      ------------------------------
      0        在跑
      1        在跑
      2        在跑
      3        等 IO(CPU 闲着)
      4        等 IO(CPU 闲着)
      5        等 IO(CPU 闲着)
      6        在跑
      7        在跑
      8        在跑
      9        等 IO(CPU 闲着)
      10       等 IO(CPU 闲着)
      11       等 IO(CPU 闲着)
      12       在跑
      13       在跑
      14       在跑
      15       等 IO(CPU 闲着)
      16       等 IO(CPU 闲着)
      17       等 IO(CPU 闲着)
      18       在跑
      19       在跑

      总耗时                      21 拍
      它真正占 CPU                12 拍
      CPU 利用率                  57%

  **一个进程一边算一边等,CPU 就有大半时间在空转。**
  **这就是为什么要有【多道】——让别的进程在它等的时候顶上。**

② 加两个同样爱等的进程,再看利用率

  进程数     总耗时     占 CPU 拍数     利用率
  ---------------------------------------------
  1 个       21         12              57%
  3 个       37         36              97%

  **活儿多了三倍,但总耗时没到三倍**——因为等待期被别人的计算填上了。
  **这就是【多道程序设计】的全部收益:不是让单件事变快,是让机器不闲着。**

单进程那张时间线上一半是空的——这不是模型的问题,是现实:一个只会算和等的进程,就是让 CPU 一半时间闲着。 三个进程时,总耗时 37 拍左右,而合计占 CPU 36 拍——利用率九成七。几乎每一拍都有人在跑。 注意【三倍活儿没换来三倍时间】:如果单进程是 21 拍,三个应该是 63 拍;实际只要 37 拍——省下来的正是原来空转的那些拍。

📋 在真 Linux 上复核:cat /proc/stat 第一行 cpu 后面的数字是各状态累计时间(user/nice/system/idle/iowait…),两次数值相减除以总增量就是这段时间的利用率;vmstat 1 会每秒打一行,看 id 与 wa 两列。

30 秒实验

  1. 把 io_len 从 4 改成 1,看利用率变成多少(等得少了,空转就少)。
  2. 把进程数加到 8 个,看利用率能不能到 100%——到不了,因为总有一段时间全在等。
  3. vmstat 1 5 看 id(空闲)与 wa(等 IO)两列,你现在看到的这两列,就是上面这张时间线的真实版。

常见误区

  1. 【CPU 空转就是浪费,所以进程越多越好】。加到一定程度后,每件事的等待都会变长,而利用率几乎不再涨——收益递减。
  2. 【利用率 100% 是好事】。它只说明 CPU 没闲着,不说明干的活有用——死循环也能把利用率跑到 100%。
  3. 【IO 等待不算成本】。对单个进程来说,等就是等,它占的是用户感受到的那段时间,一点没省。
自测三问(点开对答案) 1. 单进程每跑 3 拍等 4 拍,利用率大约多少?为什么? —— 五成多一点(3/7 左右)。**上限由 IO 比例决定**,不是 CPU 能力。 2. 三个同样的进程,为什么总耗时不到单进程的三倍? —— 因为 A 等 IO 时 B 或 C 在跑,**原来空转的拍被填上了**。这就是多道设计的收益。 3. 从 CPU 占用率能判断什么? —— 占用满→瓶颈在 CPU;占用低但响应慢→瓶颈在 IO 或锁。**先看这个再决定往哪查。**

相关笔记