Contents

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 查看具体的中断类型。

分析工具

  1. 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: 每秒非自愿上下文切换

实例

  1. 使用sysbench模拟多线程情况,sysbench --threads=10 --max-time=300 threads run
  2. 使用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性能问题时:

  1. 使用top、uptime大致查看系统整体的负载情况。
  2. 使用mpstat 查看系统级别的CPU消耗情况,判断是否存在CPU问题,着重查看usr、sy、iowait。使用vmstat查看系统级别上下文切换情况
  3. 使用pidstat 分析进程、线程级别CPU情况
  4. 查看/proc/interrupts 分析中断情况