linux系统性能分析与优化

linux系统性能分析与优化

2022-11-24 13:43

背景

有时候会遇到⼀些疑难杂症,并且监控插件并不能⼀眼⽴⻢发现问题的根源。这时候就需要登录服务器进⼀步深⼊分析问题的根源。那么分析问题需要有⼀定的技术经验积累,并且有些问题涉及到的领域⾮常⼴,才能定位到问题。所以,分析问题和踩坑是⾮常锻炼⼀个⼈的成⻓和提升⾃我能⼒。

如果我们有⼀套好的分析⼯具,那将是事半功倍,能够帮助⼤家快速定位问题,节省⼤家很多时间做更深⼊的事情。

说明

本篇⽂章主要介绍各种定位的⼯具以及会结合案例分析怎样分析问题。

分析问题的⽅法论

套⽤5W2H⽅法,可以提出性能分析的⼏个问题

What-现象是什么样的 When-什么时候发⽣ Why-为什么会发⽣

Where-哪个地⽅发⽣的问题 How much-耗费了多少资源 How to do-怎么解决问题

cpu

说明

针对应⽤程序,我们通常关注的是内核CPU调度器功能和性能。

线程的状态分析主要是分析线程的时间⽤在什么地⽅,⽽线程状态的分类⼀般分为:

on-CPU:执⾏中,执⾏中的时间通常⼜分为⽤户态时间user和系统态时间sys。

off-CPU:等待下⼀轮上CPU,或者等待I/O、锁、换⻚等等,其状态可以细分为可执⾏、匿名换⻚、睡眠、锁、空闲等状态。

如果⼤量时间花在CPU上,对CPU的剖析能够迅速解释原因;如果系统时间⼤量处于off-cpu状态,定位问题就会费时很多。

但是仍然需要清楚⼀些概念:

处理器

硬件线程 CPU内存缓存

时钟频率

每指令周期数CPI和每周期指令数IPC CPU指令

使⽤率

⽤户时间/内核时间调度器

运⾏队列抢占

多进程多线程字⻓

分析⼯具

⼯具

描述

uptime

平均负载

vmstat

包括系统范围的cpu平均负载

mpstat

查看所有cpu核信息

top

监控每个进程cpu⽤量

sar -u

查看cpu信息

pidstat

每个进程cpu⽤量分解

perf

cpu剖析和跟踪,性能计数分析

说明:

uptime,vmstat,mpstat,top,pidstat只能查询到cpu及负载的的使⽤情况。

perf可以跟着到进程内部具体函数耗时情况,并且可以指定内核函数进⾏统计,指哪打哪。

使⽤⽅式

  1. //查看系统cpu使用情况
  2. top

3

  1. //查看所有cpu核信息
  2. mpstat -P ALL 1

6

  1. //查看cpu使用情况以及平均负载
  2. vmstat 1

9

  1. //进程cpu的统计信息
  1. pidstat -u 1 -p pid

12

  1. //跟踪进程内部函数级cpu使用情况
  2. perf top -p pid -e cpu-clock

内存

说明

内存是为提⾼效率⽽⽣,实际分析问题的时候,内存出现问题可能不只是影响性能,⽽是影响服务或者引起其他问题。同样对于内存有些概念需要清楚:

主存

虚拟内存常驻内存地址空间

OOM

⻚缓存缺⻚ 换⻚

交换空间交换

⽤户分配器libc、glibc、libmalloc和mtmalloc LINUX内核级SLUB分配器

分析⼯具

⼯具

描述

free

缓存容量统计信息

vmstat

虚拟内存统计信息

top

监视每个进程的内存使⽤情况

pidstat

显示活动进程的内存使⽤统计

pmap

查看进程的内存映像信息

sar -r

查看内存

dtrace

动态跟踪

valgrind

分析程序性能及程序中的内存泄露错误

说明:

free,vmstat,top,pidstat,pmap只能统计内存信息以及进程的内存使⽤情况。 valgrind可以分析内存泄漏问题。

dtrace动态跟踪。需要对内核函数有很深⼊的了解,通过D语⾔编写脚本完成跟踪。

使⽤⽅式

  1. //查看系统内存使用情况
  2. free -m

3

  1. //虚拟内存统计信息
  2. vmstat 1

6

  1. //查看系统内存情况
  2. top

9

  1. //1s采集周期,获取内存的统计信息
  2. pidstat -p pid -r 1

