linux性能调优实战笔记
在前面的两篇博客中,我们已经明白了什么是平均负载以及各种上下文切换对于整个平均负载的影响,如何去利用ps,pidstat,vmstat去进行排查。
下面我将讲解CPU的一个重要指标:CPU使用率
CPU使用率
CPU 使用率,就是除了空闲时间外的其他时间占总 CPU 时间的百分比
这个公式其实是CPU使用率的瞬时使用率,在真实的生产中,我们利用监控各种性能工具得到的数据一般都是CPU的平均使用率。
平均使用率反应的就是某个时间段的平均使用率。
在Linux 通过 /proc 虚拟文件系统,向用户空间提供了系统内部状态的信息,而 /proc/stat 提供的就是系统的 CPU 和任务统计信息。
如果我现在只关注CPU的使用率,那么可以执行下面的命令:
lighthouse@VM-24-16-ubuntu:~$ cat /proc/stat | grep cpu
cpu 1917571 19620 2132394 248986124 517605 0 22973 0 0 0
cpu0 965226 8791 1069708 124502149 255767 0 9154 0 0 0
cpu1 952345 10829 1062685 124483975 261838 0 13818 0 0 0
以上每一列的数据的说明我们可以使用man proc进行查看。我们不必每一行的意思都要记住,运用man命令可以帮助我们更好得去理解。
下面是一些重要的数据指标
-
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的使用率,我们一般第一时间就会考虑ps和top这两个命令。
top 和 ps 是最常用的性能分析工具:
- top 显示了系统总体的 CPU 和内存使用情况,以及各个进程的资源使用情况。
- ps 则只显示了每个进程的资源使用情况。
lighthouse@VM-24-16-ubuntu:~$ top
top - 10:39:23 up 14 days, 17:36, 1 user, load average: 0.05, 0.09, 0.08
Tasks: 113 total, 1 running, 112 sleeping, 0 stopped, 0 zombie
top - 10:39:30 up 14 days, 17:36, 1 user, load average: 0.05, 0.09, 0.08
Tasks: 113 total, 1 running, 112 sleeping, 0 stopped, 0 zombie
%Cpu(s): 1.7 us, 3.2 sy, 0.0 ni, 95.0 id, 0.2 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 1963.7 total, 156.9 free, 334.1 used, 1472.7 buff/cache
MiB Swap: 0.0 total, 0.0 free, 0.0 used. 1439.7 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
273925 root 20 0 1028200 75824 32120 S 2.0 3.8 68:36.09 YDService
8467 root 20 0 1103112 25856 4844 S 1.0 1.3 170:11.03 barad_agent
8466 root 20 0 46716 11852 3292 S 0.3 0.6 14:58.56 barad_agent
3507014 root 20 0 0 0 0 I 0.3 0.0 0:02.25 kworker/1:1-events
3574311 lightho+ 20 0 7504 3736 3372 S 0.3 0.2 0:01.49 orca-data.sh
1 root 20 0 102096 11076 6328 S 0.0 0.6 0:23.05 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.13 kthreadd
3 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 rcu_gp
4 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 rcu_par_gp
5 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 slub_flushwq
6 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 netns
8 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 kworker/0:0H-events_highpri
10 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 mm_percpu_wq
11 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcu_tasks_rude_
12 root 20 0 0 0 0 S 0.0 0.0 0:00.00 rcu_tasks_trace
13 root 20 0 0 0 0 S 0.0 0.0 0:22.18 ksoftirqd/0
14 root 20 0 0 0 0 I 0.0 0.0 1:31.94 rcu_sch
top命令的第三就是关于Cpu的使用率,当我们在这个界面按下数字1时,我们可以观察到所用CPU的使用状态,它是用户态和内核态 CPU 使用率的总和,包括进程用户空间使用的 CPU、通过系统调用执行的内核空间 CPU 、以及在就绪队列等待运行的 CPU。在虚拟化环境中,它还包括了运行虚拟机占用的 CPU。
但是top中没有办法去细分进程中的用户态 CPU 和内核态 CPU。我们如果想要进一步查看进行中CPU的使用率,我们就要使用我们之前讲过的pidstat命令,它是分析每个进程 CPU 使用情况的工具。
比如,下面的 pidstat 命令,就间隔 1 秒展示了进程的 5 组 CPU 使用率,包括:
- 用户态 CPU 使用率 (%usr);
- 内核态 CPU 使用率(%system);
- 运行虚拟机 CPU 使用率(%guest);
- 等待 CPU 使用率(%wait);
- 以及总的 CPU 使用率(%CPU)。
最后的 Average 部分,还计算了 5 组数据的平均值。
lighthouse@VM-24-16-ubuntu:~$ pidstat 1 5
Linux 5.15.0-106-generic (VM-24-16-ubuntu) 06/13/2024 _x86_64_ (2 CPU)
10:49:10 AM UID PID %usr %system %guest %wait %CPU CPU Command
10:49:11 AM 0 8467 1.00 0.00 0.00 0.00 1.00 1 barad_agent
10:49:11 AM 0 273925 1.00 0.00 0.00 0.00 1.00 1 YDService
10:49:11 AM UID PID %usr %system %guest %wait %CPU CPU Command
10:49:12 AM 0 8467 0.00 1.00 0.00 0.00 1.00 1 barad_agent
10:49:12 AM 0 273925 1.00 2.00 0.00 0.00 3.00 1 YDService
10:49:12 AM UID PID %usr %system %guest %wait %CPU CPU Command
10:49:13 AM 0 8467 0.00 1.00 0.00 0.00 1.00 1 barad_agent
10:49:13 AM 0 273925 0.00 1.00 0.00 0.00 1.00 1 YDService
10:49:13 AM 0 3567198 1.00 0.00 0.00 0.00 1.00 1 kworker/u4:2-ext4-rsv-conversion
10:49:13 AM 0 3658188 1.00 0.00 0.00 0.00 1.00 0 kworker/u4:0-events_power_efficient
10:49:13 AM UID PID %usr %system %guest %wait %CPU CPU Command
10:49:14 AM 0 8467 1.00 0.00 0.00 0.00 1.00 1 barad_agent
10:49:14 AM 0 8793 1.00 0.00 0.00 1.00 1.00 1 tat_agent
10:49:14 AM 0 273925 2.00 1.00 0.00 0.00 3.00 1 YDService
10:49:14 AM 1001 3667323 1.00 1.00 0.00 0.00 2.00 0 pidstat
10:49:14 AM UID PID %usr %system %guest %wait %CPU CPU Command
10:49:15 AM 0 273925 1.00 0.00 0.00 0.00 1.00 1 YDService
10:49:15 AM 0 273974 0.00 1.00 0.00 0.00 1.00 0 YDLive
CPU 使用率过高怎么办?
当我们通过 top、ps、pidstat 等工具,你能够轻松找到 CPU 使用率较高(比如 100% )的进程。接下来,我们就要找占用 CPU 的到底是代码里的哪个函数呢?找到它,才能更高效、更针对性地进行优化。
我猜你第一个想到的,应该是 GDB(The GNU Project Debugger), 这个功能强大的程序调试利器。的确,GDB 在调试程序错误方面很强大。但是,我又要来“挑刺”了。请你记住,GDB 并不适合在性能分析的早期应用。为什么呢?
因为 GDB 调试程序的过程会中断程序运行,这在线上环境往往是不允许的。所以,GDB 只适合用在性能分析的后期,当你找到了出问题的大致函数后,线下再借助它来进一步调试函数内部的问题。
那么哪种工具适合在第一时间分析进程的 CPU 问题呢?
推荐是 perf。perf 是 Linux 2.6.31 以后内置的性能分析工具。它以性能事件采样为基础,不仅可以分析系统的各种事件和内核性能,还可以用来分析指定应用程序的性能问题。使用 perf 分析 CPU 性能问题。
第一种常见用法是 perf top,类似于 top,它能够实时显示占用 CPU 时钟最多的函数或者指令,因此可以用来查找热点函数,使用界面如下所示:
lighthouse@VM-24-16-ubuntu:~$ perf top
Samples: 3K of event 'cpu-clock:pppH', 4000 Hz, Event count (approx.): 712919059 lost: 0/0 drop: 0/0
Overhead Shared Object Symbol
4.32% [kernel] [k] finish_task_switch.isra.0
3.16% perf [.] rb_next
2.80% [kernel] [k] kallsyms_expand_symbol.constprop.0
2.63% [kernel] [k] __lock_text_start
2.14% perf [.] kallsyms__parse
1.40% [kernel] [k] number
1.35% snapd [.] 0x00000000002ff56e
1.32% [kernel] [k] format_decode
1.08% [kernel] [k] vsnprintf
1.04% perf [.] evsel__parse_sample
0.99% [kernel] [k] module_get_kallsym
0.99% perf [.] __dso__load_kallsyms
0.96% perf [.] map__process_kallsym_symbol
0.94% perf [.] rust_demangle_callback
0.93% perf [.] deliver_event
0.92% [kernel] [k] default_idle_call
0.87% snapd [.] 0x00000000002ff569
0.82% [kernel] [k] seq_read_iter
0.78% [kernel] [k] memcpy_erms
输出结果中,第一行包含三个数据,分别是采样数(Samples)、事件类型(event)和事件总数量(Event count)。比如这个例子中,perf 总共采集了 3k个 CPU 时钟事件,而总事件数则为 712919059 。另外,采样数需要我们特别注意。如果采样数过少(比如只有十几个),那下面的排序和百分比就没什么实际参考价值了。
我们看上面的输出,占比最高的是一个[kernel] 中的进程才4.32%,说明CPU的性能上没有什么问题。
接着再来看第二种常见用法,也就是 perf record 和 perf report。 perf top 虽然实时展示了系统的性能信息,但它的缺点是并不保存数据,也就无法用于离线或者后续的分析。而 perf record 则提供了保存数据的功能,保存后的数据,需要你用 perf report 解析展示。
可以理解为一个单位时间内的性能报告。
root@VM-24-16-ubuntu:~# perf record
^C[ perf record: Woken up 5 times to write data ]
[ perf record: Captured and wrote 1.438 MB perf.data (23452 samples) ]
root@VM-24-16-ubuntu:~# perf report
在实际使用中,我们还经常为 perf top 和 perf record 加上 -g 参数,开启调用关系的采样,方便我们根据调用链来分析性能问题。