linux性能优化

linux性能优化学习

性能指标

高并发和响应快 两个核心指标:吞吐和延时,从应用负载视角考察性能,直接影响产品终端的用户体验,相对应的从系统资源出发的指标(资源使用率,饱和度等)
性能指标两个角度
性能问题的本质:系统资源到达瓶颈,请求处理不够快,无法支撑更多请求
性能分析:找出应用或系统的瓶颈,并设法避免或缓解,更高效的利用系统资源处理更多请求
Linux性能工具图谱
Linux性能优化思维导图

平均负载

使用uptime可以查看系统负载

1
2
$ uptime
00:38:27 up 62 days, 14 min, 2 users, load average: 0.00, 0.04, 0.07

显示内容说明:

1
2
3
4
00:38:27                           系统当前时间
up 62 days, 14 min 主机已运行时间
2 users 用户连接数
load average: 0.00, 0.04, 0.07 系统平均负载,统计最近1,5,15分钟的系统平均负载

系统平均负载:指单位时间内,系统处于可运行状态不可中断状态的平均进程数, 即平均活跃进程数
可运行状态:正在使用CPU和正在等待CPU的进程(使用ps命令看到的处于R状态的进程)
不可中断状态:正处于内核态不可被打断的关键流程中的进程(不可中断状态是系统对进程和硬件设备的一种保护机制)
平均活跃进程不仅包括正在使用CPU的进程也包括等待CPU和等待I/O的进程,因此平均负载高不一定是CPU使用率高,也可能是I/O更繁忙了

系统逻辑CPU核心数可以使用top命令或者从文件/proc/cpuinfo中读取

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

uptime命令会显示1分钟,5分钟,15分钟的平均负载,可以用来分析系统负载趋势
系统负载异常需要结合历史数据,判断负载的变化趋势,当负载有明显升高趋势时再分析调查

  1. stress命令是Linux系统压力测试工具 —cpu cpu压测选项,-i io压测选项,-c 进程数压测选项,—timeout 执行时间

    1
    2
    3
    stress --cpu 1 --timeout 600  CPU密集型进程 模拟一个CPU使用率100%的场景 持续600s
    stress -i 1 --timeout 600 I/O密集型进程 模拟I/O压力 持续600s
    stress -c 8 --timeout 600 大量进程的场景 模拟8个进程 持续600s
  2. sysstat包,包含了常用的Linux性能工具,用来监控和分析系统性能

    1. mpstat多核CPU性能分析工具,用来实时查看每个CPU的性能指标和所有CPU的平均指标

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      $ mpstat -P ALL 5 3 显示所有CPU的指标,并每间隔5s输出一组数据,一共输出3组
      Linux 3.10.0-862.14.4.el7.x86_64 (VM_187_252_centos) 01/03/2019 _x86_64_ (1 CPU)

      01:41:06 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
      01:41:11 PM all 0.20 0.00 0.20 0.20 0.00 0.00 0.00 0.00 0.00 99.40
      01:41:11 PM 0 0.20 0.00 0.20 0.20 0.00 0.00 0.00 0.00 0.00 99.40

      01:41:11 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
      01:41:16 PM all 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00
      01:41:16 PM 0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00

      01:41:16 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
      01:41:21 PM all 0.20 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 99.80
      01:41:21 PM 0 0.20 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 99.80

      Average: CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
      Average: all 0.13 0.00 0.07 0.07 0.00 0.00 0.00 0.00 0.00 99.73
      Average: 0 0.13 0.00 0.07 0.07 0.00 0.00 0.00 0.00 0.00 99.73
    2. pidstat进程性能分析工具,用来实时查看进程的 CPU、内存、I/O 以及上下文切换等性能指标

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      $ pidstat -u 5 1 每5s输出一组数据
      Linux 3.10.0-862.14.4.el7.x86_64 (VM_187_252_centos) 01/03/2019 _x86_64_ (1 CPU)

      01:46:19 PM UID PID %usr %system %guest %CPU CPU Command
      01:46:24 PM 1000 21765 0.20 0.00 0.00 0.20 0 vim
      01:46:24 PM 1000 22625 0.00 0.20 0.00 0.20 0 pidstat
      01:46:24 PM 994 28861 0.00 0.20 0.00 0.20 0 redis-server

      Average: UID PID %usr %system %guest %CPU CPU Command
      Average: 1000 21765 0.20 0.00 0.00 0.20 - vim
      Average: 1000 22625 0.00 0.20 0.00 0.20 - pidstat
      Average: 994 28861 0.00 0.20 0.00 0.20 - redis-server
  3. watch以周期性的方式执行给定的指令,指令输出以全屏方式显示

    1
    watch -d uptime

