Linux Performance

Linux Performance

Linux Performance

性能优化相关 from nipengfei https://time.geekbang.org/column/article/69859

CPU

什么是平均负载load

是指单位时间内系统平均活跃进程数(可运行状态和不可中断状态)

平均负载如果大于cpu个数,则代表系统存在争用关系

平均负载load并不是和cpu使用率

感兴趣可以看下load的代码loadavg.c

介绍load的一篇文章

场景1 cpu计算密集型

1
stress --cpu 1 --timeout 600
1
watch -d uptime

uptime的1m load会发现升高

通过mpstat查看多核cpu情况

1
mpstat -P ALL 5

发现存在一个cpu为100%的情况,但是iowait是0%,说明是cpu使用导致的load升高

通过pidstat -u 5 1

1
2
3
pidstat -u 5 1
平均时间:     0    118192    0.00    0.20    0.00    0.00    0.20     -  kworker/u32:1-events_unbound
平均时间:     0    127220   99.80    0.00    0.00    0.00   99.80     -  stress

可以很明显找到是stress导致的cpu飙高

场景2 io密集型

1
stress -i 1 --timeout 600

此时使用watch -d uptime发现uptime升高到2.几了

1
2
3
4
watch -d uptime
Every 5.0s: uptime                                                            liuliancao: Sun Aug 14 22:54:13 2022

 22:54:13 up  3:52,  1 user,  load average: 2.70, 1.88, 1.84

观察mpstat

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
mpstat -P ALL 5
22时55分35秒  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
22时55分40秒  all    1.97    0.00    0.72    2.85    0.00    0.06    0.00    0.00    0.00   94.40
22时55分40秒    0    2.78    0.00    0.20    0.79    0.00    0.60    0.00    0.00    0.00   95.63
22时55分40秒    1    0.80    0.00    1.00    0.20    0.00    0.20    0.00    0.00    0.00   97.80
22时55分40秒    2    3.64    0.00    0.61    0.20    0.00    0.00    0.00    0.00    0.00   95.56
22时55分40秒    3    5.99    0.00    0.40    0.20    0.00    0.00    0.00    0.00    0.00   93.41
22时55分40秒    4    3.05    0.00    0.41    0.00    0.00    0.00    0.00    0.00    0.00   96.54
22时55分40秒    5    0.80    0.00    1.41    1.21    0.00    0.00    0.00    0.00    0.00   96.58
22时55分40秒    6    0.60    0.00    0.40    0.00    0.00    0.00    0.00    0.00    0.00   98.99
22时55分40秒    7    0.20    0.00    1.21    1.21    0.00    0.00    0.00    0.00    0.00   97.38
22时55分40秒    8    2.80    0.00    0.60    0.00    0.00    0.20    0.00    0.00    0.00   96.40
22时55分40秒    9    1.40    0.00    0.40    0.20    0.00    0.00    0.00    0.00    0.00   98.00
22时55分40秒   10    3.85    0.00    0.81   14.17    0.00    0.00    0.00    0.00    0.00   81.17
22时55分40秒   11    1.21    0.00    0.81    3.63    0.00    0.00    0.00    0.00    0.00   94.35
22时55分40秒   12    1.60    0.00    1.40    0.00    0.00    0.00    0.00    0.00    0.00   96.99
22时55分40秒   13    1.01    0.00    0.40    0.00    0.00    0.00    0.00    0.00    0.00   98.59
22时55分40秒   14    0.61    0.00    0.61   11.74    0.00    0.00    0.00    0.00    0.00   87.04
22时55分40秒   15    1.21    0.00    0.80   12.27    0.00    0.00    0.00    0.00    0.00   85.71

发现cpu没有明显升高,但是iowait存在比较高的情况,load升高是因为io升高导致

使用pidstat查看下

1
2
pidstat -u 5 1
平均时间:     0    133410    0.20    3.40    0.00    0.40    3.60     -  stress

