学习笔记-Linux性能优化-CPU

平均负载

1. 理解平均负载

  平均负载对很多人来说既熟悉又陌生,那我们如何理解和观测这个最常见、也是最重要的系统指标呢?
  平均负载是指单位时间内,系统处于可运行状态和不可中断状态的平均进程数量,也就是平均活跃进程数。

  可运行状态:指正在使用或者正在等待 CPU 的进程,也就是我们常用 ps 命令看到的,处于 R 状态(Running 或 Runnable)的进程。
  不可中断状态:指的是正处于内核态关键流程中的进程,并且这些流程是不可打断的,比如最常见的是等待硬件设备的 I/O 响应,也就是我们在 ps 命令中看到的 D 状态(Uninterruptible Sleep,也称为 Disk Sleep)的进程。不可中断状态实际上是系统对进程和硬件设备的一种保护机制,比如当一个进程向磁盘写入数据时,在得到磁盘回复之前,它是不能被其他进程或者中断打断的,此时进程就处于不可中断状态,否则会处于磁盘数据和进程数据不一致的情况。

  因此,你可以简单的将平均负载理解为平均活跃进程数量,那么最理想的状态,就是每个CPU上刚好运行一个进程,这样每个CPU都得到了充分利用。在实际的情况中,平均负载为多少时合理呢?多大说明系统负载高?多小说明负载很低呢?
  一般情况下,当平均负载高于CPU数量的70%的时候,你就应该分析排查负载高的问题了,一旦负载变高,就可能导致进程响应变慢,进而影响正常的功能,当然这个数字并不是绝对的,最推荐的方式,还是监控系统的平均负载,然后根据更多的历史数据,判断负载的变化趋势,当发现负载有明显的升高趋势时,再去分析和调查。
  现实工作中,我们会把平均负载CPU使用率混淆,让我们重新回顾一下平均负载的概念:平均负载是指单位时间内,系统处于可运行状态和不可中断状态的平均进程数量。也就是说它不仅包含正在使用CPU的进程,还包括等待CPU和等待IO的进程。而CPU使用率表示单位时间内,非空闲状态所占的比例,比如:

  • CPU密集型,使用大量的CPU会使得平均负载升高。
  • IO密集型,等待IO也会导致平均负载升高,但是此时CPU使用率不一定高。
  • 大量等待CPU进程调度也会导致平均负载升高,此时的CPU使用率也会比较高。

2. 平均负载分析

在分析平均负载之前,先简单的介绍一下 sysstat,它包含了常用的Linux 性能工具,用来监控和分析系统的性能,我们会用到其中的 mpstatpidstat

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

  在分析平均负载之前,我们可以通过 lscpu 命令查看当前机器的CPU信息。

$ lscpu
CPU(s):  1
...
1. 场景一: CPU密集进程
  1. 通过 uptime 命令查看平均负载的变化情况(过去 1 分钟、5 分钟、15 分钟),由下图终端的结果来看,平均负载时逐步增高的。
$ watch -d uptime
..., load average: 1.00, 0.75, 0.39
  1. 通过 mpstat 查看CPU使用率的变化情况,由下图执行结果来看,有个线程的CPU 占用率为 100%, 但是 iowait 只有 0 ,说明平均负载的升高是因为CPU 的利用率较高。
$ mpstat -P ALL 5 1
08:42:30 PM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
08:42:35 PM  all    0.50    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00   0.00
08:42:35 PM    0    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00   0.00
08:42:35 PM    1    100.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  0.00
  1. 通过 pidstat 查看进程的性能指标如下,我们可以看到到底为哪个进程导致CPU利用率升高。
$ pidstat -u 5 1

08:51:25 PM   UID       PID    %usr %system  %guest    %CPU   CPU  Command
08:51:31 PM  1002      1024    100.00   0.00    0.00    100.00     1  ***
分析步骤总结
  1. 确定服务器的核心数量,通过 uptime 命令观察平均负载的趋势走向。
  2. 通过 mpstat 命令服务器的运行情况, 包括 cpu 利用率、io 等待、用户态、系统态、争用CPU所占时间 等各个指标所占比例等等。
  3. 通过 pidstat 命令查看哪个进程所占的 cpu或者 iowait 高,又或者是系统运行进程超出了cpu 的运行能力, 导致 cpu过载。

CPU上下文切换

  Linux 是一个多任务操作系统,它支持远大于 CPU 数量的任务同时运行。当然,这些任务实际上并不是真的在同时运行,而是因为系统在很短的时间内,将 CPU 轮流分配给它们,而每个人任务执行之前,CPU都必需知道该任务所需的数据从哪里加载,又从哪里开始运行(CPU寄存器和程序计数器)。CPU上下文切换实际上就是把前一个任务的上下文保存起来,然后加载当前正在准备执行的任务的上下文到寄存器和计数器中,运行新任务。
  实际CPU的上下文切换可以分为几个不同的场景,进程上下文切换线程上下文切换以及中断上下文切换

