1. 概述
在进行资源分析时,通常我们优先关注资源的使用率,而不是负载的具体数值。对于上下文切换,其中有一种称为非自愿上下文切换的情况,通常发生在进程或线程的时间片耗尽时,操作系统会强制其释放 CPU。需要注意的是,时间片的长度与节拍率(HZ)有关,它是衡量时间片的单位。节拍率 HZ 是内核的可配选项,可以设置为 100、250、1000 等。不同的系统可能设置不同数值,你可以通过查询 /boot/config 内核选项来查看它的配置值。查看节拍率命令如下:
$ grep 'CONFIG_HZ=' /boot/config-$(uname -r)
CONFIG_HZ=250
Linux 通过 /proc 虚拟文件系统,向用户空间提供了系统内部状态的信息,而 /proc/stat 提供的就是系统的 CPU 和任务统计信息。比方说,如果你只关注 CPU 的话,可以执行下面的命令:
# 只保留各个CPU的数据
$ cat /proc/stat | grep ^cpu
cpu 280580 7407 286084 172900810 83602 0 583 0 0 0
cpu0 144745 4181 176701 86423902 52076 0 301 0 0 0
cpu1 135834 3226 109383 86476907 31525 0 282 0 0 0
常见的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 使用率,就是除了空闲时间外的其他时间占总 CPU 时间的百分比
查看CPU使用率
- top 显示了系统总体的 CPU 和内存使用情况,以及各个进程的资源使用情况。
- ps 则只显示了每个进程的资源使用情况。
使用top命令
# 默认每3秒刷新一次
$ top
top - 11:58:59 up 9 days, 22:47, 1 user, load average: 0.03, 0.02, 0.00
Tasks: 123 total, 1 running, 72 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.3 us, 0.3 sy, 0.0 ni, 99.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 8169348 total, 5606884 free, 334640 used, 2227824 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 7497908 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 78088 9288 6696 S 0.0 0.1 0:16.83 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.05 kthreadd
4 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/0:0H
...
使用pidstat命令
# 每隔1秒输出一组数据,共输出5组
$ pidstat 1 5
15:56:02 UID PID %usr %system %guest %wait %CPU CPU Command
15:56:03 0 15006 0.00 0.99 0.00 0.00 0.99 1 dockerd
...
Average: UID PID %usr %system %guest %wait %CPU CPU Command
Average: 0 15006 0.00 0.99 0.00 0.00 0.99 - dockerd
如何找到使用CPU高的函数?GDB可以分析,不过这个工具一般不会再生产上使用,因为会影响程序运行。生产环境一般使用perf命令
$ perf top
Samples: 833 of event 'cpu-clock', Event count (approx.): 97742399
Overhead Shared Object Symbol
7.28% perf [.] 0x00000000001f78a4
4.72% [kernel] [k] vsnprintf
4.32% [kernel] [k] module_get_kallsym
3.65% [kernel] [k] _raw_spin_unlock_irqrestore
...
指标解释
| 列名 | 含义 |
|---|---|
| Overhead | 性能事件在所有采样中的比例,以百分比表示。 |
| Shared | 动态共享对象(Dynamic Shared Object),包括内核、进程名、动态链接库名、内核模块名等。 |
| Object | 动态共享对象的类型:- [.]表示用户空间的可执行程序或动态链接库。- [k]表示内核空间。 |
| Symbol | 符号名(函数名)。若函数名未知,则显示十六进制地址。 |
perf top像top一样,只实时展示数据,并不保存数据,下面可以使用perf record收集数据
$ perf record # 按Ctrl+C终止采样
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.452 MB perf.data (6093 samples) ]
$ perf report # 展示类似于perf top的报告
加 -g 显示调用关系
2. 案例
- 机器配置:2 CPU,8GB 内存
- 预先安装 docker、sysstat、perf、ab 等工具,如 apt install docker.io sysstat linux-tools-common apache2-utils
准备两台虚拟机
配置docker镜像配置文件
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://docker.1ms.run",
"https://docker.1panel.live/"
]
}
EOF
systemctl daemon-reload
systemctl restart docker
VM1 终端 1:
$ docker run --name nginx -p 10000:80 -itd feisky/nginx
$ docker run --name phpfpm -itd --network container:nginx feisky/php-fpm
VM 2终端 1:
# 172.23.28.83是第一台虚拟机的IP地址
$ curl http://172.23.28.83:10000/
It works!
VM2 终端:
# 并发10个请求测试Nginx性能,总共测试100个请求
$ ab -c 10 -n 100 http://172.23.28.83:10000/
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd,
...
Requests per second: 11.63 [#/sec] (mean)
Time per request: 859.942 [ms] (mean)
...
继续测试
$ ab -c 10 -n 10000 http://172.23.28.83:10000/
回到VM1执行top,并输入1
通过perf继续查看是php-fpm的哪个函数导致的
# -g开启调用关系分析,-p指定php-fpm的进程号21515
$ perf top -g -p 20583
清理环境
docker rm -f nginx phpfpm
3. 小结
3.1. CPU 使用率:性能问题排查的第一关注点
CPU 使用率是系统性能的最直观和常用的指标,也是性能排查的首要关注点。理解各类 CPU 使用率的含义,对定位问题至关重要。以下是常见的 CPU 使用率类型及其意义:
- 用户 CPU(%user)和 Nice CPU(%nice)
-
- 用户态进程占用较多的 CPU。
- 排查重点:进程性能问题,例如高计算量的程序或异常负载的应用。
- 系统 CPU(%system)
-
- 内核态占用较多的 CPU。
- 排查重点:内核线程或系统调用的性能问题。
- 等待 I/O(%iowait)
-
- CPU 空闲,但等待 I/O 操作完成。
- 排查重点:存储性能问题,例如磁盘 I/O 的瓶颈。
- 中断(%irq)和软中断(%softirq)
-
- CPU 被硬中断或软中断的处理程序占用。
- 排查重点:中断服务程序,如网络、存储驱动等内核模块。
3.2. 性能问题排查思路
- 确认 CPU 使用率来源
使用top、pidstat等工具查看各进程或线程的 CPU 占用情况:
-
top:实时显示系统资源使用情况。pidstat:按进程或线程统计 CPU 使用率,定位高 CPU 使用的具体进程或线程。
- 深入分析问题根因
使用perf等性能分析工具,进一步排查具体函数或系统调用:
-
- 检测高 CPU 消耗的热点函数。
- 确认系统调用或内核线程的异常行为。
- 针对性优化
-
- 用户态问题:优化代码逻辑、降低计算复杂度。
- 内核态问题:调整系统参数或升级相关驱动。
- I/O 问题:优化存储性能或调整 I/O 调度策略。
- 中断问题:优化设备配置或减少中断处理开销。
通过以上方法,可以快速定位并解决 CPU 使用率升高导致的性能问题。
附录