发现还是stress导致的

场景3 大量进程的情况

stress模拟8个cpu使用的情况

1
2
3
4
stress -c 20 --timeout 600
Every 5.0s: uptime                                                            liuliancao: Sun Aug 14 23:02:38 2022

 23:02:38 up  4:01,  1 user,  load average: 6.72, 3.40, 2.44

可以发现load 1m 飙高到接近8,load 5m小于1m表示负载是最近开始飙高,

使用mpstat -P ALL 5查看

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
07时24分49秒  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
07时24分54秒  all   99.74    0.00    0.25    0.00    0.00    0.01    0.00    0.00    0.00    0.00
07时24分54秒    0  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
07时24分54秒    1   99.80    0.00    0.20    0.00    0.00    0.00    0.00    0.00    0.00    0.00
07时24分54秒    2   99.60    0.00    0.40    0.00    0.00    0.00    0.00    0.00    0.00    0.00
07时24分54秒    3   99.80    0.00    0.20    0.00    0.00    0.00    0.00    0.00    0.00    0.00
07时24分54秒    4  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
07时24分54秒    5   99.80    0.00    0.00    0.00    0.00    0.20    0.00    0.00    0.00    0.00
07时24分54秒    6  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
07时24分54秒    7   99.80    0.00    0.20    0.00    0.00    0.00    0.00    0.00    0.00    0.00
07时24分54秒    8   99.80    0.00    0.20    0.00    0.00    0.00    0.00    0.00    0.00    0.00
07时24分54秒    9   99.60    0.00    0.40    0.00    0.00    0.00    0.00    0.00    0.00    0.00
07时24分54秒   10   98.20    0.00    1.80    0.00    0.00    0.00    0.00    0.00    0.00    0.00
07时24分54秒   11   99.60    0.00    0.40    0.00    0.00    0.00    0.00    0.00    0.00    0.00
07时24分54秒   12  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
07时24分54秒   13   99.80    0.00    0.20    0.00    0.00    0.00    0.00    0.00    0.00    0.00
07时24分54秒   14  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00
07时24分54秒   15  100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00

发现大部分cpu都在工作,