CPU上下文切换

CPU上下文切换的工作原理

CPU寄存器是CPU内置的容量小,但速度极快的内存. 程序计数器(PC)是用来存储CPU正在执行的指令位置,或者即将执行的下一条指令位置. CPU寄存器和程序计数器都是CPU在运行任何任务之前,必须的依赖环境.CPU寄存器和程序计数器被称为CPU上下文.
CPU上下文切换
CPU上下文切换:把前一个任务的CPU上下文保存起来,然后加载新任务的上下文到CPU寄存器和程序计数器中,最后再跳转到程序计数器所指的新位置,运行新任务.保存下来的上下文,存储在系统内核中,在系统再次调度执行时再次加载进来,保证任务原先的状态不受影响,让任务看起来还是连续运行的.

CPU上下文切换根据任务不同,分为3个不同场景:进程上下文切换,线程上下文切换,中断上下文切换

进程上下文切换

Linux按照特权等级把进程的运行空间分为内核空间和用户空间.

  • 内核空间具有最高权限,可以访问所有资源
  • 用户空间只能访问受限资源,不能直接访问内存等硬件设备,必须通过系统调用陷入到内核中,才能访问这些资源

即:进程既可以在用户空间运行,又可以在内核空间运行,进程在用户空间运行称为进程的用户态,陷入到内核空间的时候,称为进程的内核态
从用户态到内核态的转变,需要通过系统调用完成.
系统调用会发生2次CPU上下文的切换,从用户态到内核态,CPU寄存器里用户态的指令位置需要先保存起来,CPU寄存器再更新为内核态指令的新位置,最后跳到内核态运行内核任务.系统调用后,CPU寄存器需要恢复原来保存用户态,然后再切换到用户空间,继续运行程序.
系统调用过程不涉及虚拟内存等进程用户态的资源,也不切换进程

  • 进程上下文切换是指从一个进程切换到另一个进程运行
  • 系统调用过程一直在同一个进程运行

进程是由内核来管理和调度,进程的切换只能发生在内核态,进程的上下文包括虚拟内存,栈,全局变量等用户空间的资源和内核堆栈,寄存器等内核空间的状态
进程的上下文切换:在保存当前进程的内核状态和CPU寄存器之前,需要将该进程的虚拟内存,栈等保存下来,加载了下一个进程的内核态后,需要刷下进程的虚拟内存和用户栈
进程上下文切换
进程上下文切换频繁,很容易导致CPU将大量时间耗费在寄存器,内核栈以及虚拟内存等资源的保存和恢复上,缩短了真正运行进程的时间,导致平均负载升高
Linux通过TLB(快表,虚拟地址到物理地址的转换表)管理虚拟内存到物理内存的映射关系,当虚拟内存更新后,TLB也需要更新,内存访问也会随之变慢.多处理器系统,缓存被多个处理器共享,刷新缓存不仅影响当前处理器的进程,也影响共享缓存其它处理器的进程.
进程上下文切换的时机:

  1. 进程时间片耗尽
  2. 系统资源不足,进程被挂起,先执行其他进程,等资源满足时再运行
  3. 进程sleep主动挂起
  4. 有更高优先级进程时
  5. 有硬件中断时,CPU上进程被挂起,转而执行内核中的中断服务程序