进程上下文切换

   Linux 特权等级,将进程的运行空间分为内核空间和用户空间,内核空间具有最高的访问权限;而用户空间只能访问受限的资源,不能直接访问内存等硬件设备,必需通过系统调用陷入内核中,才能访问这些特权资源。从用户态到内核态的转变,是通过系统调用完成的。比如我们的程序访问文件内容就需要进行系统调用,首先通过系统调用打开文件,将内容读取到内核空间,拷贝到用户空间之后才能被程序处理。
  由此可见,系统调用的时候会发生了CPU上下文切换。 CPU寄存器里原来用户态程序的上下文信息需要先保存起来,为了执行内核态代码,加载内核代码的上下文,运行内核任务,系统调用结束之后,需要切换到用户空间。所以,一次系统调用,会发生两次CPU上下文切换
  系统调用过程中,并不会涉及到虚拟内存等进程用户态的资源,也不会切换进程。这跟我们通常所说的进程上下文切换是不一样的,进程上下文切换指的是从一个进程切换到另一个进程,而系统调用一直是一个进程,所以通常被称为特权模式切换,而不是上下文切换。
  进程是由内核来管理和调度的,进程的切换只能发生在内核态。所以,进程的上下文不仅包括了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的状态。因此进程上下文切换比系统调用多了一步,保存当前进程的内核状态和CPU寄存器之前,需要先把该进程的虚拟内存、栈等保存起来。
究竟什么时候会切换进程上下文呢?

  • 操作系统为了保证公平调度,CPU时间被划分为一段段的时间片,然后轮流分配给各个进程,当某个进程的时间片耗尽之后,会被系统挂起,切换到其他等待CPU的进程运行。
  • 进程在资源不足的情况下,要等到资源满足之后才可以运行,这个时候也会被挂起,由系统调度其他进程。
  • 进程通过sleep 函数将自己主动挂起。
  • 优先级较高的进程运行时,为了保证高优先级的先被运行,当前进程会被挂起。
  • 发生硬件中断时,CPU进程会中断挂起,转而执行内核中的中断服务进程。
线程上下文切换

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

  • 当进程只有一个线程时,进程等于线程。
  • 当进程有多个线程时,全局变量等线程公共资源是不用修改的,而线程自己的私有数据,比如栈和寄存器,也是需要保存的。
      如果为第一种,线程上下文切换过程和进程切换相同,对于第二种,由于不需要保存公共资源,所以同进程内的线程切换,要比多进程间的切换消耗更小。
中断上下文切换

  为了快速响应硬件的事件,中断处理会打断进程的正常调度和执行,转而调用中断处理程序,响应设备事件,而在打断进程的正常调度之前,需要保存当前进程的状态。跟进程上下文不同,中断并不涉及进程的用户态,所以不需要保存和恢复这个进程的虚拟内存、全局变量等用户态资源。中断上下文,其实只包括内核态中断服务程序执行所必需的状态,包括 CPU 寄存器、内核堆栈、硬件中断参数等。
  对同一个 CPU 来说,中断处理比进程拥有更高的优先级,所以中断上下文切换并不会与进程上下文切换同时发生。由于中断会打断正常进程的调度和执行,所以大部分中断处理程序都短小精悍,以便尽可能快的执行结束,但是中断上下文切换也需要消耗 CPU,切换次数过多也会耗费大量的 CPU,甚至严重降低系统的整体性能。所以,当你发现中断次数过多时,就需要注意去排查它是否会给你的系统带来严重的性能问题。

查看上下文切换情况

  过多的上下文切换,会将资源耗费在寄存器、内核栈以及虚拟内存的数据的保存和恢复上,缩短进程真正的执行时间,而上下文的切换包括两种:

  • 自愿上下文切换:指进程无法获取所需要的资源,导致的上下文切换,比如IO、内存等系统资源不足。
  • 非自愿上下文切换:指的是进程的时间片已经到期,被系统强制调度发生。
      我们主要通过vmstat 查询系统的上下文切换情况, 主要特别关注以下四列内容:
  1. r: 就绪队列的长度,表示运行或者等待CPU的进程数量。
  2. b:表示不可中断睡眠状态的进程数。
  3. in:每秒中断次数。
  4. cs: 每秒上下文切换的次数。
场景: 案例分析
  1. 首先通过 vmstat 工具 观察系统的上下文切换情况:
$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
11  0      0 323460 140952 1159544    0    0     2     8   74   65  1  1 99  0  0
 9  0      0 323460 140952 1159544    0    0     0     0 1550 942091 39 61  0  0  0

  关注内容:上下文切换次数:由 65 升高为 942091;有序队列的长度 8 已经远远超过了系统的CPU个数(2);CPU使用率(us + sy)已经到达了 100%;中断次数也提升到了 1550 次。
