Android系统性能监控最全面分析与实践(一)
1、背景
随着移动互联网技术的发展,安卓APP的功能越来越多,对于APP性能的要求也随之提高。目前有很多应用性能监控(APM:Application perfmance monitor)的工具,如阿里的mobileperf,网易开源的Emmagee,腾讯的Matrix等等。
以上主流的性能监控是针对APP层,对安卓系统性能的监控也非常重要。谈及系统性能,我们很容易想到CPU使用率、CPU使用率TOP5的进程、内存、内存占用TOP5进程、网络速度、磁盘速度这6个常见的系统性能指标,本文针对这6个指标,通过查阅资料和实践,在应用层开发出一套更高效低耗的Android系统性能通用监控工具,本文也争取成为当前网上分析“Android系统性能监控指标获取和上报”最全面的文章。
2、方案实现
2.1 方案需求分析
结合上述背景分析,为在应用层实现“Android系统性能监控指标获取和上报”这个目标,在此说明本文的需求分析:
-
高效低耗低介入:开发一个系统性能监控工具,自然不希望该工具本身为系统性能造成负担,所以在保证完成性能监控的同时还要考虑工具本身的性能。要避免使用耗时较长或者阻塞的方法,同时该功能应该在业务最少介入的情况下,完成性能指标获取和上报;
-
本地保存:因为CPU使用率等指标都需要较频繁采集,需要将获取到的数据本地保存,等收集到一定数量数据再一次性压缩上报到后台;
-
灵活配置:不同指标获取的频率不同,因此针对不同指标能配置不同的获取频率;同时该监控功能可灵活配置开启和停止,以保证只在规定时间内进行性能监控,其他时间不会对系统造成任何影响。
2.2 方案流程框图
- 设置6个指标获取和上报的间隔时间,每个指标可以独立配置;
- 配置获取间隔,不配置上报间隔:仅获取数据,并作本地保存;
- 不配置获取间隔,配置上报间隔:不获取数据,则上报动作无效;
- 根据获取间隔获取对应指标数据保存在本地,达到上报时间则读取文件,将获取到的数据压缩上报到后台;
- 重复以上动作直到服务结束,期间可以灵活修改获取和上报时间;
3、CPU性能指标获取
CPU是性能监控最重要的指标之一,可以更好的帮我们监控设备的真实使用情况。其中CPU使用率的获取更是老生常谈的事情,那么关于CPU,有哪些指标对分析设备性能有帮助呢?在此推荐以下指标:
- CPU整体使用率和每个核的使用率;
- CPU有效核数;
- 实时CPU使用率最高的5个进程名;
3.1 CPU整体使用率和每个核的使用率
方案一:top指令
说到CPU使用率,熟悉linux或者Android的小伙伴第一个想到的办法一定是top指令,因为top指令非常强大,如下图我们可以知道,系统当前有432个进程,总CPU使用率为800%,其中697%处于空闲状态,其中占用CPU使用率最高的是system_server进程,占用了23.6%的CPU。由此可见top指令可以监视系统整体和各个进程的CPU使用率,但无法获取每个核的使用率,且比较的耗时,因此并不是最好的选择。
方案二:ps指令
ps(Process Status的缩写)指令是最基本同时也是非常强大的进程查看指令,可以查询系统内每个进程的运行状态、CPU使用率、占用的内存等,因为现在需要获取CPU使用率,可以执行:
ps -eo pid,%cpu,CMD --sort=-%cpu |head -n 6
指令解析:使用-o参数可自定义格式,这里只显示进程号、CPU使用率、进程名,并按CPU使用率降序排序,最后显示前5个进程。
ps指令耗时是毫秒级别,优于top指令,然而也无法获取整体或者每个核的使用率,并不适合现在的需求。
方案三:dumpsys cpuinfo
Android提供的dumpsys工具可以用于查看感兴趣的系统服务信息与状态,dumpsys cpuinfo可以用来查看安卓系统当前的cpu使用情况,执行的速度是非常快的,因为其打印的内容并非执行该命令的时候临时去更新的,而是系统内部有着相关的机制会在特定情况更新cpuinfo。为方便查看下图经过裁剪,我们可以看到系统整体CPU使用率和前5个进程的使用率,还缺少每个核的使用率,因此还不是最优的方案。不过在3.3小节dumpsys cpuinfo另有他用,其实现原理也可见下文介绍。
方案四:读取/proc/stat文件
既然说是老生常谈,那么读取/proc/stat文件来计算CPU使用率是目前最常见最有用的方法,Android源码获取CPU使用率也同样采用这种方法。虽然网上有很多资料,在此我们简单的重温一遍原理。proc系统文件是动态从系统内核读出所需要的信息并提交的,/proc/stat文件包含了所有CPU活动的信息,该文件中的所有值都是从系统启动开始累计到当前时刻,文件的内容如下:
cpu 38248 2957 53495 7615534 1767 0 116 0 0 0
cpu0 11928 595 17683 901362 510 0 31 0 0 0
cpu1 9638 536 15581 931712 424 0 37 0 0 0
cpu2 6578 404 9512 948917 305 0 20 0 0 0
cpu3 4607 296 5976 958486 240 0 10 0 0 0
cpu4 1718 262 1397 973129 91 0 7 0 0 0
cpu5 1519 206 1116 973438 66 0 4 0 0 0
cpu6 1022 462 1402 953706 56 0 4 0 0 0
cpu7 1236 193 825 974780 71 0 1 0 0 0
...
上述数值的单位是jiffies,这是内核中的一个全局变量,用来记录自系统启动一来产生的节拍数,在linux中,一个节拍大致可理解为操作系统进程调度的最小时间片,不同linux内核可能值有不同,通常在1ms到10ms之间。我们以第一行为例做介绍:
- user (38248) 从系统启动开始累计到当前时刻,处于用户态的运行时间,不包含 nice值为负进程
- nice (2957) 从系统启动开始累计到当前时刻,nice值为负的进程所占用的CPU时间
- system (53495) 从系统启动开始累计到当前时刻,处于核心态的运行时间
- idle (7615534) 从系统启动开始累计到当前时刻,除IO等待时间以外的其它等待时间
- iowait (1767) 从系统启动开始累计到当前时刻,IO等待时间
- irq (0) 从系统启动开始累计到当前时刻,硬中断时间
- softirq (116) 从系统启动开始累计到当前时刻,软中断时间
计算方法:
- 采样两个足够短的时间隔的cpu快照,记为t1,t2.每一个cpu快照均为(user、nice、system、idle、iowait、irq、softirq)的7元组;
- 计算出两个时刻的总的cpu时间,即上述7元组相加,分别记作:totalCpuTime1、totalCpuTime2
- 计算出两个时刻的非空闲时间,即上述7元组除了idle值的其他值点相加,分别记作:occupiedCpuTime1、occupiedCpuTime2
- 计算cpu使用率 = 100*(occupiedCpuTime2 – occupiedCpuTime1) / (totalCpuTime2 – totalCpuTime1)(按100%计算,如果是多核情况下还需乘以cpu的个数)
无论是cpu整体使用率还是cpu0到cpu7单个核的使用率都可以使用这种方法计算出对应的使用率。
结论
获取CPU使用率方法 | 能否获取多核使用率 | 效率 | 实现难度 | 推荐 |
---|---|---|---|---|
top指令 | 否 | 低 | 易 | |
ps指令 | 否 | 高 | 易 | |
dumpsys cpuinfo | 否 | 高 | 易 | |
/proc/stat文件 | 能 | 高 | 中 | ✔ |
3.2 CPU有效核数
当前设备有效核数虽然无法提供性能监控上直观的表现,但却是必不可少的数据,应用层直接调用Runtime.getRuntime().availableProcessors()即可获取。
3.3 实时CPU使用率最高的5个进程
除了获取CPU整体使用率知道系统大致的负载情况,还可以获取当前时间系统内占用CPU最大的若干个进程进一步分析,在此以获取实时CPU使用率最高的5个进程为例。
方案一:top指令
3.1小节说到top指令可以监视系统整体和各个进程的CPU使用率,并且是实时的,然而耗时太长。
方案二:ps指令
3.1小节说到ps指令可以查询每个进程的CPU使用率,再根据CPU使用率降序排序就可以获得TOP5进程。然而实践之后,发现实时性很差。比如开启某个占用CPU使用率较高的进程,通过top指令可以在TOP5进程查到该进程,但通过ps指令需要等待一段时间才可以查到。通过查询man手册,找到了原因,关键信息如下:
- %CPU:it is the CPU time used divided by the time the process has been running (cputime/realtime ratio), expressed as a percentage. 由此可知通过ps获取的CPU使用率是该进程从运行到现在,这一整段时间的CPU平均使用率,而非进程实时的使用率。
方案三:读取/proc/[pid]/stat
该文件包含了某一个进程的所有活动信息,该文件中所有值都是从系统启动开始累计到当前时刻,因为数值较多,此处仅介绍需要用到的4个参数,详细可见参考文档:《花式读取Android CPU使用率》。
- pid:进程ID
- utime:该进程处于用户态的时间,单位:jiffies
- stime:该进程处于内核态的时间,单位:jiffies
- cutime:该进程等待子进程的utime,单位:jiffies
- cstime:该进程等待子进程的stime,单位:jiffies
因此该进程的总CPU时间 = utime + stime + cutime + cstime,该数值包含该进程所有线程的CPU时间。某一进程CPU使用率的计算方式:
- 采样两个足够短的时间隔的cpu快照与进程快照:
- 每一个cpu快照均为(user、nice、system、idle、iowait、irq、softirq)的7元组;
- 每一个进程快照均为 (utime、stime、cutime、cstime)的4元组;
- 分别计算出两个时刻的总的CPU时间与线程的CPU时间,分别记作:totalCpuTime1、totalCpuTime2、processCpuTime1、processCpuTime
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏不详细讲解Android基础知识,网上非常多资料,仅整理记录在CVTE工作中,遇到的20个实际问题和解决思路,包括但不仅限于源码分析,思路剖析,处理流程等。本专栏的文章都经过脱敏处理,因笔者也正处于逐渐深入理解Android系统的阶段,学习该专栏可以了解Android开发实际处理的需求和Bug(仅代表个人),欢迎各位童鞋私聊交流安卓/嵌入式技术,欢迎加入CVTE。