使用pidstat -u 5 1查看

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
平均时间:   UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
平均时间:     0         1    0.80    0.80    0.00    0.60    1.59     -  systemd
平均时间:     0        50    0.20    0.00    0.00    0.00    0.20     -  ksoftirqd/7
平均时间:     0        75    0.00    0.20    0.00    0.20    0.20     -  ksoftirqd/12
平均时间:     0       102    0.00    0.20    0.00    0.00    0.20     -  kworker/u32:9-events_unbound
平均时间:     0       103    0.00    0.20    0.00    0.00    0.20     -  kworker/u32:10-i915
平均时间:     0       105    0.00    0.20    0.00    0.00    0.20     -  kworker/u32:12-mt76
平均时间:     0       597    0.40    0.00    0.00    0.60    0.40     -  systemd-journal
平均时间:     0       613    0.20    0.00    0.00    0.20    0.20     -  systemd-udevd
平均时间:     0      1275    0.00    0.20    0.00    0.00    0.20     -  acpid
平均时间:   105      1283    0.60    0.00    0.00    1.19    0.60     -  dbus-daemon
平均时间:     0      1324    0.40    0.00    0.00    0.00    0.40     -  sunloginclient
平均时间:     0      1366    0.20    0.00    0.00    0.00    0.20     -  NetworkManager
平均时间:     0      1401    0.80    2.39    0.00    0.20    3.18     -  atop
平均时间:     0      2575    0.20    0.00    0.00    0.00    0.20     -  lxd
平均时间:  1000      3205    0.40    0.00    0.00   64.81    0.40     -  tracker-miner-f
平均时间:  1000      3250    0.99    0.60    0.00    0.60    1.59     -  Xorg
平均时间:  1000      3435    1.39    0.00    0.00    0.80    1.39     -  gnome-shell
平均时间:     0      4205    0.00    0.20    0.00    0.40    0.20     -  bettercap
平均时间:  1000      4921    0.80    0.20    0.00    0.40    0.99     -  gnome-terminal-
平均时间:     0      5038   77.93    0.00    0.00   21.27   77.93     -  stress
平均时间:     0      5039   96.42    0.00    0.00    2.78   96.42     -  stress
平均时间:     0      5040   79.92    0.00    0.00   19.28   79.92     -  stress
平均时间:     0      5041   85.49    0.00    0.00   13.92   85.49     -  stress
平均时间:     0      5042   94.23    0.00    0.00    4.97   94.23     -  stress
平均时间:     0      5043   80.32    0.00    0.00   18.69   80.32     -  stress
平均时间:     0      5044   80.52    0.20    0.00   18.49   80.72     -  stress
平均时间:     0      5045   69.18    0.00    0.00   30.22   69.18     -  stress
平均时间:     0      5046   71.17    0.00    0.00   28.23   71.17     -  stress
平均时间:     0      5047   81.51    0.00    0.00   17.89   81.51     -  stress
平均时间:     0      5048   83.50    0.00    0.00   15.71   83.50     -  stress
平均时间:     0      5049   65.61    0.00    0.00   33.60   65.61     -  stress
平均时间:     0      5050   68.19    0.00    0.00   31.41   68.19     -  stress
平均时间:     0      5051   85.29    0.00    0.00   13.92   85.29     -  stress
平均时间:     0      5052   67.79    0.00    0.00   31.41   67.79     -  stress
平均时间:     0      5053   88.67    0.00    0.00   10.54   88.67     -  stress
平均时间:     0      5054   72.96    0.00    0.00   26.44   72.96     -  stress
平均时间:     0      5055   73.96    0.00    0.00   25.25   73.96     -  stress
平均时间:     0      5056   64.41    0.00    0.00   34.99   64.41     -  stress
平均时间:     0      5057   87.48    0.00    0.00   11.93   87.48     -  stress
平均时间:  1000      5206    0.20    0.00    0.00    0.00    0.20     -  watch
平均时间:     0      5436    0.00    0.40    0.00    0.00    0.40     -  pidstat

发现%wait很高,表示cpu队列一直在等待,存在慢的进程

cpu上下文切换

程序在运行的时候必须依赖的环境称为CPU上下文。

上下文切换把上一个任务的CPU上下文保存起来,加载新任务的上下文到寄存器和程序计数器中。

上下文切换分为进程上下文切换,线程上下文切换和中断上下文切换。

进程上下文切换

linux按照特权等级,把进程的运行空间分为内核空间和用户空间。对应的ring级别分为ring 0和ring 3。

进程可以在内核空间和用户空间运行,在用户空间时候称为进程处于用户态,而陷入内核空间的时候,

称为进程的内核态。

从用户态到内核态,需要通过系统调用来完成。比如查看文件的时候调用open()打开文件,然后调用read()

读取文件内容,调用write()把内容写到标准输出,最后调用close()关闭文件。

由于在调用前,需要保存用户上下文,调用的时候,切换到对应的内核态指令的位置,调用结束后又会保存内核态的

上下文,再次切换会用户态,所以一次系统调用发生了两次CPU上下文切换。

什么时候会触发上下文切换? 发生进程调度进出的时候

什么时候会发生进程调度?

  • 进程结束运行的时候
  • 进程的时间片用完
  • 系统资源不足
  • 进程主动sleep
  • 有高优先级进程
  • 硬件中断
线程上下文切换

线程是调度的基本单位,进程则是资源拥有的基本单位。linux调度的是线程,而不是进程,进程

提供了包含全局变量,虚拟内存等资源。关于进程和线程:

  • 进程只有一个线程的时候,进程等于线程。
  • 当进程拥有多个线程的时候,这些线程会共享相同的虚拟内存和全局变量。这些资源在上下文切换