线程上下文切换

线程和进程的最大区别:线程是调度的基本单位,进程是资源拥有的基本单位

  • 当进程只有一个线程时,可以认为进程就等于线程
  • 当进程拥有多个线程时,这些线程共享相同的虚拟内存和全局变量资源,这些资源在上下文切换中不需要修改
  • 线程也有自己的私有数据(例如栈和寄存器等),在上下文切换时需要保存

线程上下文切换:

  1. 两个线程数据不同进程,资源不共享,上下文切换过程与进程上下文切换一样
  2. 两个线程属于同一个进程,有许多共享资源,切换时,虚拟内存等共享资源保持不动,只切换线程的私有数据,寄存器等不共享资源.

中断上下文切换

中断处理会打断进程的正常调度和执行,转而调用中断处理程序,响应设备事件
中断上下文切换不涉及进程的用户态,中断上下文只包括内核态中断服务程序处理所必须的状态,包括CPU寄存器,内核堆栈,硬件中断参数等

系统上下文切换情况的查看

vmstat主要用来分析系统内存使用情况,也可分析CPU上下文切换和中断次数

1
2
3
4
5
6
#每隔5s输出一次数据
$ 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
2 0 520 894220 230456 519308 0 0 2 29 33 53 0 0 99 0 0
0 0 520 893940 230460 519348 0 0 0 13 46 66 0 0 99 0 0

  • cs(context switch)是每秒上下文切换次数
  • in(interrupt)是每秒中断次数
  • r(Running or Runnable)是就绪队列长度(正在运行或等待CPU的进程数)
  • b(Blocked)是处于不可中断睡眠状态的进程数

vmstat给出系统总体的上下文切换情况,每个进程的详细情况,需要使用pisstat查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#每隔5s输出一组数据 -w参数报告任务切换情况
$ pidstat -w 5
Linux 3.10.0-862.14.4.el7.x86_64 (VM_187_252_centos) 01/15/2019 _x86_64_ (1 CPU)

11:55:15 PM UID PID cswch/s nvcswch/s Command
11:55:20 PM 0 3 0.80 0.00 ksoftirqd/0
11:55:20 PM 0 9 9.38 0.00 rcu_sched
11:55:20 PM 0 11 0.20 0.00 watchdog/0
11:55:20 PM 0 31 0.20 0.00 khugepaged
11:55:20 PM 0 219 0.80 0.00 kworker/0:1H
11:55:20 PM 0 221 0.20 0.00 kworker/u2:2
11:55:20 PM 0 249 0.80 0.00 jbd2/vda1-8
11:55:20 PM 0 698 1.20 0.00 smbd
11:55:20 PM 0 1306 0.40 0.00 cleanupd
11:55:20 PM 1000 25163 1.00 0.00 python36
11:55:20 PM 1000 25355 0.40 0.00 sshd
11:55:20 PM 1000 25634 0.20 0.00 sshd
11:55:20 PM 0 26461 1.20 0.00 kworker/0:0
11:55:20 PM 1000 26575 0.20 0.00 pidstat

  • cswch 每秒自愿上下文切换的次数 指进程无法获取所需资源,导致的上下文切换
  • nvcswch 每秒非自愿上下文切换的次数 指进程由于时间片已到等原因,被系统强制调度,进而发生的上下文切换
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
#-wt参数输出线程的上下文切换指标
$ pidstat -wt 5
Linux 3.10.0-862.14.4.el7.x86_64 (VM_187_252_centos) 01/16/2019 _x86_64_ (1 CPU)

