linux 性能优化-- cpu 切换以及cpu过高

摘要

本文先介绍了cpu上下文切换的基础知识,以及上下文切换的类型(进程,线程等切换)。然后介绍了如何查看cpu切换次数的工具和指标的解释。同时对日常分析种cpu过高的情况下如何分析和定位的方法做了一定的介绍,使用一个简单的案例进行分析,先用top,pidstat等工具找出占用过高的进程id,然后通过分析到底是用户态cpu过高,还是内核态cpu过高,并用perf 定位到具体的调用函数。(来自极客时间课程学习笔记)

cpu 上下文切换的基础知识

1、多任务竞争CPU,cpu变换任务的时候进行CPU上下文切换(context switch)。CPU执行任务有4种方式:进程、线程、或者硬件通过触发信号导致中断的调用。

2、当切换任务的时候,需要记录任务当前的状态和获取下一任务的信息和地址(指针),这就是上下文的内容。因此,上下文是指某一时间点CPU寄存器(CPU register)和程序计数器(PC)的内容, 广义上还包括内存中进程的虚拟地址映射信息.

3、上下文切换的过程:

  • (1)记录当前任务的上下文(即寄存器和计算器等所有的状态);
  • (2)找到新任务的上下文并加载;
  • (3)切换到新任务的程序计算器位置,恢复其任务。

4、根据任务的执行形式,相应的下上文切换,有进程上下文切换、线程上下文切换、以及中断上下文切换三类。

5、进程和线程的区别:
进程是资源分配和执行的基本单位;线程是任务调度和运行的基本单位。线程没有资源,进程给指针提供虚拟内存、栈、变量等共享资源,而线程可以共享进程的资源。

6、进程上下文切换:是指从一个进程切换到另一个进程。

(1)进程运行态为内核运行态和进程运行态。内核空间态资源包括内核的堆栈、寄存器等;用户空间态资源包括虚拟内存、栈、变量、正文、数据等

(2)系统调用(软中断)在内核态完成的,需要进行2次CPU上下文切换(用户空间-->内核空间-->用户空间),不涉及用户态资源,也不会切换进程。

(3)进程是由内核来管理和调度的,进程的切换只能发生在内核态。所以,进程的上下文不仅包括了用户空间的资源,也包括内核空间资源。

(4)进程的上下文切换过程:

  • (a)接收到切换信号,挂起进程,记录当前进程的虚拟内存、栈等资源存储;
  • (b)将这个进程在 CPU 中的上下文状态存储于起来;
  • (c)然后在内存中检索下一个进程的上下文;
  • (d)并将其加载到 CPU的寄存器中恢复;
  • (3)还需要刷新进程的虚拟内存和用户栈;
  • (f)最后跳转到程序计数器所指向的位置(即跳转到进程被中断时的代码行),以恢复该进程。

(5)、下列将会触发进程上下文切换的场景:

  • (a)、根据调度策略,将CPU时间划片为对应的时间片,当时间片耗尽,当前进程必须挂起。
  • (b)、资源不足的,在获取到足够资源之前进程挂起。
  • (c)、进程sleep挂起进程。
  • (d)、高优先级进程导致当前进度挂起
  • (e)、硬件中断,导致当前进程挂起

7、线程上下文切换:

  • (1)、不通进程之间的线程上下文切换,其过程和进程上下文切换大致相同。
  • (2)、进程内部的线程进上下文切换。不需要切换进程的用户资源,只需要切换线程私有的数据和寄存器等。这会比进程上下文进程切换消耗的资源少,所以多线程相比多进程的优势。

8、中断上下文切换
快速响应硬件的事件,中断处理会打断进程的正常调度和执行。同一CPU内,硬件中断优先级高于进程。切换过程类似于系统调用的时候,不涉及到用户运行态资源。但大量的中断上下文切换同样可能引发性能问题。

cpu上下文切换的诊断思路

  1. 查看系统上下文切换
# 每隔5秒输出1组数据
$ vmstat 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0 7005360  91564 818900    0    0     0     0   25   33  0  0 100  0  0

重点关注信息:

  • cs(context switch)是每秒上下文切换的次数。
  • us 用户态cpu情况
  • sy 内核态cpu情况
  • in(interrupt)则是每秒中断的次数。
  • r(Running or Runnable)是就绪队列的长度,也就是正在运行和等待 CPU 的进程数。
  • b(Blocked)则是处于不可中断睡眠状态的进程数。