综合这些指标我们可以知道系统的就绪队列过长,导致大量的上下文切换,而上下文切换又导致了CPU 占用率的升高。

  1. 通过 pidstat 查看CPU和进程上下文切换情况:
$ pidstat -w -u 1

02:22:06 PM   UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
02:22:07 PM     0      1058    1.00    0.00    0.00    0.00    1.00     0  AliYunDun
02:22:07 PM     0     17861   38.00   61.00    0.00    0.00   99.00     0  sysbench

02:22:06 PM   UID       PID   cswch/s nvcswch/s  Command
02:22:07 PM     0        11      1.00      0.00  ksoftirqd/0
02:22:07 PM     0        12     42.00      0.00  rcu_sched
02:22:07 PM     0        22      2.00      0.00  kcompactd0
02:22:07 PM     0       145      5.00      0.00  load_calc
02:22:07 PM    32       498      1.00      0.00  rpcbind
02:22:07 PM  1003       569      1.00      0.00  memcached
02:22:07 PM     0       732      1.00      0.00  php-fpm
02:22:07 PM  1002       790     10.00      0.00  redis-server
02:22:07 PM     0      1058     11.00      0.00  AliYunDun
02:22:07 PM     0      1120     31.00      0.00  AliYunDunMonito
02:22:07 PM     0      5201     10.00      0.00  AliSecGuard
02:22:07 PM     0     17678      1.00      0.00  sshd
02:22:07 PM     0     17857      1.00      0.00  kworker/u4:1-events_unbound
02:22:07 PM     0     17860      6.00      0.00  kworker/0:1-events
02:22:07 PM     0     17890      1.00      0.00  pidstat

从输出发现,CPU使用率的升高是因为 sysbench导致的,但是上下文切换是来自其他的进程,不过图中输出的上下文切换次数,明显比 942091 小的多,讲到的几种上下文切换场景。其中提到, Linux 调度的基本单位实际上是线程,那么,是不是 pidstat 忽略了线程的数据呢?通过 man pidstat 的描述,实际上默认展示的是进程数据,所以我们需要再加上 -t 参数:

$ pidstat -wt 1

02:30:37 PM   UID      TGID       TID   cswch/s nvcswch/s  Command
02:30:39 PM     0         -     17944  12110.71  74812.50  |__sysbench
02:30:39 PM     0         -     17945  13327.68  71582.14  |__sysbench
02:30:39 PM     0         -     17946  10166.96  76762.50  |__sysbench
02:30:39 PM     0         -     17947   5872.32  88392.86  |__sysbench
02:30:39 PM     0         -     17948  14217.86  68983.93  |__sysbench
……

  虽然 sysbench 进程的上下文切换次数不多,但是它的子进程的上下文次数却又很多,所以罪魁祸首就是过多的 sysbench 线程。那四个关注指标中的中断次数又如何解释呢?到底是什么类型的中断上升了呢?
我们可以通过 /proc/interrupts 这个只读文件中读取中断发生的类型,通过下列命令观察中断的变化情况:

$ watch -d cat /proc/interrupts

分析步骤和总结
  1. 通过 vmstat 查看服务的总体状态,包括CPU 使用率、上下文切换次数、有序队列和中断次数(四个关键指标)。
  2. 通过 pidstat 查看进程的上下文切换情况,定位到进程或者线程。
  3. 通过查看/proc/interrupts 文件确定中断发生的类型和次数。
  自愿上下文切换次数变多,说明进程在等待资源,有可能发生了 io或者其他问题;非自愿上下文切换次数变多,说明进程都在争抢CPU,说明CPU变成了瓶颈;中断次数变多,说明 CPU被中断处理程序占用,需要查看/proc/interrupts 文件分析具体的中断类型。

CPU达到100%应该怎么办

如何查看CPU使用率

查看CPU的使用率,第一反应肯定是topps工具:

  • top:现实系统总体的 CPU和内存情况,以及各个进程的资源使用情况。
  • ps :显示每个进程的资源使用情况。
    比如下列就为 top 命令的输出情况:
$ top
top - 15:29:09 up 3 days, 19:13,  2 users,  load average: 0.02, 0.02, 0.49
Tasks: 135 total,   3 running, 132 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  6.7 sy,  0.0 ni, 93.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   1887.8 total,    304.7 free,    300.5 used,   1282.5 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.   1400.1 avail Mem 

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                                                                                                               
      1 root      20   0  104512  12152   9112 S   0.0   0.6   0:04.57 systemd                                                                                                                                               
      2 root      20   0       0      0      0 S   0.0   0.0   0:00.02 kthreadd

  其中第三行就为系统的 CPU使用率,不过默认展示的所有 CPU的平均值,只需要按下数字 1,就可以切换到每个CPU的使用率了。下方空白行之后是进程的实时信息,每个进程都有一个 %CPU列,标识该进程的 CPU使用情况,不过 top工具并没有细分进程的用户态CPU还是内核态CPU,可以使用 pidstat工具查看更加详细的信息。