时候无需修改。

  • 线程也有自己的私有数据,保存着栈和寄存器中,上下文切换时候需要保存。

线程的上下文切换分为两种:

  • 前后线程同属一个进程,此时只需要切换进程的私有数据,全局变量和虚拟内存保持不动。
  • 前后线程属于两个进程,切换时候和进程切换上下文一样。
中断上下文切换

比如键盘输入,为了快速响应硬盘事件,中断由于有更高的优先级,会打断进程的运行,转而让cpu处理中断。

中断上下文并不涉及用户态,中断上下文只包括内核态的中断服务必须的状态,包括CPU寄存器、内核堆栈、

硬件中断参数等。

中断一般分为软中断和硬中断。硬中断是硬件发来的让cpu优先处理其请求,比如硬盘按下,

打印机打印,鼠标移动等。软中断比如cpu除以0触发等等,是软件相关的中断。

软中断本质上是利用硬件中断的方式模拟中断通过异步的方式更好地利用cpu。

中断分为上半部和下半部。

上半部优先处理硬件相关时间敏感的工作,下半部通过内核线程处理剩下的工作。

可以查看cat /proc/softirqs发现有11种中断类型。

linux里面把每个cpu的软中断线程标记为ksoftirqd/0等

一般软中断过高可能和网卡流量高有关系。

上下文切换相关命令排查

vmstat查看

man一下vmstat,发现vmstat是对系统比较直观的参数展示,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
   Procs
       r: 等待运行的进程数
       b: 处在非中断睡眠状态的进程数
       w: 被交换出去的可运行的进程数。
       此数由 linux 计算得出,但 linux 并不耗尽交换空间

   Memory
       swpd: 虚拟内存使用情况,单位:KB
       free: 空闲的内存,单位KB
       buff: 被用来做为缓存的内存数,单位:KB

   Swap
       si: 从磁盘交换到内存的交换页数量,单位:KB/秒
       so: 从内存交换到磁盘的交换页数量,单位:KB/秒

   IO
       bi: 发送到块设备的块数,单位:块/秒
       bo: 从块设备接收到的块数,单位:块/秒
   System
       in: 每秒的中断数,包括时钟中断
       cs: 每秒的环境(上下文)切换次数

   CPU
       按 CPU 的总使用百分比来显示
       us: CPU 使用时间
       sy: CPU 系统使用时间
       id: 闲置时间
1
2
3
4
~ vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b 交换 空闲 缓冲 缓存   si   so    bi    bo   in   cs us sy id wa st
 1  0      0 8090992 222512 3761816    0    0    57    43  249  355  5  0 94  0  0
1
2
3
4
5
pidstat -w 5
平均时间:   UID       PID   cswch/s nvcswch/s  Command
平均时间:     0         1     17.86      0.22  systemd
平均时间:     0         2      0.10      0.00  kthreadd
平均时间:     0        14      7.95      0.00  ksoftirqd/0

cswch表示voluntary context switches 自愿上下文切换 无法获取资源,等待资源导致的上下文切换

nvcswch表示non voluntary context switches 非资源上下文切换 时间片使用完等原因,被系统强制调度

使用sysbench模拟多线程调度

1
  sysbench --threads=25 --max-time=300 threads run

使用vmstat观察cs参数提高

1
2
3
4
➜  ~ sudo vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b 交换 空闲 缓冲 缓存   si   so    bi    bo   in   cs us sy id wa st
 7  0      0 8866560 211748 3315608    0    0   244    58  454 4420  4 10 86  0  0

