公众号旧文
为什么 CPU 时间可以大于墙上时间?
我们在 Linux 中查看服务状态时,经常会看到一个容易让人产生误解的指标:CPU 时间。例如执行 systemctl status 查看某个服务,可能会看到:
CPU: 1.234s
很多人第一眼会认为,这个服务运行了 1.234 秒。实际上并不是这样。这里的 1.234 秒指的是CPU 时间(CPU time),而不是我们现实生活中理解的经过时间。要理解这个概念,就需要区分两个完全不同的时间:CPU 时间和墙上时间(Wall-clock time)。
什么是墙上时间?
墙上时间,英文叫 Wall-clock time,可以简单理解为“现实世界中的时间”。
假设我们启动一个程序,从开始运行到程序结束,墙上的钟从 10:00:00 走到了 10:01:00,那么这个程序的墙上时间就是 60 秒。
墙上时间关注的是:
从开始到结束,现实世界究竟过去了多长时间。
无论程序在这期间是在使用 CPU、等待磁盘、等待网络,还是因为某种原因暂时没有运行,这些时间都会计算在墙上时间里面。
例如一个程序运行 10 秒,其中只有 2 秒真正使用 CPU,剩下 8 秒都在等待网络,那么它的墙上时间仍然是 10 秒。
因此,墙上时间可以理解成我们平时最直观的“计时器时间”。
什么是 CPU 时间?
CPU 时间则完全不同。
CPU 时间关注的不是“程序存在了多久”,而是:
CPU 总共花了多少时间执行这个程序的代码。
还是刚才的例子。
一个程序从启动到结束一共经过 10 秒,但是这 10 秒里面,它可能只有 2 秒真正需要 CPU 计算,另外 8 秒都在等待网络响应。
那么:
墙上时间:10 秒
CPU 时间:2 秒
这并不矛盾。
因为程序虽然存在了 10 秒,但 CPU 并没有连续工作 10 秒。
在 Linux 服务中看到的:
CPU: 2.000s
通常就是在表达类似的含义:从服务启动到当前时刻,这个服务累计消耗了多少 CPU 执行时间。
因此,一个服务运行了几天,而 CPU 时间只有几十秒,是完全正常的。
例如一个长期运行的网络服务可能一直处于等待状态,只有收到请求时才短暂占用 CPU。它可能已经运行了 30 天,但累计 CPU 时间只有几分钟。
CPU 时间为什么甚至可以大于墙上时间?
这里还有一个非常有意思的地方:CPU 时间甚至可以大于墙上时间。
乍一看似乎不可能。
假设一个程序只运行了 10 秒,怎么可能使用了超过 10 秒的 CPU?
关键就在于:现代计算机通常拥有多个 CPU 核心。
假设一个程序运行了 10 秒,并且在这 10 秒里同时使用了两个 CPU 核心,而且两个核心都持续工作。
那么:
墙上时间:10 秒
CPU 0:10 秒
CPU 1:10 秒
CPU 时间:20 秒
现实世界只过去了 10 秒,但两个 CPU 核心各自工作了 10 秒,加起来就是 20 秒的 CPU 时间。
如果程序同时使用 4 个 CPU 核心并持续运行 10 秒,那么累计 CPU 时间理论上甚至可以接近:
10 × 4 = 40 秒
因此,墙上时间是现实世界流逝的时间,而 CPU 时间是 CPU 各个核心为程序工作的时间总和。
这也是为什么在多核系统中,CPU 时间完全可能大于墙上时间。
简单来说,可以记住一句话:
墙上时间告诉你“这个程序存在了多久”,CPU 时间告诉你“CPU 总共为它工作了多久”。
一个程序可以运行很久,却几乎不消耗 CPU;也可以运行很短时间,却因为同时使用多个 CPU 核心而产生很高的累计 CPU 时间。
理解了这两个概念,再去看 Linux 中 systemctl status、time、进程监控等工具显示的各种时间数据,就不容易把“程序运行了多久”和“CPU 工作了多久”混为一谈了。