12:10:12 AM UID TGID TID cswch/s nvcswch/s Command
12:10:17 AM 0 3 - 0.40 0.00 ksoftirqd/0
12:10:17 AM 0 - 3 0.40 0.00 |__ksoftirqd/0
12:10:17 AM 0 9 - 0.80 0.00 rcu_sched
12:10:17 AM 0 - 9 0.80 0.00 |__rcu_sched
12:10:17 AM 0 11 - 0.40 0.00 watchdog/0
12:10:17 AM 0 - 11 0.40 0.00 |__watchdog/0
12:10:17 AM 0 31 - 0.20 0.00 khugepaged
12:10:17 AM 0 - 31 0.20 0.00 |__khugepaged
12:10:17 AM 0 219 - 0.80 0.00 kworker/0:1H
12:10:17 AM 0 - 219 0.80 0.00 |__kworker/0:1H
12:10:17 AM 0 221 - 0.20 0.00 kworker/u2:2
12:10:17 AM 0 - 221 0.20 0.00 |__kworker/u2:2
12:10:17 AM 0 249 - 0.60 0.00 jbd2/vda1-8
12:10:17 AM 0 - 249 0.60 0.00 |__jbd2/vda1-8
12:10:17 AM 0 - 1308 1.00 0.00 |__tuned
12:10:17 AM 0 - 901 1.20 0.00 |__in:imjournal
12:10:17 AM 27 - 1278 1.99 0.00 |__mysqld
12:10:17 AM 27 - 1279 1.99 0.00 |__mysqld
12:10:17 AM 27 - 1280 1.99 0.00 |__mysqld
12:10:17 AM 27 - 1281 1.99 0.00 |__mysqld
12:10:17 AM 27 - 1282 1.99 0.00 |__mysqld
12:10:17 AM 27 - 1283 1.99 0.00 |__mysqld
12:10:17 AM 27 - 1284 1.99 0.00 |__mysqld
12:10:17 AM 27 - 1285 1.99 0.00 |__mysqld
12:10:17 AM 27 - 1286 1.99 0.00 |__mysqld
12:10:17 AM 27 - 1287 1.99 0.00 |__mysqld
12:10:17 AM 27 - 1289 1.00 0.00 |__mysqld
12:10:17 AM 27 - 1290 1.00 0.00 |__mysqld
12:10:17 AM 27 - 1291 0.20 0.00 |__mysqld
12:10:17 AM 27 - 1292 0.20 0.00 |__mysqld

CPU使用率

  • user(us),用户态CPU时间,不包含nice时间,包括了guest时间
  • nice(ni),低优先级用户态时间,即进程优先级被调整为1-19(nice取值范围为-20到19,数值越大优先级反而越低)之间时的CPU时间
  • 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),代表以低优先级运行虚拟机的时间

CPU使用率:除了空闲时间外的其他时间占总CPU时间的百分比
性能工具计算CPU使用率取一段时间的两次值,做差后,再计算这段时间内的平均CPU使用率

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

由于性能工具给出的都是一段时间平均CPU使用率,所以要注意间隔时间的设置.(多个性能工具对比分析时,要保证它们用的相同的间隔)

CPU使用率查看

  • top显示系统总体的CPU和内存使用情况,以及各个进程的资源使用情况
  • ps只显示各个进程的资源使用情况
  • pidstat专门分析每个进程占用系统资源的工具

CPU使用过高使用perf分析

perf是Linux下的性能分析工具,能够进行函数与指令级的热点查找

  • perf top实时显示占用CPU时钟最多的函数或指令,用来查找热点函数或者指令
  • perf record提供保存数据功能
  • perf report解析展示record的数据

perf top和perf record添加-g参数开启调用关系采样,方便根据调用链分析性能问题

常规方法无法找到CPU使用率问题

  1. 应用直接调用了其他二进制程序,运行时间较短,通过top等工具不易发现
  2. 应用本身不断崩溃重启,启动过程资源初始化,很可能占用相当多的CPU

