【Android Linux内存及性能优化】(八) 系统性能分析工具

本文接着
【Android Linux内存及性能优化】(一) 进程内存的优化 - 堆段
【Android Linux内存及性能优化】(二) 进程内存的优化 - 栈段 - 环境变量 - ELF
【Android Linux内存及性能优化】(三) 进程内存的优化 - ELF执行文件的 数据段-代码段
【Android Linux内存及性能优化】(四) 进程内存的优化 - 动态库- 静态库
【Android Linux内存及性能优化】(五) 进程内存的优化 - 线程
【Android Linux内存及性能优化】(六) 系统内存的优化
【Android Linux内存及性能优化】(七) 程序内存泄漏检查工具

二、系统性能分析

2.1 性能分析

程序性能的问题,有很多原因,需要对症下药。
导致软件性能低下,主要有下面3种原因:

  1. 程序的运算量很大,导致CPU 过于繁忙,CPU 是瓶颈。
    可以运行 top 命令,如果某个进程的CUP 利用率很高,则说明CPU 是性能瓶颈。

  2. 程序需要大量的I/O ,读写文件、内存操作等,CPU 更多的是处于等待,I/O 部分成为程序性能瓶颈。
    对于大量I/O 引起的程序性能问题,可以学习这篇文章: 《使用异步 I/O 大大提高应用程序的性能

  3. 程序之间相互等待,结果CPU 利用率很低,但运行速度依然很慢,程序间的共享与死锁制约了程序的性能。
    如果系统的CPU 利用率并不高,而且也不存在大量的I/O 操作,那么很有可能是多个线程之间相互等待造成的,这时就需要对程序进行大规模的重构。


2.1.1 /proc 目录

通过proc 目录,能够了解到CPU 和I/O 设备的工作状况,从而能够帮助分析导致程序性能低下的原因。


2.1.1.1 系统相关 cat /proc/stat

ciellee@sh:~$ cat /proc/stat 
累计时间  user  nice system   idle   iowait  irq  softirq
cpu  3167348 29348 1381731 80468196 1075970 0 101955 0 0 0
cpu0 397571 3476 157335 10150112 70357 0 35871 0 0 0
cpu1 372224 3435 174119 10136025 51305 0 16722 0 0 0
cpu2 418781 3775 195023 10131454 32896 0 7197 0 0 0
cpu3 381767 4442 166107 10172208 33756 0 14511 0 0 0
cpu4 376322 3423 160088 10192147 30325 0 3210 0 0 0
cpu5 388663 3460 158360 10202394 29673 0 4082 0 0 0
cpu6 435006 3761 212003 9410045 713497 0 5559 0 0 0
cpu7 397011 3573 158693 10073807 114157 0 14800 0 0 0
intr 185329212 9 479 0 0 0 0 0 0 1 5007429 0 0 0 0 0 0 0 60384 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1954
ctxt 550116756
btime 1590973874
processes 536879
procs_running 1
procs_blocked 0
softirq 198450776 16075871 60900311 42960 3505868 1818711 0 1749208 63006387 0 51351460

CPU 后面数值分别代表着CPU 在不同状态下所用的时间,其单位为 jiffy (0.01s),7个数值的含义分别是:

  • user :从系统启动开始累计到当前的时间,用户态的CPU 时间,不包含 nice 值为负的进程。
  • nice :从系统启动开始累计到当前时刻,nice 值为负的进程所占用的CPU 时间。
  • system :从系统启动开始累计到当前时刻,内核所占用的CPU 时间
  • idle :从系统启动开始累计到当前时刻,除硬盘 IO 等待时间以外其他等待时间。
  • iowait :比系统启动开始累计到当前时刻,硬盘IO 等待时间。
  • irq :从系统启动开始累计到当前时刻,硬中断时间。
  • softirq :从系统启动开始累计到当前时刻,软中断时间。

由此可以推导出:
CPU 时间 = user + nice + idle + iowait + irq + softirq
CPU 的利用率 = 1 - (idle)/(user + system + nice + iowait + irq + softirq)