12

  1. //查看进程的内存映像信息
  2. pmap -d pid 15
  3. //检测程序内存问题
  4. valgrind --tool=memcheck --leak-check=full --log-file=./log.txt ./程序名 18

磁盘IO

说明

磁盘通常是计算机最慢的⼦系统,也是最容易出现性能瓶颈的地⽅,因为磁盘离 CPU 距离最远⽽且 CPU 访问磁盘要涉及到机械操作,⽐如转轴、寻轨等。访问硬盘和访问内存之间的速度差别是以数量级来计算的,就像1天和1分钟的差别⼀样。要监测 IO 性能,有必要了解⼀下基本原理和 Linux 是如何处理硬盘和内存之间的 IO 的。

在理解磁盘IO之前,同样我们需要理解⼀些概念,例如:

⽂件系统 VFS

⽂件系统缓存

⻚缓存page cache

缓冲区⾼速缓存buffer cache

⽬录缓存

inode inode缓存

分析⼯具

⼯具

描述

iostat

磁盘详细统计信息

iotop

按进程查看磁盘IO的使⽤情况

pidstat

按进程查看磁盘IO的使⽤情况

perf

动态跟踪⼯具

使⽤⽅式

  1. //查看系统io信息
  2. iotop

3

  1. //统计io详细信息
  2. iostat -d -x -k 1 10

6

  1. //查看进程级io的信息
  2. pidstat -d 1 -p pid

9

  1. //查看系统IO的请求,比如可以在发现系统IO异常时,可以使用该命令进行调查,就能指定到底是什么原因导致的IO异常
  2. perf record -e block:block_rq_issue -ag
  3. ^C
  4. perf report

⽹络

说明

⽹络的监测是所有 Linux ⼦系统⾥⾯最复杂的,有太多的因素在⾥⾯,⽐如:延迟、阻塞、冲突、丢包等,更糟的是与 Linux 主机相连的路由器、交换机、⽆线信号都会影响到整体⽹络并且很难判断是因为 Linux ⽹络⼦系统的问题还是别的设备的问题,增加了监测和判断的复杂度。现在我们使⽤的所有⽹卡都称为⾃适应⽹卡,意思是说能根据⽹络上的不

同⽹络设备导致的不同⽹络速度和⼯作模式进⾏⾃动调整。

分析⼯具

⼯具

描述

ping

主要透过 ICMP 封包 来进⾏整个⽹络的状况报告

traceroute

⽤来检测发出数据包的主机到⽬标主机之间所经过的⽹关数量的⼯具

netstat

⽤于显示与IP、TCP、UDP和ICMP协议相关的统计数据,⼀般⽤于检验本机各端⼝的⽹络连接情况

ss

可以⽤来获取socket统计信息,它可以显示和netstat类似的内容。但ss的优势在于它能够显示更多

更详细的有关TCP和连接状态的信息,⽽且⽐netstat更快速更⾼效

host,nslooku p

可以⽤来查出某个主机名的 IP

tcpdump

是以包为单位进⾏输出的,阅读起来不是很⽅便

tcpflow

是⾯向tcp流的, 每个tcp传输会保存成⼀个⽂件,很⽅便的查看

sar -n DEV

⽹卡流量情况

sar -n SOCK

查询⽹络以及tcp,udp状态信息

使⽤⽅式

  1. //显示网络统计信息
  2. netstat -s

3

  1. //显示当前UDP连接状况
  2. netstat -nu

6

  1. //显示UDP端口号的使用情况
  2. netstat -apu

9

  1. //统计机器中网络连接各个状态个数
  2. netstat -a | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 12
  3. //显示TCP连接
  4. ss -t -a 15
  5. //显示sockets摘要信息
  6. ss -s

18

  1. //显示所有udp sockets
  2. ss -u -a

21

22

//tcp,etcp状态

23

sar -n TCP,ETCP 1

24

25

//查看网络IO

26

sar -n DEV 1

27

28

//抓包以包为单位进行输出

29

tcpdump -i eth1 host 192.168.1.1

and port 80

30

31

//抓包以流为单位显示数据内容

32

tcpflow -cp host 192.168.1.1

系统负载

说明

Load 就是对计算机⼲活多少的度量(WikiPedia:the system Load is a measure of the amount of work that a com pute system is doing)

简单的说是进程队列的⻓度。Load Average 就是⼀段时间(1分钟、5分钟、15分钟)内平均Load

分析⼯具

⼯具

描述

top

查看系统负载情况

uptime

查看系统负载情况

strace

统计跟踪内核态信息