系统的就绪队列过长,也就是正在运行和等待 CPU 的进程数过多,导致了大量的上下文切换,而上下文切换又导致了系统 CPU 的占用率升高。


# 每隔5秒输出1组数据
$ pidstat -w 5
Linux 4.15.0 (ubuntu)  09/23/18  _x86_64_  (2 CPU)

08:18:26      UID       PID   cswch/s nvcswch/s  Command
08:18:31        0         1      0.20      0.00  systemd
08:18:31        0         8      5.40      0.00  rcu_sched

这个结果中有两列内容是我们的重点关注对象。一个是 cswch ,表示每秒自愿上下文切换(voluntary context switches)的次数,另一个则是 nvcswch ,表示每秒非自愿上下文切换(non voluntary context switches)的次数。

  • 自愿上下文切换,是指进程无法获取所需资源,导致的上下文切换。比如说, I/O、内存等系统资源不足时,就会发生自愿上下文切换。
  • 非自愿上下文切换,则是指进程由于时间片已到等原因,被系统强制调度,进而发生的上下文切换。比如说,大量进程都在争抢 CPU 时,就容易发生非自愿上下文切换。
# 每隔1秒输出1组数据(需要 Ctrl+C 才结束)# -w参数表示输出进程切换指标,而-u参数则表示输出CPU使用指标
pidstat -w -u 1

# 每隔1秒输出一组数据(需要 Ctrl+C 才结束)# -wt 参数表示输出线程的上下文切换指标$ 
pidstat -wt 1

查看系统中断使用情况

linux的中断使用情况可以从 /proc/interrupts 这个只读文件中读取。/proc 实际上是 Linux 的一个虚拟文件系统,用于内核空间与用户空间之间的通信。/proc/interrupts 就是这种通信机制的一部分,提供了一个只读的中断使用情况。


# -d 参数表示高亮显示变化的区域
$ watch -d cat /proc/interrupts
           CPU0       CPU1
...
RES:    2450431    5279697   Rescheduling interrupts
...

重调度中断(RES),这个中断类型表示,唤醒空闲状态的 CPU 来调度新的任务运行。这是多处理器系统(SMP)中,调度器用来分散任务到不同 CPU 的机制,通常也被称为处理器间中断(Inter-Processor Interrupts,IPI)。

每秒上下文切换多少次才算正常呢

这个数值其实取决于系统本身的 CPU 性能。如果系统的上下文切换次数比较稳定,那么从数百到一万以内,都应该算是正常的。但当上下文切换次数超过一万次,或者切换次数出现数量级的增长时,就很可能已经出现了性能问题。这时,需要根据上下文切换的类型,再做具体分析。

比方说:

  • 自愿上下文切换变多了,说明进程都在等待资源,有可能发生了 I/O 等其他问题;
  • 非自愿上下文切换变多了,说明进程都在被强制调度,也就是都在争抢 CPU,说明 CPU 的确成了瓶颈;
  • 中断次数变多了,说明 CPU 被中断处理程序占用,还需要通过查看 /proc/interrupts 文件来分析具体的中断类型。

cpu 分析思路1

首先通过uptime查看系统负载,然后使用mpstat结合pidstat来初步判断到底是cpu计算量大还是进程争抢过大或者是io过多,接着使用vmstat分析切换次数,以及切换类型,来进一步判断到底是io过多导致问题还是进程争抢激烈导致问题。

cpu使用率过高的分析思路

cpu使用率相关的概念

CPU 使用率相关的重要指标:

  • user(通常缩写为 us),代表用户态 CPU 时间。注意,它不包括下面的 nice 时间,但包括了 guest 时间。
  • nice(通常缩写为 ni),代表低优先级用户态 CPU 时间,也就是进程的 nice 值被调整为 1-19 之间时的 CPU 时间。这里注意,nice 可取值范围是 -20 到 19,数值越大,优先级反而越低。
  • system(通常缩写为 sys),代表内核态 CPU 时间。idle(通常缩写为 id),代表空闲时间。注意,它不包括等待 I/O 的时间。
  • iowait(通常缩写为 wa),代表等待 I/O 的 CPU 时间。irq(通常缩写为 hi),代表处理硬中断的 CPU 时间。softirq(通常缩写为si),代表处理软中断的 CPU 时间。
  • steal(通常缩写为 st),代表当系统运行在虚拟机中的时候,被其他虚拟机占用的 CPU 时间。
  • guest(通常缩写为 guest),代表通过虚拟化运行其他操作系统的时间,也就是运行虚拟机的 CPU 时间。
  • guest_nice(通常缩写为 gnice),代表以低优先级运行虚拟机的时间。

