从事Linux性能调优的朋友,一定对"CPU负载"和"CPU利用率"这两个术语不陌生,但许多人容易将它们混为一谈。实际上,虽然可以通过top命令同时获取这两个指标,它们的含义却截然不同——理清这一区别,是理解"磁盘缓慢为何导致负载飙升"的关键前提。
利用率(CPU utilization)统计的是程序运行期间实际占用CPU时间的百分比,可以理解为"CPU在某段时间内的忙碌程度"。若该数值长期居高,例如超过90%,基本可以判定CPU已趋于饱和。而负载(load a verage)则不同,它统计的是某段时间内正在使用CPU的进程数与等待CPU的进程数之和,即CPU队列的长度。这两个指标之间的微妙差异,常常产生令人困惑的现象。
利用率高并不必然意味着负载大。假设一个计算密集型任务持续让CPU满负荷运转,利用率自然居高不下,但队列中可能仅有它一个进程,负载反而偏低。反过来,利用率低时负载是否可能很高?答案是肯定的。例如程序大部分时间在等待IO,仅偶尔占用CPU,利用率不高,但等待IO的进程可能在队列中堆积,导致负载飙升。这正是问题的关键所在。

网络上有一个生动的电话亭类比:电话好比CPU,打电话的人好比进程。有人拿起电话直接通话,一分钟不浪费——这是CPU利用率高的进程;有人先翻半小时号码簿,再打半分钟——这是CPU利用率低的进程。无论怎么打,电话亭外排队的人数与内部通话效率并无直接关系。排队人多意味着负载高,人少则负载低。因此,仅凭CPU利用率根本无法判断后台排队规模。
CPU负载的计算天然受核心数量影响。一块双核CPU可同时处理2个任务,负载达到2.0才相当于满载;单核CPU负载超过1.0即饱和。因此业界常说的"有多少内核,就有多少负载上限"正是这个道理。在Linux系统中,可以通过/proc/cpuinfo文件查看CPU的物理标识、逻辑处理器、核心数等详细信息。例如下方输出片段中,cpu cores: 2表示物理CPU拥有两个核心。
$ cat /proc/cpuinfo processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 63 model name : Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz stepping : 2 microcode : 0x36 cpu MHz : 2399.998 cache size : 20480 KB physical id : 0 siblings : 2 core id : 0 cpu cores : 2 apicid : 0 initial apicid : 0 fpu : yes fpu_exception : yes cpuid level : 15 wp : yes ...(省略部分flags信息)
这里几个关键字段值得注意:processor表示逻辑CPU编号,model name是CPU型号,physical id对应物理CPU编号,cpu cores则是物理核心数。目前服务器普遍启用超线程技术,逻辑CPU数量往往是核心数的两倍。但超线程不等同于独立核心,在共享资源时仍可能产生等待,因此不能简单依据逻辑CPU数量来计算负载上限。
二、CPU负载率的计算方式
Load a verage概念起源于UNIX系统,各版本公式略有差异,但其核心都是统计当前"可运行进程"的数量——包括正在使用CPU的进程与等待CPU的进程。理论上,若该数值持续大于CPU核心数,说明CPU资源不足。然而在Linux环境中,情况远比这复杂。
Linux的load a verage统计中多了一类进程:uninterruptible sleep(不可中断睡眠)状态。这种状态通常出现在等待IO设备或网络时。Linux设计者原本认为这种睡眠非常短暂,进程很快会恢复运行,因此将其视为runnable(可运行)进程。问题在于,现实中的uninterruptible sleep并不一定短暂——当磁盘缓慢、IO出现瓶颈时,大量进程长时间处于此睡眠状态,负载数字会急剧攀升。但睡眠中的进程根本不需要CPU,即使所有CPU都空闲,这些进程也无法运行。因此,将uninterruptible sleep计入负载,本质上偏离了load a verage的原始意义。
正是这一设计差异,导致在Linux上观察load a verage常常失去参考价值。当你看到负载很高时,无法区分是"CPU不够用"还是"IO设备存在瓶颈"。反过来,这也解释了为什么磁盘慢时CPU负载会飙升:大量进程因等待磁盘IO进入uninterruptible sleep状态,队列长度暴增,负载直线上升,而CPU本身可能根本没有繁忙。实际运维中,CPU负载飙升的根源不外乎两类——要么CPU自身任务过多、软中断和上下文切换频繁;要么就是磁盘太慢,导致不可中断睡眠进程堆积。第二种情况通常更为隐蔽,也更容易被误判。