vmstat

查看负载情况

dmesg

查看内核⽇志信息

使⽤⽅式

  1. //查看负载情况
  2. uptime

3

4 top

5

6 vmstat

7

  1. //统计系统调用耗时情况
  2. strace -c -p pid 10
  3. //跟踪指定的系统操作例如epoll_wait
  4. strace -T -e epoll_wait -p pid 13
  5. //查看内核日志信息
  6. dmesg

⽕焰图

说明

⽕焰图(Flame Graph)是 Bredan Gregg 创建的⼀种性能分析图表,因为它的样⼦近似⽕⽽得名。

⽕焰图主要是⽤来展示 CPU的调⽤栈。

y 轴表示调⽤栈,每⼀层都是⼀个函数。调⽤栈越深,⽕焰就越⾼,顶部就是正在执⾏的函数,下⽅都是它的⽗函数。

x 轴表示抽样数,如果⼀个函数在 x 轴占据的宽度越宽,就表示它被抽到的次数多,即执⾏的时间⻓。注意,x 轴不代表时间,⽽是所有的调⽤栈合并后,按字⺟顺序排列的。

⽕焰图就是看顶层的哪个函数占据的宽度最⼤。只要有”平顶”(plateaus),就表示该函数可能存在性能问题。颜⾊没有特殊含义,因为⽕焰图表示的是 CPU 的繁忙程度,所以⼀般选择暖⾊调。

常⻅的⽕焰图类型有On-CPUOff-CPU,还有 MemoryHot/ColdDifferential 等等。

安装依赖库

  1. //必须按照systemtap,默认系统已安装
  2. yum install systemtap systemtap-runtime 3
  3. //内核调试库必须跟内核版本对应,例如:uname -r 2.6.18-308.el5
  4. kernel-debuginfo-2.6.18-308.el5.x86_64.rpm
  5. kernel-devel-2.6.18-308.el5.x86_64.rpm
  6. kernel-debuginfo-common-2.6.18-308.el5.x86_64.rpm

8

  1. //安装内核调试库
  2. debuginfo-install --enablerepo=debuginfo search kernel
  3. debuginfo-install --enablerepo=debuginfo search glibc

安装步骤

  1. git clone https://github.com/lidaohang/quick_location.git
  2. cd quick_location

CPU级别⽕焰图

cpu占⽤过⾼,或者使⽤率提不上来,你能快速定位到代码的哪块有问题吗?

⼀般的做法可能就是通过⽇志等⽅式去确定问题。现在我们有了⽕焰图,能够⾮常清晰的发现哪个函数占⽤cpu过⾼,或者过低导致的问题

on-CPU ⽤户态

cpu占⽤过⾼,执⾏中的时间通常⼜分为⽤户态时间user和系统态时间sys。

使⽤⽅式

1

  1. //on-CPU user
  2. sh ngx_on_cpu_u.sh pid 4
  3. //进入结果目录
  4. cd ngx_on_cpu_u

7

  1. //on-CPU kernel
  2. sh ngx_on_cpu_k.sh pid 10
  3. //进入结果目录
  4. cd ngx_on_cpu_k

13

  1. //开一个临时端口8088
  2. // python3中SimpleHTTPServer改为http.server
  3. python -m SimpleHTTPServer 8088

17

  1. //打开浏览器输入地址
  2. 127.0.0.1:8088/pid.svg

20

DEMO

  1. #include <stdio.h>
  2. #include <stdlib.h>

3

  1. void foo3()
  2. {
  3. }

7

  1. void foo2()
  2. {
  3. int i;
  4. for(i=0 ; i < 10; i++)

12 foo3();

13 }

14

  1. void foo1()
  2. {
  3. int i;
  4. for(i = 0; i< 1000; i++)

19 foo3();

20 }

21

  1. int main(void)
  2. {
  3. int i;
  4. for( i =0; i< 1000000000; i++) {

26 foo1();

27 foo2();

28 }

29 }

DEMO⽕焰图

image

off-CPU ⽤户态

cpu过低,利⽤率不⾼。等待下⼀轮CPU,或者等待I/O、锁、换⻚等等,其状态可以细分为可执⾏、匿名换⻚、睡眠、锁、空闲等状态。

使⽤⽅式

  1. // off-CPU user
  2. sh ngx_off_cpu_u.sh pid 3
  3. //进入结果目录
  4. cd ngx_off_cpu_u