性能分析工具给出的都是间隔一段时间的平均 CPU 使用率,所以要注意间隔时间的设置,特别是用多个工具对比分析时,你一定要保证它们用的是相同的间隔时间。比如,对比一下 top 和 ps 这两个工具报告的 CPU 使用率,默认的结果很可能不一样,因为 top 默认使用 3 秒时间间隔,而 ps 使用的却是进程的整个生命周期。

查看cpu使用率情况

top 和 ps 是最常用的性能分析工具:

  • top 显示了系统总体的 CPU 和内存使用情况,以及各个进程的资源使用情况。

# 默认每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
...

这个输出结果中,第三行 %Cpu 就是系统的 CPU 使用率,top 默认显示的是所有 CPU 的平均值,这个时候你只需要按下数字 1 ,就可以切换到每个 CPU 的使用率了。继续往下看,空白行之后是进程的实时信息,每个进程都有一个 %CPU 列,表示进程的 CPU 使用率。它是用户态和内核态 CPU 使用率的总和,包括进程用户空间使用的 CPU、通过系统调用执行的内核空间 CPU 、以及在就绪队列等待运行的 CPU。在虚拟化环境中,它还包括了运行虚拟机占用的 CPU。

分析工具

预先安装 stress 和 sysstat 包,如 apt install stress sysstat。

stress 是一个 Linux 系统压力测试工具,这里我们用作异常进程模拟平均负载升高的场景。而 sysstat 包含了常用的 Linux 性能工具,用来监控和分析系统的性能。我们的案例会用到这个包的两个命令 mpstat 和 pidstat。

  • mpstat 是一个常用的多核 CPU 性能分析工具,用来实时查看每个 CPU 的性能指标,以及所有 CPU 的平均指标。
  • pidstat 是一个常用的进程性能分析工具,用来实时查看进程的 CPU、内存、I/O 以及上下文切换等性能指标

pidstat

下面的 pidstat 命令,就间隔 1 秒展示了进程的 5 组 CPU 使用率,

pidstat 1 5

0:26:55 PM   UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
10:26:56 PM  1000      1558    0.99    0.99    0.00    0.00    1.98     1  Xorg
10:26:56 PM  1000      1908    0.99    0.00    0.00    0.00    0.99     3  vmtoolsd
10:26:56 PM  1000      2002    0.99    0.99    0.00    0.00    1.98     0  gnome-terminal-

包括:

  • 用户态 CPU 使用率 (%usr);
  • 内核态 CPU 使用率(%system);
  • 运行虚拟机 CPU 使用率(%guest);
  • 等待 CPU 使用率(%wait);
  • 以及总的 CPU 使用率(%CPU)。
  • CPU 表示使用的CPU的编号

perf

perf 是 Linux 2.6.31 以后内置的性能分析工具。它以性能事件采样为基础,不仅可以分析系统的各种事件和内核性能,还可以用来分析指定应用程序的性能问题。

第一种常见用法是 perf top,类似于 top,它能够实时显示占用 CPU 时钟最多的函数或者指令,因此可以用来查找热点函数,使用界面如下所示:

perf top

samples: 2K of event 'cpu-clock:pppH', 4000 Hz, Event count (approx.): 371909314 lost: 0/0 drop: 0/0
Overhead  Shared Object                              Symbol
  36.56%  [kernel]                                   [k] __lock_text_start
  10.43%  [kernel]                                   [k] vmw_cmdbuf_header_submit
   6.06%  [kernel]                                   [k] clear_page_orig
   2.93%  [kernel]                                   [k] __softirqentry_text_start
   2.29%  [kernel]  

