JohnShen's Blog.

Linux性能优化-CPU篇

字数统计: 3.4k阅读时长: 11 min
2020/12/13 Share

平均负载

平均负载提供了一个快速查看系统整体性能的手段。平均负载是指单位时间内,系统处于可运行状态和不可中断状态的平均进程数,也就是平均活跃进程数,它和 CPU 使用率并没有直接关系。

  • 可运行状态进程,是指正在使用 CPU 或者正在等待 CPU 的进程( ps 命令看到的处于 R 状态 Running 或 Runnable)的进程
  • 不可中断状态进程:是正处于内核态关键流程中的进程,这些流程不可打断,如等待硬件设备的 I/O 响应( ps 命令中看到的 D 状态 Uninterruptible Sleep,也称为 Disk Sleep)的进程

可以简单理解为,平均负载其实就是平均活跃进程数。平均活跃进程数,直观上的理解就是单位时间内的活跃进程数,实际上是活跃进程数的指数衰减平均值。

当平均负载为 2 时,意味着:

  • 在只有 2 个 CPU 的系统上,意味着所有的 CPU 都刚好被完全占用
  • 在 4 个 CPU 的系统上,意味着 CPU 有 50% 的空闲
  • 在只有 1 个 CPU 的系统中,则意味着有一半的进程竞争不到 CPU

查看CPU个数

1
grep 'model name' /proc/cpuinfo | wc -l

负载是否合理

比较 load1 / load5 / load15:

  • 如果 1 分钟、5 分钟、15 分钟的三个值基本相同,或者相差不大,那就说明系统负载很平稳。
  • 如果 1 分钟的值远小于 15 分钟的值,就说明系统最近 1 分钟的负载在减少,而过去 15 分钟内却有很大的负载。
  • 如果 1 分钟的值远大于 15 分钟的值,就说明最近 1 分钟的负载在增加,这种增加有可能只是临时性的,也有可能还会持续增加下去,所以就需要持续观察。

一般生产环境下,当 ,就应该分析排查负载高的问题了。

平均负载与 CPU 使用率

平均负载不仅包括了正在使用 CPU 的进程,还包括等待 CPU 和等待 I/O 的进程。

CPU 使用率,是单位时间内 CPU 繁忙情况的统计,跟平均负载并不一定完全对应:

  • CPU 密集型进程,使用大量 CPU 会导致平均负载升高,此时这两者是一致的;
  • I/O 密集型进程,等待 I/O 也会导致平均负载升高,但 CPU 使用率不一定很高;
  • 大量等待 CPU 的进程调度也会导致平均负载升高,此时的 CPU 使用率也会比较高。

工具使用

yum install -y sysstat

mpstat:多核 CPU 性能分析工具,用来实时查看每个 CPU 的性能指标,以及所有 CPU 的平均指标。

pidstat:进程性能分析工具,用来实时查看进程的 CPU、内存、I/O 以及上下文切换等性能指标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-u:默认的参数,显示各个进程的cpu使用统计
-r:显示各个进程的内存使用统计
-d:显示各个进程的IO使用情况
-p:指定进程号
-w:显示每个进程的上下文切换情况
-t:显示选择任务的线程的统计信息外的额外信息

# 所有进程的 CPU 使用情况
pidstat
pidstat -u -p ALL
# 间隔5秒后输出一组数据
pidstat -u 5 1
# 内存使用,指定进程,每秒展示一次,展示四次
pidstat -r -p 29468 1 4
# 各进程的IO使用
pidstat -d
# 进程的上下文切换
pidstat -w
# 特定进程的线程统计信息
pidstat -t -p 12920
# 表示输出线程的上下文切换指标
pidstat -wt 1

CPU 上下文切换

任务运行前,CPU 都需要知道任务从哪里加载、又从哪里开始运行,即需要系统事先帮它设置好 CPU 寄存器和程序计数器

CPU 上下文:CPU在运行前必须依赖的环境。一是CPU 寄存器,是 CPU 内置的容量小、但速度极快的内存。二是程序计数器,则是用来存储 CPU 正在执行的指令位置、或者即将执行的下一条指令位置。