根据CPU 的利用率,可以知道当前系统的CPU 负载情况。

从这些数据,可以知道分析性能瓶颈在哪里:

  1. 程序代码有问题,导致占用了大量的CPU ,可以计算CPU 用户态利用率
    CPU 用户态利用率 = (user + nice)/(user + system + nice + idle + iowait + irq + softirq)

  2. 程序代码调用了大量的系统调用,导致Linux 内核占用了大量的CPU
    CPU 内核态利用率 = (system)/(user + system + nice + idle + iowait + irq + softirq)

  3. 系统和Flash 、内存等 有大量的交互和等待,从而导致系统性能下降
    IO 利用率 = (iowait)/(user + system + nice + idle + iowait + irq + softirq)


2.1.1.2 系统相关 cat /proc/loadavg

ciellee@sh:~$ cat /proc/loadavg
1.97 2.14 1.74 2/1048 6774

上面数字中:
1.97 	:	1分钟平均负载
2.14 	:	5分钟平均负载
1.74 	:	15分钟平均负载
2		:	在采样时刻,运行队列的任务的数量,与/proc/stat 的procs_running 表示相同意思
1048 	:	在采样时刻,系统中活跃的任务的个数(不包括运行已经结束的任务)
6774	:	最大的 PID 值,包括轻量级进程,即线程

2.1.1.3 进程相关 cat /proc/51/stat

ciellee@sh:~$ cat /proc/51/stat
51 (migration/7) S 2 0 0 0 -1 69238848 0 0 0 0 0 11 0 0 
-100 0 1 0 4 0 0 18446744073709551615 0 0 0 0 0 0 0 
2147483647 0 0 0 0 17 7 99 1 0 0 0 0 0 0 0 0 0 0 0

每个参数解析如下;
pid = 50 				进程(线程)pid号
comm = migration		应用程序或命名的名字
task_state=S			任务的状态:R:running,S:sleeping,D:disk sleep,
								  T:stopped,T:tracing stop,Z:zombie,X:dead
ppid = 2				父进程ID
pgid = 0				线程组号
sid = 0					该任务所在的会话组ID
ttnr = 0				该任务的tty终端的设备号,
tty_pgrp = -1 			终端的进程组号,当前运行在该任务所在终端的前台任务(包括 shell 应用程序)的pid
task->flags = 69238848	进程标志位,查看该任务的特性
min_flt = 0				该任务不需要从硬盘拷数据而发生的缺页(次缺页)次数
cmin_flt = 0			累计的该任务的所有的waited-for 进程曾经发生的次缺页的数目
maj_flt = 0				该任务需要从硬盘拷数据而发生的缺页(主缺页)次数
cmaj_flt = 0			累计的该任务的所有的waited-for 进程曾经发生的主缺页的数目
utime = 0				该任务在用户态运行的时间,单位为 jiffy
stime = 11				该任务在核心态运行的时间,单位为 jiffy
cutime = 0 				累计的该任务的所有的waited-for 进程曾经在用户态运行的时间,单位为 jiffy
cstime = 0				累计的该任务的所有的waited-for 进程曾经在核心态运行的时间,单位为 jiffy
priority = -100			任务的动态优先级
nice = 0				任务的静态优先级
num_threads = 1			该任务所在的线程组里线程的个数
it_real_value = 0		由于计时间隔导致下一个SIGALRM 发送进程的时延,以jiffy 为单位
start_time = 4			该任务的启动的时间,单位为 jiffy
vsize = 0				该任务的虚拟地址空间大小
rss = 0(page)			该任务当前驻留虚拟地址空间的大小
vsize = 18446744073709551615(bytes)	该任务的虚拟空间大小
rss = 0(page)			该任务当前驻留物理地址空间的大小
rlim = 0(bytes)			该任务能驻留物理地址空间的最大值
start_code = 0			该任务在虚拟地址空间的代码段的起始地址
end_code = 0			该任务在虚拟地址空间的代码段的结束地址
start_stack = 0			该任务在虚拟地址空间的栈的结束地址
kstkesp = 0				esp(32位堆栈指针)的当前值,与在进程的内核堆栈页得到的一致	
kstkeip = 0				指向将要执行的指令的指针,EIP(32位指令指针)的当前值
pendingsig = 2147483647	待处理信号的位图,记录发产送给进程的普通信号
block_sig = 0			阻塞信号的位图
sigign = 0				忽略的信号的位图
sigcatch = 0			被俘获的信号的位图
wchan = 0				如果该进程是睡眠状态,该值给出调度的调用点
nswap = 17				被 swapped 的页数,当前没用
cnswap = 7				所有子进程被swapped 的页数和,当前没用
exit_signal = 99		该进程结束时,向父进程所发送的信号
task_cpu(task) = 1		运行在哪个CPU 上
task_rt_priority = 0	实时进程的相对优先级别
task_policy = 0			进程的调度策略,0:非实时进程,1:FIFO实时进程,2:RR实时进程