6

  1. //off-CPU kernel
  2. sh ngx_off_cpu_k.sh pid 9
  3. //进入结果目录
  4. cd ngx_off_cpu_k

12

  1. //开一个临时端口8088
  2. python -m SimpleHTTPServer 8088

15

  1. //打开浏览器输入地址
  2. 127.0.0.1:8088/pid.svg

18

image

内存级别⽕焰图

如果线上程序出现了内存泄漏,并且只在特定的场景才会出现。这个时候我们怎么办呢?有什么好的⽅式和⼯具能快速的发现代码的问题呢?同样内存级别⽕焰图帮你快速分析问题的根源。

使⽤⽅式

  1. sh ngx_on_memory.sh pid 2
  2. //进入结果目录
  3. cd ngx_on_memory

5

  1. //开一个临时端口8088
  2. python -m SimpleHTTPServer 8088

8

  1. //打开浏览器输入地址
  2. 127.0.0.1:8088/pid.svg

11

image

性能回退-红蓝差分⽕焰图

你能快速定位CPU性能回退的问题么? 如果你的⼯作环境⾮常复杂且变化快速,那么使⽤现有的⼯具是来定位这类问题是很具有挑战性的。当你花掉数周时间把根因找到时,代码已经⼜变更了好⼏轮,新的性能问题⼜冒了出来。

主要可以⽤到每次构建中,每次上线做对⽐看,如果损失严重可以⽴⻢解决修复。

通过抓取了两张普通的⽕焰图,然后进⾏对⽐,并对差异部分进⾏标⾊:红⾊表示上升,蓝⾊表示下降。 差分⽕焰图是以当前(“修改后”)的profile⽂件作为基准,形状和⼤⼩都保持不变。因此你通过⾊彩的差异就能够很直观的找到差异部分,且可以看出为什么会有这样的差异。

使⽤⽅式

  1. cd quick_location

2

  1. //抓取代码修改前的profile 1文件
  2. perf record -F 99 -p pid -g -- sleep 30
  3. perf script > out.stacks1 6

7

  1. //抓取代码修改后的profile 2文件
  2. perf record -F 99 -p pid -g -- sleep 30
  3. perf script > out.stacks2 11
  4. //生成差分火焰图:
  5. ./FlameGraph/stackcollapse-perf.pl ../out.stacks1 > out.folded1
  6. ./FlameGraph/stackcollapse-perf.pl ../out.stacks2 > out.folded2
  7. ./FlameGraph/difffolded.pl out.folded1 out.folded2 | ./FlameGraph/flamegraph.pl > diff2.svg 16

DEMO

  1. //test.c
  2. #include <stdio.h>
  3. #include <stdlib.h>

4

  1. void foo3()
  2. {
  3. }

8

  1. void foo2()
  2. {
  3. int i;
  4. for(i=0 ; i < 10; i++)

13 foo3();

14 }

15

  1. void foo1()
  2. {

18

19

20

21 }

22

int i;

for(i = 0; i< 1000; i++) foo3();

  1. int main(void)
  2. {
  3. int i;
  4. for( i =0; i< 1000000000; i++) {

27 foo1();

28 foo2();

29 }

30 }

31

  1. //test1.c
  2. #include <stdio.h>
  3. #include <stdlib.h>

35

  1. void foo3()
  2. {
  3. }

39

  1. void foo2()
  2. {
  3. int i;
  4. for(i=0 ; i < 10; i++)

44 foo3();

45 }

46

  1. void foo1()
  2. {
  3. int i;
  4. for(i = 0; i< 1000; i++)

51 foo3();

52 }

53

  1. void add()
  2. {
  3. int i;
  4. for(i = 0; i< 10000; i++)

58 foo3();

59 }

60

61

int main(void)

62

{

63

int i;

64

for( i =0; i<

1000000000; i++) {

65

foo1();

66

foo2();

67

add();

68

}

69

}

DEMO红蓝差分⽕焰图

image

参考资料

http://www.brendangregg.com/index.html http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html http://www.brendangregg.com/FlameGraphs/memoryflamegraphs.html http://www.brendangregg.com/FlameGraphs/offcpuflamegraphs.html http://www.brendangregg.com/blog/2014-11-09/differential-flame-graphs.html https://github.com/openresty/openresty-systemtap-toolkit https://github.com/brendangregg/FlameGraph https://www.slideshare.net/brendangregg/blazing-performance-with-flame-graphs

posted @ 2023-04-25 09:56  XU-NING  阅读(44)  评论(0编辑  收藏  举报