CPU上下文切换:先把前一个任务的 CPU 上下文(也就是 CPU 寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务。根据任务的不同,CPU 的上下文切换就可以分为几个不同的场景:进程上下文切换线程上下文切换以及中断上下文切换

过多的上下文切换,会把 CPU 时间消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上

进程上下文切换

把进程的运行空间分为内核空间和用户空间。进程在用户空间运行时,被称为进程的用户态,而陷入内核空间的时候,被称为进程的内核态。从用户态到内核态的转变,需要通过系统调用来完成。

系统调用过程也会发生CPU上下文切换,一次系统调用的过程,其实是发生了两次 CPU 上下文切换。不过系统调用过程中,并不会涉及到用户态资源,也不会切换进程。这跟通常所说的进程上下文切换是不一样。系统调用过程通常称为特权模式切换,而不是上下文切换。

进程的上下文不仅包括了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的状态。进程的上下文不仅包括了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的状态。因此,进程的上下文切换就比系统调用时多了一步:在保存当前进程的内核状态和 CPU 寄存器之前,需要先把该进程的虚拟内存、栈等保存下来;而加载了下一进程的内核态后,还需要刷新进程的虚拟内存和用户栈。

保存上下文和恢复上下文的过程并不是“免费”的,需要内核在 CPU 上运行才能完成。每次上下文切换都需要几十纳秒到数微秒的 CPU 时间。在进程上下文切换次数较多的情况下,很容易导致 CPU 将大量时间耗费在寄存器、内核栈以及虚拟内存等资源的保存和恢复上,进而大大缩短了真正运行进程的时间。这也正是导致平均负载升高的一个重要因素。

线程上下文切换

线程是调度的基本单位,而进程则是资源拥有的基本单位。所谓内核中的任务调度,实际上的调度对象是线程;而进程只是给线程提供了虚拟内存、全局变量等资源。

当进程拥有多个线程时,这些线程会共享相同的虚拟内存和全局变量等资源。这些资源在上下文切换时是不需要修改的。另外,线程也有自己的私有数据,比如栈和寄存器等,这些在上下文切换时也是需要保存的。

线程的上下文切换其实就可以分为两种情况:

  • 前后两个线程属于不同进程。此时,因为资源不共享,所以切换过程就跟进程上下文切换是一样。
  • 前后两个线程属于同一个进程。因为虚拟内存是共享的,所以在切换时虚拟内存这些资源就保持不动,只需切换线程的私有数据、寄存器等不共享数据。

同进程内的线程切换,要比多进程间的切换消耗更少的资源,而这,也正是多线程代替多进程的一个优势。

中断上下文切换

为了快速响应硬件的事件,中断处理会打断进程的正常调度和执行,转而调用中断处理程序,响应设备事件。而在打断其他进程时,就需要将进程当前的状态保存下来,这样在中断结束后,进程仍然可以从原来的状态恢复运行。

中断上下文切换并不涉及到进程的用户态。

对同一个 CPU 来说,中断处理比进程拥有更高的优先级。

工具使用

vmstat 主要用来分析系统的内存使用情况,也常用来分析 CPU 上下文切换和中断的次数。主要是给出系统总体的上下文切换情况:

  • cs(context switch)是每秒上下文切换的次数。

  • in(interrupt)则是每秒中断的次数。

  • r(Running or Runnable)是就绪队列的长度,也就是正在运行和等待 CPU 的进程数。

  • b(Blocked)则是处于不可中断睡眠状态的进程数。

分析进程使用情况需使用pidstat -w:

  • cswch ,表示每秒自愿上下文切换(voluntary context switches)的次数,

  • nvcswch ,表示每秒非自愿上下文切换(non voluntary context switches)的次数。

所谓自愿上下文切换,是指进程无法获取所需资源,导致的上下文切换。比如说, I/O、内存等系统资源不足时,就会发生自愿上下文切换。而非自愿上下文切换,则是指进程由于时间片已到等原因,被系统强制调度,进而发生的上下文切换。比如说,大量进程都在争抢 CPU 时,就容易发生非自愿上下文切换。

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

CPU 使用率

/proc/stat 提供系统的 CPU 和任务统计信息; /proc/[pid]/stat展示进程的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)。
  • iowait(通常缩写为 wa),代表等待 I/O 的 CPU 时间。
  • irq(通常缩写为 hi),代表处理硬中断的 CPU 时间。
  • softirq(通常缩写为 si),代表处理软中断的 CPU 时间。
  • steal(通常缩写为 st),代表当系统运行在虚拟机中的时候,被其他虚拟机占用的 CPU 时间。
  • guest(通常缩写为 guest),代表通过虚拟化运行其他操作系统的时间,也就是运行虚拟机的 CPU 时间。
  • guest_nice(通常缩写为 gnice),代表以低优先级运行虚拟机的时间。

要弄清楚用户(%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 等工具也不容易发现。

第二,应用本身在不停地崩溃重启,而启动过程的资源初始化,很可能会占用相当多的 CPU。这类进程可以用 pstree 或者 execsnoop 找到它们的父进程,再从父进程所在的应用入手,排查问题的根源。

CATALOG
  1. 1. 平均负载
    1. 1.0.1. 查看CPU个数
    2. 1.0.2. 负载是否合理
    3. 1.0.3. 平均负载与 CPU 使用率
    4. 1.0.4. 工具使用
  • 2. CPU 上下文切换
    1. 2.0.1. 工具使用
  • 3. CPU 使用率