Linux性能优化——基础篇(CPU性能)
平均负载
通过平均负载可以查看系统整体性能、负载情况。查看系统平均负载的方法,top、uptime命令。load average后分别是1、5、15分钟的平均负载。
10:08:20 up 232 days, 20:05, 1 user, load average: 0.02, 0.04, 0.03
平均负载含义
关于系统负载uptime命令给出的解释:
System load averages is the average number of processes that are either in a runnable or uninterruptable state.
A process in a runnable state is either using the CPU or waiting to use the CPU.
A process in uninterruptable state is waiting for some I/O access, eg waiting for disk.
The averages are taken over the three time intervals. Load averages are not normalized for the number of CPUs in a system,
so a load average of 1 means a single CPU system is loaded all the time while on a 4 CPU system it means it was idle 75% of the time.
平均负载也就是处于运行状态、不可中断状态的进程数(活跃进程数)。运行状态是说处于使用CPU或者等待使用CPU的进程,也就是R状态的进程。 不可中断中台是指进程处于内核态的关键流程中的进程,D状态的进程。例如1min平均负载为2,表示该时间段内活跃进程数为2,若是2核CPU,则表示所有CPU刚好被完全占用。
平均负载多大合适
由上述解释,平均负载等于CPU核数时最合适。可以通过top命令或者查看/proc/cpuinfo查看cpu核数。当平均负载大于CPU核数时表示系统已经过载。
关于平均负载的几个易错点
平均负载不等于CPU使用率。平均负载不仅包含正在使用CPU的进程,还包括等待CPU或是等待IO的进程。CPU密集型进程,两者基本相似,IO密集型进程即使平均负载升高,cpu使用率不一定很高。 同样大量等待CPU调度的进程也会使平均负载升高。
-
CPU密集型
使用
stress --cpu=1 --timeout 600
模拟CPU密集型进程,机器2核。某一时刻系统负载为:
load average: 1.02, 0.64, 0.38
使用
mpstat -P all 2
查看系统进程的详情。可以看到系统CPU使用率较高,IOWAIT几乎为0.Linux 4.18.0-305.3.1.el8.x86_64 (VM-32-17-centos) 2023年10月09日 _x86_64_ (2 CPU) 11时28分24秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle 11时28分26秒 all 54.80 0.00 1.52 0.00 0.00 0.00 0.00 0.00 0.00 43.69 11时28分26秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle 11时28分28秒 all 52.01 0.00 0.50 0.00 0.00 0.25 0.00 0.00 0.00 47.24
使用
pidstat -u 2
查看具体进程使用情况: 可以看到stress进程CPU使用率达100%,wait为0。可判断负载升高是由于stress进程CPU升高引起的。11时30分55秒 UID PID %usr %system %guest %wait %CPU CPU Command 11时30分57秒 0 11 0.00 0.50 0.00 2.00 0.50 1 rcu_sched 11时30分57秒 0 260337 0.50 0.00 0.00 0.00 0.50 1 redis-server 11时30分57秒 0 355574 0.00 0.50 0.00 0.00 0.50 0 milvus_server 11时30分57秒 0 1514698 0.50 0.50 0.00 0.00 1.00 0 barad_agent 11时30分57秒 0 2128959 0.50 0.00 0.00 0.00 0.50 0 YDService 11时30分57秒 0 2671991 99.50 0.00 0.00 0.00 99.50 0 stress 11时30分57秒 0 2773539 0.50 0.00 0.00 0.00 0.50 1 ld-linux-x86-64
-
IO密集型
上下文切换
以上,系统负载升高的原因可能是CPU使用率升高或者IO升高,另外进程竞争资源也会引发负载升高。本质是上下文切换占用了大量CPU,上下文切换是需要内核在CPU上运行的。
CPU上下文:CPU寄存器和程序计数器
CPU上下文切换: 将前一个任务的CPU上下文进行保存,加载新任务的CPU上下文到寄存器和程序计数器,然后跳转到程序计数器指向的位置。
任务区分的CPU上下文切换场景: 进程上下文切换、线程上下文切换、中断上下文切换
进程上下文切换
预备知识:
CPU特权等级: Ring 0 - Ring 3, Ring0具有最高权限,可以直接访问所有资源,Ring3只能访问受限资源,不能直接访问内存等硬件设备。
进程运行空间: 内核空间(Ring0)、用户空间(Ring3)
进程的内核态、用户态只是进程分别运行在内核空间、用户空间。
**用户态到内核态的切换是由系统调用完成的**
进程是由内核来管理和调度的,进程切换只发生在内核态。进程的上下文包括虚拟内存、栈、全局变量等用户态资源也包括内核堆栈、寄存器等内核空间状态。
CPU上下文切换与系统调用(特权模式切换)
一次系统调用涉及到从内核态、用户态的两次转变,所以需要先将原来用户态的寄存器内存和PC保存,然后加载内核态寄存器内容和PC,然后跳转到PC指向位置。 系统调用结束后,需要恢复原用户态内容,继续运行进程。也就是说一次系统调用涉及到2次CPU上下文切换。
系统调用与进程上下文切换
- 系统调用是在同一个进程内完成的,且不涉及到虚拟内存等进程用户态的资源。
- 进程上下文切换,因进程上下文涉及用户态、内核态资源,所以相比系统调用多了一步内核态资源的保存与恢复。
进程调度的时机
-
CPU时间片消耗完毕
-
进程所需的系统资源不足
-
进程通过sleep这样的方式主动将自己挂起
-
高优先级进程抢占
-
硬件中断
另外虚拟内存到物理内存的映射是通过TLB(Translation Lookaside Buffer)来管理的,虚拟内存刷新之后,TLB也需要刷新,内存访问速度会变慢, 在多处理器系统中缓存又是被共享的,所以其他处理器上的进程也会受影响。综上,进程上下文切换需要保存和恢复用户态、内核态资源,例如虚拟内存、用户栈、全局变量 CPU寄存器、PC,这些资源切换需要占用CPU时间;另一方面,TLB的刷新也降低了内存访问的速度。
线程上下文切换
- 不同进程的线程切换,与进程上下文切换类似
- 相同进程的线程切换,虚拟内存保持不动,只需要切换线程的私有数据、寄存器等不共享的数据
中断上下文切换
中断上下文: CPU寄存器、内核堆栈、硬件中断参数。不包含用户态的虚拟内存、全局变量等。
分析上下文切换
预备知识
自愿上下文切换: 进程无法获取所需资源,导致的上下文切换。例如I/O、内存等系统资源不足。
非自愿上下文切换: 进程由于时间片已到,被系统强制调度,例如大量进程竞争CPU。
也就是说资源上下文切换变多表明进程都在等待资源,很可能发生了IO等待;非资源上下文切换变多,说明进程因竞争CPU在被强制调度,很可能出现了CPU瓶颈。
另外,当中断次数变多时,需要查看/proc/interrupts 查看具体的中断类型。
分析工具
- vmstat, 查看系统整体的上下文切换情况
# vmstat -aw 1
procs -----------------------memory---------------------- ---swap-- -----io---- -system-- --------cpu--------
r b swpd free inact active si so bi bo in cs us sy id wa st
0 0 0 234176 4473348 2733180 0 0 0 34 1 1 1 1 99 0 0
关注cs(每秒上下文切换次数),in(每秒中断次数) 2. pidstat, 查看进程的上下文切换情况
# pidstat -w 1
Linux 4.18.0-305.3.1.el8.x86_64 (VM-32-17-centos) 2023年10月18日 _x86_64_ (2 CPU)
20时47分31秒 UID PID cswch/s nvcswch/s Command
20时47分33秒 0 11 164.50 0.00 rcu_sched
20时47分33秒 0 17 0.50 0.00 migration/1
20时47分33秒 0 18 0.50 0.00 ksoftirqd/1
cswch: 每秒自愿上下文切换 nvcswch: 每秒非自愿上下文切换
实例
- 使用sysbench模拟多线程情况,
sysbench --threads=10 --max-time=300 threads run
- 使用vmstat 查看前后系统表现。
# sysbench 运行之前
procs -----------------------memory---------------------- ---swap-- -----io---- -system-- --------cpu--------
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 0 0 221356 280044 5472176 0 0 0 0 1202 1813 1 0 99 0 0
1 0 0 221152 280044 5472176 0 0 0 0 1427 2172 1 1 99 0 0
# sysbench 运行之后
procs -----------------------memory---------------------- ---swap-- -----io---- -system-- --------cpu--------
r b swpd free buff cache si so bi bo in cs us sy id wa st
6 0 0 217984 280044 5472268 0 0 0 34 1 0 1 1 99 0 0
8 1 0 217944 280044 5472272 0 0 0 268 5095 1792665 22 76 2 0 0
procs指标中的就绪队列长度也超过了cpu核数2, sy的数值达到了80%,说明此时CPU只要消耗在内核上。而系统的上下文切换次数达到百万级别,同时系统中断也达到几千。 说明CPU上升很有可能是上下文切换导致的。 3. 使用pidstat分析进程、线程上下文切换
# pidstat -wut 1
Linux 4.18.0-305.3.1.el8.x86_64 (VM-32-17-centos) 2023年10月19日 _x86_64_ (2 CPU)
15时51分17秒 UID TGID TID %usr %system %guest %wait %CPU CPU Command
15时51分18秒 0 260343 - 0.00 0.96 0.00 0.00 0.96 1 redis-server
15时51分18秒 0 - 260367 0.96 0.00 0.00 0.00 0.96 1 |__redis-server
15时51分18秒 0 - 355584 0.96 0.00 0.00 0.00 0.96 1 |__wal_thread
15时51分18秒 0 1514698 - 0.96 0.00 0.00 0.00 0.96 0 barad_agent
15时51分18秒 0 - 2773570 0.96 0.00 0.00 0.00 0.96 1 |__Thread-0
15时51分18秒 0 2813962 - 39.42 151.92 0.00 0.00 191.35 1 sysbench
15时51分18秒 0 - 2813963 3.85 15.38 0.00 50.00 19.23 0 |__sysbench
15时51分18秒 0 - 2813964 4.81 15.38 0.00 47.12 20.19 0 |__sysbench
15时51分18秒 0 - 2813965 3.85 14.42 0.00 46.15 18.27 1 |__sysbench
15时51分18秒 0 - 2813966 3.85 15.38 0.00 52.88 19.23 1 |__sysbench
15时51分18秒 0 - 2813967 3.85 15.38 0.00 62.50 19.23 1 |__sysbench
15时51分18秒 0 - 2813968 3.85 15.38 0.00 44.23 19.23 1 |__sysbench
15时51分18秒 0 - 2813969 2.88 14.42 0.00 34.62 17.31 0 |__sysbench
15时51分18秒 0 - 2813970 4.81 14.42 0.00 38.46 19.23 0 |__sysbench
# 上下文切换情况
平均时间: UID TGID TID cswch/s nvcswch/s Command
16时03分37秒 0 - 2817624 29334.00 140991.00 |__sysbench
16时03分37秒 0 - 2817625 34078.00 126975.00 |__sysbench
16时03分37秒 0 - 2817626 30947.00 137324.00 |__sysbench
可见sysbench进程占用了接近2核的CPU,sy的CPU占用达到150, 从线程上可以看到__sysbench线程大多数时间消耗在等到CPU资源上. 4. 查看/proc/interrupts 分析中断情况
watch -d 'cat /proc/interrupts | sort -nr -k 2'
查看中断情况,发现 RES中断次数变化速度最快。而RES中断表示唤醒空闲状态的CPU来调度新的任务运行。所以这里中断升高的原因还是线程调度引发。
上线切换多大是合理的
CPU性能不一致,合理的上下文切换次数也就不一样,若是上下文切换次数在几百到上万 的范围内且十分稳定则认为是正常的。当上下文切换次数达到上万次或出现较块增加,就可能已经出现性能问题了。
总结
分析CPU性能问题时:
- 使用top、uptime大致查看系统整体的负载情况。
- 使用mpstat 查看系统级别的CPU消耗情况,判断是否存在CPU问题,着重查看usr、sy、iowait。使用vmstat查看系统级别上下文切换情况
- 使用pidstat 分析进程、线程级别CPU情况
- 查看/proc/interrupts 分析中断情况