可以通过pstree找到进程的父进程,再从父进程所在应用入手,排查问题根源

进程

进程状态

  1. R(Running或Runnable)表示进程在CPU的就绪队列中,正在运行或正在等待运行
  2. D(Disk Sleep)不可中断睡眠状态,一般表示进程正在跟硬件交互,并且交互过程不允许被其他进程或中断打断
  3. Z(Zombie)僵尸进程,进程实际已经结束了,但父进程还没有回收它的资源
  4. S(Interruptible Sleep)可中断睡眠状态,表示进程因为等待某个事件而被系统挂起.当进程等待的事件发生时,它将被唤醒并进入R状态
  5. I(Idle)空闲状态,用在不可中断睡眠的内核线程上.
  6. T或t(Stopped或Traced),表示进程处于暂停或者跟踪状态
  7. X(Dead)表示进程已经消亡(top或者ps命令中不会看到它)

dstat是一个全能系统信息统计工具

1
2
3
4
5
6
7
8
9
10
$ dstat 
You did not select any stats, using -cdngy by default.
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai hiq siq| read writ| recv send| in out | int csw
9 2 72 17 0 0|6902k 88k| 0 0 |1550B 22k| 501 500
96 4 0 0 0 0| 0 0 | 54B 154B| 0 0 |1120 450
96 4 0 0 0 0| 0 0 | 164B 1188B| 0 0 |1163 462
88 12 0 0 0 0| 0 0 | 164B 456B| 0 0 |1156 579
23 4 70 3 0 0| 12k 192k| 164B 456B| 0 0 | 503 631
1 2 95 2 0 0| 116k 0 |1090B 1300B| 0 0 | 267 353

僵尸进程使用pstree找出其父进程,在父进程中解决

软中断

中断是一种异步的事件处理机制,可以提高系统的并发处理能力
中断处理程序会打断其他进程的进行,所以,中断处理程序需要尽可能快的运行,减少对正常进程运行调度的影响
Linux将中断过程分为两个阶段,解决中断处理程序执行过长和中断丢失的问题

  • 上半部:快速处理中断,在中断禁止模式运行,主要处理跟硬件紧密相关的或时间敏感的工作,直接处理硬件请求,硬中断,快速执行
  • 下半部:延迟处理上半部未完成的工作,通常以内核线程的方式运行,由内核触发,软中断,延迟执行

软中断和内核线程查看

/proc/softirqs 软中断运行情况
/proc/interrupts 硬中断运行情况

软中断内核线程 ksoftirqd/CPU 编号

1
2
$ ps aux | grep ksoftirqd | grep -v grep
root 3 0.0 0.0 0 0 ? S Feb19 0:08 [ksoftirqd/0]

CPU性能

CPU性能指标

CPU性能指标

CPU性能工具

根据指标找工具

性能指标 工具 说明
平均负载 uptime top uptime最简单; top提供更全的指标
系统整体CPU使用率 vmstat mpstat top sar /proc/stat top vmstat mpstat只可以动态查看,sar可以记录历史数据 /proc/stat是其它性能工具的数据来源
进程CPU使用率 top pidstat ps htop atop top和ps可以按CPU使用率给进程排序,而pidstat只显示实际用了CPU的进程 htop和atop以不同颜色显示更直观
系统上下文切换 vmstat 除了系统上下文切换数,还提供运行状态和不可中断状态进程数量
进程上下文切换 pidstat -w参数
软中断 top /proc/softirqs mpstat top提供软中断CPU使用率 /proc/softirqs和mpstat提供各种软中断在每个CPU上的运行次数
硬中断 vmstat /proc/interrupts vmstat提供总的中断次数 /proc/interrupts提供各个中断在每个CPU上运行的基类次数
网络 dstat sar tcpdump dstat sar提供总的网络接收和发送情况 tcpdump动态抓取正在进行的网络通信
I/O dstat sar dstat和sar都提供了I/O的整体情况
CPU个数 /proc/cpuinfo lscpu lscpu更直观
事件剖析 perf execsnoop perf可以用来分析CPU的缓存以及内核调用链,execsnoop用来监控短时进程