通过文件stat 的utime, stime, cutime 和 cstime 的数值,能够计算出进程的CPU 占用率。

要想获得CPU 占用率,需要两个采样点:
采样点1: 系统时间记为 sys1, 进程时间分别为:utime1,stime1,cutime1,cstime1
采样点2: 系统时间记为 sys2, 进程时间分别为:utime2,stime2,cutime2,cstime2

进程CPU 占用率 = ( (utime2 + stime2 - cutime2 - stime2) - (utime1 + stime1 - cutime1 - stime1) ) / (sys2 - sys1)
进程用户态占用率 = ( (utime2 - cutime2) - (utime1 - cutime1) ) / (sys2 - sys1)
进程内核态占用率 = ( (stime2 - stime2) - (stime1 - stime1) ) / (sys2 - sys1)


2.1.1.4 进程相关 top

top 是最常用来监控系统范围内进程活动的工具,它提供运行在系统上的与CPU 关系最密切的进程列表,以及许多有意义的统计值(如负载平均,进程数量 以及 使用的 存储器和页面空间的数量)。

Tasks: 302 total,   1 running, 229 sleeping,   0 stopped,   0 zombie
%Cpu(s):  3.7 us,  1.6 sy,  0.0 ni, 93.2 id,  1.3 wa,  0.0 hi,  0.1 si,  0.0 st
KiB Mem : 16281312 total,  2675480 free,  2865172 used, 10740660 buff/cache
KiB Swap: 16605180 total, 16598768 free,     6412 used. 12037752 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                           
 3571 ciellee   20   0 2139236 191748  40460 S  23.5  1.2  96:04.80 compiz                                                                            
 1141 root      20   0  580232 185248 137744 S  17.6  1.1  41:17.93 Xorg                                                                              
 3315 ciellee   20   0  827724 302988  15876 S  11.8  1.9   1:40.13 hud-service                                                                       
 3515 ciellee   20   0 2036500 104556  49964 S  11.8  0.6   1:17.75 nautilus                                                                          
 2521 ciellee   20   0 2951112 209128  36744 S   5.9  1.3 112:49.49 WeChat.exe                                                                        
 3270 ciellee   20   0  516464  48528  18988 S   5.9  0.3   0:23.38 bamfdaemon                                                                        
 3341 ciellee   20   0  704340 120900  24328 S   5.9  0.7   7:01.88 unity-panel-ser                                                                   
24020 ciellee   20   0   24928   4140   3404 R   5.9  0.0   0:00.03 top                                                                               
    1 root      20   0  175892   5764   3220 S   0.0  0.0   0:03.46 systemd                                                                           
    2 root      20   0       0      0      0 S   0.0  0.0   0:00.13 kthreadd                                                                          
    4 root       0 -20       0      0      0 I   0.0  0.0   0:00.00 kworker/0:0H                                                                      
    6 root       0 -20       0      0      0 I   0.0  0.0   0:00.00 mm_percpu_wq
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章