使用pidstat -u -tw检查线程上下文

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
07时25分42秒     0     18984     18985  39711.80   1793.40  (sysbench)__sysbench
07时25分42秒     0         -     18986  40166.60   1816.20  |__sysbench
07时25分42秒     0         -     18987  40228.80   1803.00  |__sysbench
07时25分42秒     0         -     18988  40279.60   1884.80  |__sysbench
07时25分42秒     0         -     18989  40313.80   1845.80  |__sysbench
07时25分42秒     0         -     18990  40058.60   1776.20  |__sysbench
07时25分42秒     0         -     18991  40400.40   1872.60  |__sysbench
07时25分42秒     0         -     18992  40332.00   1850.40  |__sysbench
07时25分42秒     0         -     18993  39709.40   1788.80  |__sysbench
07时25分42秒     0         -     18994  40311.00   1867.40  |__sysbench
07时25分42秒     0         -     18995  40124.00   1823.80  |__sysbench
07时25分42秒     0         -     18996  40228.40   1818.80  |__sysbench
07时25分42秒     0         -     18997  40105.20   1794.80  |__sysbench
07时25分42秒     0         -     18998  39606.40   1777.20  |__sysbench
07时25分42秒     0         -     18999  40131.40   1797.20  |__sysbench
07时25分42秒     0         -     19000  39858.20   1852.60  |__sysbench
07时25分42秒     0         -     19001  39676.00   1768.60  |__sysbench
07时25分42秒     0         -     19002  39163.00   1779.40  |__sysbench
07时25分42秒     0         -     19003  40331.80   1794.20  |__sysbench
07时25分42秒     0         -     19004  39751.20   1800.60  |__sysbench
07时25分42秒     0         -     19005  39577.60   1782.40  |__sysbench
07时25分42秒     0         -     19006  40001.00   1799.00  |__sysbench
07时25分42秒     0         -     19007  39965.20   1820.00  |__sysbench
07时25分42秒     0         -     19008  39736.20   1782.60  |__sysbench
07时25分42秒     0         -     19009  39948.80   1852.40  |__sysbench

发现sysbench进行上下文切换数量很多

而通过观察/proc/stats也会发现RES部分会明显升高,

1
2
3
4
5
6
7
8
73,75c73,75
<  RES:      14605      20838      20089      23930      25157      84892      26030      20687      17731      23070      36385      26992      28407      30080      24712      20254   Rescheduling interrupts
<  CAL:      46181      40172      37618      38326      39866      37922      38575      32578      36656      36854      35093      38177      37131      36806      34525      38760   Function call interrupts
<  TLB:       5453       7443       6335       7063       7295       8125       7679       9385       7211       6737       5571       6152       6422       8290       7725       6918   TLB shootdowns
---
>  RES:      21666      32475      29558      35739      37829     115528      39801      31482      26094      33610      51101      40816      42468      46303      39079      30826   Rescheduling interrupts
>  CAL:      51344      44463      42073      42738      44157      42547      42846      36954      40302      40457      39915      42809      41409      41101      39474      42373   Function call interrupts
>  TLB:       5740       7838       6818       7306       7504       8514       7938       9830       7364       7003       6254       6446       6826       8501       7960       7107   TLB shootdowns

RES指Rescheduling interrupts,指cpu重新调度的中断。

一般来说:

  • 自愿上下文切换增多,表示系统依赖其他资源较多,比如内存等存在瓶颈
  • 非资源上下文切换增多,说明进程被强制调度,存在CPU争用的情况
  • 中断次数变多说明CPU被中断占用,通过/proc/interrupts进行分析

CPU使用率

linux的时间是通过节拍决定

1
2
3
4
5
6
7
➜  ~ grep CONFIG_HZ /boot/config-5.17.0-1-amd64 
# CONFIG_HZ_PERIODIC is not set
# CONFIG_HZ_100 is not set
CONFIG_HZ_250=y
# CONFIG_HZ_300 is not set
# CONFIG_HZ_1000 is not set
CONFIG_HZ=250

表示内核程序1s内会触发250次时间节拍中断,用户空间程序的节拍频率的称为USER_HZ,默认是100HZ,即1/100s,对应10ms一次节拍