输出结果中,第一行包含三个数据,分别是采样数(Samples)如2K、事件类型(event)如cpu-clock:pppH和事件总数量(Event count)如:371909314。

  • 第一列 Overhead ,是该符号的性能事件在所有采样中的比例,用百分比来表示。
  • 第二列 Shared ,是该函数或指令所在的动态共享对象(Dynamic Shared Object),如内核、进程名、动态链接库名、内核模块名等。
  • 第三列 Object ,是动态共享对象的类型。比如 [.] 表示用户空间的可执行程序、或者动态链接库,而 [k] 则表示内核空间。
  • 最后一列 Symbol 是符号名,也就是函数名。当函数名未知时,用十六进制的地址来表示。

第二种常见用法,也就是 perf record 和 perf report。 perf top 虽然实时展示了系统的性能信息,但它的缺点是并不保存数据,也就无法用于离线或者后续的分析。而 perf record 则提供了保存数据的功能,保存后的数据,需要你用 perf report 解析展示。

$ 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的报告

操作案例

1.启动docker 运行进程:

# 运行nginx 容器 并指定容器名,映射端口号,指定容器镜像
$ docker run --name nginx -p 10000:80 -itd feisky/nginx
# 运行app 应用容器 挂在nginx 容器之后 
$ docker run --name phpfpm -itd --network container:nginx feisky/php-fpm

2.ab工具测试服务器性能
ab(apache bench)是一个常用的 HTTP 服务性能测试工具,这里用来模拟 Ngnix 的客户端。

# 并发10个请求测试Nginx性能,总共测试100个请求
$ ab -c 10 -n 100 http://serverhost/
# 并发10个请求测试Nginx性能,总共测试10000个请求
$ ab -c 10 -n 10000 http://serverhost/

3.分析过程

# top 和 pidstat 找出占用cpu 使用较高的进程id
top
pidstat 1 5
# -g开启调用关系分析,-p指定php-fpm的进程号21515
$ perf top -g -p 21515

# 按方向键切换到 php-fpm,再按下回车键展开 php-fpm 的调用关系,你会发现,调用关系最终到了 sqrt 和 add_function。看来,我们需要从这两个函数入手了。

# 从容器phpfpm中将PHP源码拷贝出来
$ docker cp phpfpm:/app .
# 使用grep查找函数调用
$ grep sqrt -r app/ 
#找到了sqrt调用
app/index.php: $x += sqrt($x);
$ grep add_function -r app/ #没找到add_function调用,这其实是PHP内置函数

# 查看源码修改之后重新部署
# 停止原来的应用
$ docker rm -f nginx phpfpm
# 运行优化后的应用
$ docker run --name nginx -p 10000:80 -itd feisky/nginx:cpu-fix
$ docker run --name phpfpm -itd --network container:nginx feisky/php-fpm:cpu-fix
  1. perf 看不到 容器里面的调用链的解决方案
# 使用perf record 记录采样分析数据到本地为: perf.data
$ perf record
# 指定符号路径为容器文件系统的路径
$ mkdir /tmp/foo
$ PID=$(docker inspect --format {{.State.Pid}} phpfpm)
# bindfs 这个工具需要额外安装。bindfs 的基本功能是实现目录绑定(类似于 mount --bind)
$ bindfs /proc/$PID/root /tmp/foo
$ perf report --symfs /tmp/foo

# 使用完成后不要忘记解除绑定
$ umount /tmp/foo/

总结

CPU 使用率是最直观和最常用的系统性能指标,在排查性能问题时,通常会关注的第一个指标。所以更要熟悉它的含义,尤其要弄清楚:

  • 用户(%user)、
  • Nice(%nice)、
  • 系统(%system) 、
  • 等待 I/O(%iowait) 、
  • 中断(%irq)以及软中断(%softirq)

这几种不同 CPU 的使用率。比如说:

  • 用户 CPU 和 Nice CPU 高,说明用户态进程占用了较多的 CPU,所以应该着重排查进程的性能问题。
  • 系统 CPU 高,说明内核态占用了较多的 CPU,所以应该着重排查内核线程或者系统调用的性能问题。
  • I/O 等待 CPU 高,说明等待 I/O 的时间比较长,所以应该着重排查系统存储是不是出现了 I/O 问题。
  • 软中断和硬中断高,说明软中断或硬中断的处理程序占用了较多的 CPU,所以应该着重排查内核中的中断服务程序。

碰到 CPU 使用率升高的问题,你可以借助 top、pidstat 等工具,确认引发 CPU 性能问题的来源;再使用 perf 等工具,排查出引起性能问题的具体函数.

你可能感兴趣的:(linux 性能优化-- cpu 切换以及cpu过高)