$ pidstat 1 5
                       进程号  用户态  系统态  运行虚拟机  等待CPU  总的
03:40:28 PM   UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
03:40:29 PM     0      1058    1.00    0.00    0.00    0.00    1.00     0  AliYunDun
03:40:29 PM     0      1120    1.00    0.00    0.00    0.00    1.00     0  AliYunDunMonito
03:40:29 PM     0     21088    0.00    1.00    0.00    0.00    1.00     0  pidstat
CPU使用率过高怎么办

  通过toppspidstat等工具,可以轻松的找到CPU使用率较高的进程,那如何找到占用 CPU到底是代码里的哪个函数呢?可以通过 perf 工具来完成,它是一个基于性能事件采样为基础,不仅可以分析系统的各种事件和内核性能,还可以分析指定应用程序的性能问题。

CPU使用率过高,但是无法找到高CPU的应用

  系统的CPU使用率,不仅包含进程用户态和内核态的运行,还包括中断处理、等待IO以及内核线程等,所以当系统的CPU过高时,不一定能找到对应的高CPU使用率的进程
  所以,如果通过top命令发现系统的整体CPU命令很高,但是无论是根据下方进程列表的展示还是通过其他的工具(pidstat)查看,都找不到高CPU使用率的进程时,有可能为下面这些情况:

  • 应用里直接调用了其他二进制程序,这些程序的通常运行时间比较短,通过top工具也不容易发现。
  • 应用本身在不停的崩溃重启,而启动过程的资源初始化,很可能会占用相当多的CPU
  • 这些进程都为短时进程,运行很短的时间就会结束,很难通过top这种间隔时间较长的工具发现。
    分析步骤和总结
      1. 通过top命令,观察就绪队列中的进程数量(第二行 tasksrunning 字段的值)和下方展示的进程列表中运行态的进程数量是否存在较大的误差。
      2. 通过pidstat 命令查看可能出现问题的活跃进程的信息。
      3. 观察 top命令的输出,下方的进程列表中某个进程的 pid 是否不断变化。
      4. 通过 pstree命令,找出该进程的父进程,然后进入app进行内部分析,寻找不断创建子进程的原因所在。
      在得到猜测的结果之后,我们需要验证该猜测到底是否正确,所以我们可以使用 perf工具或者专门为短时进程设计的工具 execsnoop来验证猜想。execsnoop通过 ftrace 实时监控进程的 exec()行为,并输出短时进程的基本信息,包括进程pid、父进程pid、命令行参数以及执行的结果。
系统中出现了大量的不可中断进程和僵尸进程

  在top命令的 s(进程状态)列中包含以下几种状态:

d: 不可中断睡眠(uninterruptible sleep), 表示进程正在和硬件交互,不允许被其他进程打断。
I: 空闲(idle)。
R: 运行(running)。
S: 可中断睡眠(sleep),表示进程正在等待某个事件而被系统挂起。当进程等待的事件发生时,它会被唤醒并进入R状态。
Z: 僵尸进程 (Zombile),表示进程已经结束,但是父进程还没有回收它的资源(比如进程描述符、PID等)
T/t: 忽略

  僵尸进程是多进程应用容易碰到的问题。正常情况下,当一个进程创建了子进程后,它应该通过系统调用 wait()waitpid() 等待子进程结束,回收子进程的资源;而在子进程结束时,会向父进程发送SIGCHLD信号,所以父进程还可以注册SIGCHLD信号的处理函数,异步回收资源。
  如果父进程没有处理子进程的状态,子进程就提前退出,这是子进程就会变成僵尸进程。通常情况下,僵尸进程的持续时间较短,在父进程回收它的资源就会消亡;或者由init进程回收后也会消亡。
  一旦父进程没有处理子进程的终止,还一直保持运行状态,那么子进程就会一直处于僵尸状态。大量的僵尸进程会用尽PID进程号,导致不能创建新进程。
分析步骤和总结:
  大量不可中断进程

  1. 通过 top命令,观察平均负载、进程各个状态的数量、CPU使用率、iowait等指标。
  2. 通过dstat 查看 iowait 升高时,磁盘读写情况。
  3. 通过 top命令观察处于 D(不可中断)状态的所有进程。
  4. 通过 pidstat命令分析进程的读写情况,如果该读写较高时,着重分析该进程的调用堆栈。
      僵尸进程
  1. 通过 pstree 命令找出僵尸进程的父进程。
  2. 着重分析该进程的运行情况。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355

推荐阅读更多精彩内容