根据工具查指标

性能指标 CPU性能指标
uptime 平均负载
top 平均负载、运行队列、整体的CPU使用率以及每个进程的状态和CPU使用率
htop top增强版,以不同颜色区分不同类型的进程,更直观
atop CPU/内存/磁盘/网络等各种资源的全面监控
vmstat 系统整体的CPU使用率/上下文切换次数/中断次数,包括处于运行和不可中断状态的进程数量
mpstat 每个CPU的使用率和软中断次数
pidstat 进程和线程的CPU使用率/中断上下文切换次数
/proc/softirqs 软中断类型和在每个CPU上的累计中断次数
/proc/interrupts 硬中断类型和在每个CPU上的累计中断次数
ps 每个进程的状态和CPU使用率
pstree 进程的父子关系
dstat 系统整体的CPU使用率
sar 系统整体CPU使用率,包括可配置的历史数据
strace 进程的系统调用
perf CPU性能事件剖析,调用链/CPU缓存/CPU调度等
execsnoop 监控短时进程

性能分析

CPU性能分析

CPU性能优化

CPU优化

  1. 应用程序优化
    1. 编译器优化,编译器优化选项 例如:gcc优化选项-O2
    2. 算法优化
    3. 异步处理
    4. 多线程代替多进程
    5. 善用缓存
  2. 系统优化
    1. CPU绑定
    2. CPU独占
    3. 优先级调整
    4. 为进程设置资源限制
    5. NUMA(Non-Uniform Memory Access)优化
    6. 中断负载均衡

Linux内存

Linux内存原理

Linux内存简介

Linux内存使用情况查看

free命令可以查看系统内存使用情况

1
2
3
4
5
6
7
8
$ free
total used free shared buff/cache available
Mem: 1882860 589828 387196 112 905836 1090592
Swap: 2047996 105736 1942260
$ free -m
total used free shared buff/cache available
Mem: 1838 576 377 0 885 1064
Swap: 1999 103 1896

Mem物理内存使用情况,Swap交换分区使用情况

  1. total总内存大小
  2. used已使用内存大小,包含共享内存
  3. free未使用内存大小
  4. shared共享内存大小
  5. buff/cache缓存和缓冲区大小
  6. available新进程可用内存大小,不仅包含未使用内存,也包括可回收的缓存,一般会比未使用内存大,但并不是所有缓存都可以被回收.

free显示整个系统内存使用情况,进程内存使用情况用top或ps等工具查看.

1
2
3
4
5
6
7
8
9
Tasks:  88 total,   1 running,  87 sleeping,   0 stopped,   0 zombie
%Cpu(s): 1.7 us, 0.7 sy, 0.0 ni, 97.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 1882860 total, 384436 free, 590704 used, 907720 buff/cache
KiB Swap: 2047996 total, 1942260 free, 105736 used. 1089708 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
22272 root 20 0 3438356 326068 6172 S 0.3 17.3 27:45.68 java
16200 root 20 0 2488224 55908 6636 S 0.3 3.0 8:51.48 java
538 CaseZhe+ 20 0 945436 52264 7216 S 0.3 2.8 1:31.74 vim
547 CaseZhe+ 20 0 1113148 51592 25008 S 0.0 2.7 0:00.80 python36

  1. VIRT进程虚拟内存大小,只要是进程申请过的内存,即使还没有真正分配物理内存,也计算在内
  2. RES常驻进程内存大小,进程实际使用的物理内存大小,但不包括Swap和共享内存
  3. SHR共享内存大小(和其他进程共同使用的共享内存/加载的动态链接库/程序代码段等)
  4. %MEM进程使用物理内存占系统总内存的百分比

参考