CPU使用率是单位时间内CPU使用情况的统计,以百分比的方式展示。
CPU时间是Linux通过事先定义的节拍率(内核中表示为 HZ),触发时间中断,并使用全局变量Jiffies记录了开机以来的节拍数。每发生一次时间中断,Jiffies的值就加 1。 节拍率 HZ 是内核的可配选项,可以设置为100、250、1000等。不同的系统可能设置不同数值,你可以通过查询 /boot/config 内核选项来查看它的配置值。比如在我的系统中,节拍率设置成了1000,也就是每秒钟触发1000次时间中断。
# grep 'CONFIG_HZ=' /boot/config-$(uname -r)
CONFIG_HZ=1000
同时,正因为节拍率HZ是内核选项,所以用户空间程序并不能直接访问。为了方便用户空间程序,内核还提供了一个用户空间节拍率 USER_HZ,它总是固定为100,也就是1/100 秒。这样,用户空间程序并不需要关心内核中HZ被设置成了多少,因为它看到的总是固定值USER_HZ。
CPU使用率相关的重要指标
- user(通常缩写为us),代表用户态CPU时间。注意,它不包括下面的nice时间,但包括了guest时间。
- nice(通常缩写为ni),代表低优先级用户态CPU时间,也就是进程的nice值被调整为1-19之间时的CPU时间。这里注意,nice可取值范围是-20到19,数值越大,优先级反而越低。
- system(通常缩写为sys),代表内核态CPU时间。
- idle(通常缩写为 id),代表空闲时间。注意,它不包括等待I/O的时间(iowait)。
- iowait(通常缩写为 wa),代表等待I/O的CPU时间。
- irq(通常缩写为 hi),代表处理硬中断的CPU时间。
- softirq(通常缩写为 si),代表处理软中断的CPU时间。
- steal(通常缩写为 st),代表当系统运行在虚拟机中的时候,被其他虚拟机占用的CPU 时间。
- guest(通常缩写为 guest),代表通过虚拟化运行其他操作系统的时间,也就是运行虚拟机的 CPU 时间。
- guest_nice(通常缩写为 gnice),代表以低优先级运行虚拟机的时间。
指标异常的一些场景:
- 用户CPU和Nice CPU高,说明用户态进程占用了较多的CPU,所以应该着重排查进程的性能问题。
- 系统CPU高,说明内核态占用了较多的CPU,所以应该着重排查内核线程或者系统调用的性能问题。
- I/O等待CPU高,说明等待I/O的时间比较长,所以应该着重排查系统存储是不是出现了I/O问题。
- 软中断和硬中断高,说明软中断或硬中断的处理程序占用了较多的CPU,所以应该着重排查内核中的中断服务程序。
cpu使用率
CPU使用率,就是除了空闲时间外的其他时间占总CPU时间的百分比,用公式来表示就是:
空闲时间
CPU使用率 = 1 - --------------
总cpu时间
通过查看/proc/stat
,我们根据公式可以计算出系统cpu使用率。但是这里面的数值是开机以来的累积数据,计算出来的cpu使用率是开机到现在的cpu使用率。
我们计算cpu使用率,一般是选择一段时间内的cpu使用率,比如top使用的是3秒内的数据,而ps则用的是进程的生命周期数据。
计算一段时间内的cpu使用率的公式:
空闲时间new - 空闲时间old
CPU使用率 = 1 - ------------------------------
总cpu时间new - 总cpu时间old
进程的cpu使用率是通过/proc/[pid]/stat
文件内的数据来计算的。
怎么查看cpu使用率?
使用一些性能工具就能看到。
- top 显示了系统总体的 CPU 和内存使用情况,以及各个进程的资源使用情况。
- ps 则只显示了每个进程的资源使用情况。
- pidstat 显示了用户态和内核态的cpu使用率
# 默认每3秒刷新一次
# top
top - 19:33:00 up 6:11, 5 users, load average: 0.00, 0.01, 0.05
Tasks: 100 total, 1 running, 99 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 999720 total, 671020 free, 102360 used, 226340 buff/cache
KiB Swap: 10485756 total, 10485756 free, 0 used. 722092 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 43668 4124 2488 S 0.0 0.4 0:01.18 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.01 kthreadd
3 root 20 0 0 0 0 S 0.0 0.0 0:15.86 ksoftirqd/0
5 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 kworker/0:0H
7 root rt 0 0 0 0 S 0.0 0.0 0:02.79 migration/0
8 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcu_bh
9 root 20 0 0 0 0 S 0.0 0.0 0:01.40 rcu_sched
...
这个输出结果中,第三行%Cpu就是系统的CPU使用率,具体每一列的含义上一节都讲过,只是把CPU时间变换成了CPU使用率,我就不再重复讲了。不过需要注意,top默认显示的是所有CPU的平均值,这个时候你只需要按下数字1 ,就可以切换到每个CPU的使用率了。
继续往下看,空白行之后是进程的实时信息,每个进程都有一个%CPU列,表示进程的CPU 使用率。它是用户态和内核态CPU使用率的总和,包括进程用户空间使用的CPU、通过系统调用执行的内核空间CPU 、以及在就绪队列等待运行的CPU。在虚拟化环境中,它还包括了运行虚拟机占用的CPU。
# 每隔1秒输出一组数据,共输出2组
# pidstat 1 2
Linux 3.10.0-693.el7.x86_64 (node74-1) 2019年06月16日 _x86_64_ (2 CPU)
19时36分16秒 UID PID %usr %system %guest %wait %CPU CPU Command
19时36分17秒 0 13528 0.00 0.99 0.00 0.00 0.99 0 kworker/0:0
19时36分17秒 0 13745 0.00 0.99 0.00 0.00 0.99 1 pidstat
19时36分17秒 UID PID %usr %system %guest %wait %CPU CPU Command
19时36分18秒 0 950 0.99 0.00 0.00 0.00 0.99 1 tuned
19时36分18秒 0 13745 0.00 0.99 0.00 0.00 0.99 1 pidstat
平均时间: UID PID %usr %system %guest %wait %CPU CPU Command
平均时间: 0 950 0.50 0.00 0.00 0.00 0.50 - tuned
平均时间: 0 13528 0.00 0.50 0.00 0.00 0.50 - kworker/0:0
平均时间: 0 13745 0.00 0.99 0.00 0.00 0.99 - pidstat
部分指标含义:
- 用户态 CPU 使用率 (%usr)
- 内核态 CPU 使用率(%system)
- 运行虚拟机 CPU 使用率(%guest)
- 等待 CPU 使用率(%wait)
- 总的 CPU 使用率(%CPU)
CPU 使用率过高怎么办?
通过top
,ps
,pidstat
我们可以很容易的找到使用cpu高的进程,找到进程后,下一步该做什么呢?
- 使用GDB(The GNU Project Debugger)来调试程序,找出问题点。但是GDB会在程序中打上断点,影响业务正常工作。
- 使用perf分析CPU性能问题,它以性能事件采样为基础,不仅可以分析系统的各种事件和内核性能,还可以用来分析指定应用程序的性能问题。
# yum -y install perf
Samples: 106 of event 'cpu-clock', Event count (approx.): 26160156
Overhead Shared Object Symbol
56.38% [kernel] [k] mpt_put_msg_frame
8.97% [kernel] [k] _raw_spin_unlock_irqrestore
3.82% [kernel] [k] _raw_qspin_lock
3.82% [kernel] [k] xfs_iflush_int
3.82% [kernel] [k] xfs_inode_buf
...
输出结果中,第一行包含三个数据,分别是采样数(Samples)、事件类型(event)和事件总数量(Event count)。比如这个例子中,perf总共采集了106个CPU时钟事件,而总事件数则为26160156
- 第一列 Overhead ,是该符号的性能事件在所有采样中的比例,用百分比来表示。
- 第二列 Shared ,是该函数或指令所在的动态共享对象(Dynamic Shared Object),如内核、进程名、动态链接库名、内核模块名等。
- 第三列 Object ,是动态共享对象的类型。比如 [.] 表示用户空间的可执行程序、或者动态链接库,而 [k] 则表示内核空间。
- 最后一列 Symbol 是符号名,也就是函数名。当函数名未知时,用十六进制的地址来表示。
# perf record # 先进行采样
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.232 MB perf.data (1762 samples) ]
# perf report # 对采样的数据生成报告,跟perf top一样
案例
还是之前模拟多进程的例子
使用stress模拟多进程
# -c 指定几个进程,-t指定时间
# stress -c 2 -t 600
stress: info: [13786] dispatching hogs: 2 cpu, 0 io, 0 vm, 0 hdd
使用top查看cpu使用率
# top
top - 19:53:34 up 6:31, 6 users, load average: 1.67, 0.62, 0.26
Tasks: 105 total, 3 running, 102 sleeping, 0 stopped, 0 zombie
%Cpu0 :100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 :100.0 us, 0.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 999720 total, 631232 free, 107600 used, 260888 buff/cache
KiB Swap: 10485756 total, 10485756 free, 0 used. 710932 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
13788 root 20 0 7264 96 0 R 99.7 0.0 1:42.94 stress
13787 root 20 0 7264 96 0 R 99.3 0.0 1:43.02 stress
...
从上面可以看到是stress进程导致cpu使用率比较高。
使用perf查看进程的哪个函数导致cpu上升的
# -g 开启调用关系分析,-p 指定进程id
# perf record -g -p 13788
^C[ perf record: Woken up 4 times to write data ]
[ perf record: Captured and wrote 0.966 MB perf.data (15691 samples) ]
# perf report
Samples: 15K of event 'cpu-clock', Event count (approx.): 3922750000
Children Self Command Shared Object Symbol
+ 36.33% 36.32% stress libc-2.17.so [.] __random_r
+ 30.27% 30.20% stress libc-2.17.so [.] __random
+ 12.40% 12.40% stress libc-2.17.so [.] rand
+ 9.80% 9.78% stress stress [.] 0x0000000000002dc1
+ 4.31% 4.31% stress stress [.] 0x0000000000001000
从输出结果可以看到是stress的__random
随机数生成函数导致cpu上升的。 使用回车键可以看到引用内容。