cpu使用率有几个很重要的参数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
user(us): 代表用户态CPUJ时间,不包括nice时间,包含guest时间。
nice(ni):低优先级用户态CPU时间,也就是进程nice值被设置成1-19的CPU时间。nice的取值范围是-19-20,数字越高,优先级越底。不同
体系系统可能略微不同。
system(sys): 内核态CPU时间。
idle(id): 代表空闲时间,不包括等待iowait的时间。
iowait(wa): 表示等待I/O的CPU时间。
irq(hi): 表示处理硬件中断的CPU时间。
softirq(si): 表示处理软中断的CPU时间。
steal(st): 代表当系统运行在虚拟机中的时候,被其他虚拟机占用的CPU时间。
guest(guest): 代表运行虚拟机的CPU时间。
guest_nice(gnice): 代表以低优先级运行虚拟机的时间。

CPU使用率 = 1 - 空闲时间/总CPU时间

平均CPU使用率 = 1 - (空闲时间2-空闲时间1)/(总CPU时间1-总CPU时间2)

使用perf分析热点函数等
1
2
3
  perf top # 查看函数调用和分析频率
  perf record -g # 记录一段时间的函数依赖调用,Ctrl-C暂停记录
  perf report -g # 访问perf的数据

这里举了一个例子不再详述

CPU使用率100%,如何排查

  • 通过ps, pidstat, top等确定高CPU使用率的程序或者命令
  • 通过观察监控系统,日志系统确定是最近发生的还是一直有的

比如是否有发布,是否有缩容,实际有流量激增

  • 确定具体的应用,观察应用的日志,排查是否是具体的请求等等
  • 通过perf或者程序的追踪程序观察热点函数

如果找不到呢?

多观察一下,并且可以通过 perf top 或者 execsnoop 查看,可能是存在短时应用,这些程序在频繁重启等

软中断

中断是系统用来响应硬件设备请求的一种机制,它会打断程序的调度和执行,然后调用对应的中断处理程序来响应设备的请求。

linux将中断分为上半部和下半部。

  • 上半部用来快速处理中断,在中断禁止模式下运行,主要处理和硬件相关的或者时间敏感的工作,对应硬件中断
  • 下半部用来延迟处理上半部未完成的工作,通常以内核线程的方式运行,对应软中断,通常名字为Ksoftirqd/CPU编号

硬件中断的统计次数在 /proc/interrupts, 常见的有时钟中断,磁盘相关的中断,温度事件,显卡等

软件中断的统计次数在 /proc/softirqs , 常见的有网卡收发相关,定时器相关,任务调度,RCU锁相关

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
➜  ~ cat /proc/softirqs 
                    CPU0       CPU1       CPU2       CPU3       CPU4       CPU5       CPU6       CPU7       CPU8       CPU9       CPU10      CPU11      CPU12      CPU13      CPU14      CPU15      
          HI:       9031       6254       7243       6443       5101       9251     279819      17015      10496       9310       4795       6254      11077       7640       8547       7909
       TIMER:      77724      51682      57134      55582      63671     345875     110982      62003      51988      50116      44069      48748      44494      46420      63170      56155
      NET_TX:          3          4         11          8          4         10          7          1          4          4          6          9          0          2          6          2
      NET_RX:        216        179         95         66         65         59        121        415        108       6840        785        196       2686        148      29002        418
       BLOCK:         84        259        195        168        219        104        217        303        183        154        264        164        145        261        229        187
    IRQ_POLL:          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0          0
     TASKLET:      96179      55966      60119      41061      22617        667      15795      36390      72232     144126      67512      75367      74565      39411     291496     160630
       SCHED:     478386     408729     396019     394562     436672     681771     474057     404191     352733     375179     382859     379720     369687     430896     378160     380524
     HRTIMER:        703        222        137          5          2         14          1          2          0          0          1         86          0          8       1927       3515
         RCU:     300615     289577     300542     299460     319489     471398     344540     291728     258167     281839     287994     284638     277495     311581     283218     277838

参考文档