1、背景介绍
目前各平台通过各自的脚本获取数据,然后通过其他工具对数据进行展示,方案已能够满足当前的项目需求,但跨平台数据统计项和数据展示方式不一致,可对比性较差。
当前的监控方案是通过shell脚本,定期执行top命令收集cpu使用信息,执行showmem命令收集内存使用信息,然后保存到日志文件中。最后将日志文件下载到PC并导入Excel对数据进行筛选和分析,展示出cpu和内存的使用情况。
该方案实现简单,数据采集完全通过shell脚本完成,数据分析由Excel完成,但存在以下不足:
A、脚本通用性较差,因为在linux平台和QNX平台需要使用不同的命令获取内存信息
B、数据分析逻辑不能通用,不同平台获取的数据格式存在差异
C、使用top命令获取cpu使用信息,不能控制采样间隔,每次获取的是系统的top占用,不利于统计待机的cpu占用,数据存在误差
2、新的方案
统一通过代码获取cpu和memory数据,以获取更好的性能,减少系统资源的消耗,并能够按PID进行性能统计,增强数据的实时和有效性。同时提供扩展,便于将来支持更多的统计内容。在数据收集层,优先采用系统API和文件系统,无法实现时再考虑使用shell命令实现。
3、数据收集
(1)CPU使用数据
目前Android平台通过读取 /proc/${pid}/stat 文件获取cpu使用信息,linux平台也可采用相同的方案,在QNX平台,需要读取 /proc/${pid}/as 文件获得cpu信息
(2)Memory数据
对于单个进程的内存使用数据,Android平台实用的是 getProcessMemoryInfo 接口获取内存占用信息,linux平台和QNX平台没有提供这个API,需要自行设计实现。Linux平台可通过 /proc/${pid}/statm 文件获取,QNX平台可通过 /proc/${pid}/vmstat 文件获取。
对于系统总体的内存使用数据,Android平台和linux平台通过读取 /proc/meminfo 文件获取,在QNX平台,通过读取 /proc 目录的大小获得内存使用信息
4、QNX CPU 获取
(1)单个CPU整体占用算法
Pid为1的进程为Idle进程,通过计算Idle进程的CPU使用时间得出CPU的使用情况。
ProcFd =open( "/proc/1/as", O_RDONLY );
使用 DCMD_PROC_TIDSTATUS 参数读取Idle进程下各个thread的信息,其中tid和cpu一一对应,tid = 1 对应 cpu0。单cpu情况下 tid = 1 即可获得全部数据。
debug_data.tid = 1;
devctl( ProcFd,DCMD_PROC_TIDSTATUS, &debug_data, sizeof( debug_data ), NULL );
通过 debug_data.sutime 可以获取该进程的cpu占用时间,这个时间是从该进程启动以来的总时间,要计算当前的实时cpu占用情况,需要进行定期采样。默认采用500ms采用周期(采样周期可通过配置文件修改),计算两次的差值即可得到采样周期内该进程的cpu使用时间。
假设第一次采样时间记为 sutime1,第二次采样记为 sutime2,采样间隔记为 time_delta。则采样周期内cpu占用为
100.0 - ((float)( (sutime2 - sutime1) * 100 ) / (float)time_delta )
(2)多个CPU整体占用算法
对于多个CPU,分别计算单个CPU的使用情况,Idle进程的不同tid对应使用了不同的CPU。
根据CPU数量循环对不同Idle进程的thread进行计算,计算出每个cpu的占用率。
总体占用率为: 各个CPU占用率总和 / CPU数量。
统计数据除总体占用数据外,还需要记录各个CPU的数据以供后续分析。
通过读取对应pid的文件,可以获取该进程的相关信息
ProcFd =open( "/proc/${pid}/as", O_RDONLY );
使用 DCMD_PROC_INFO 参数读取该进行的详细信息
devctl(fd,DCMD_PROC_INFO, info, sizeof(info), 0)
其中 info.start_time, info.utime, info.stime 分别记录了该进程的启动时间,cpu占用时间和系统占用时间,这些都是从进程启动以来的总体时间,因此和前面一样,需要采样才能获取当前时刻的实时占用情况。
这里也可以通过 info.num_threads 获得该进程的线程数量信息,然后通过DCMD_PROC_TIDSTATUS参数获得每个线程的cpu占用情况
debug_data.tid= i ;
devctl(ProcFd, DCMD_PROC_TIDSTATUS, &debug_data, sizeof( debug_data ), NULL );
5、QNX 内存获取
(1)总内存占用
系统总内存的获取参考QNX源码,通过系统指针_syspage_ptr->asinfo.entry_size 获取。
staticuint64_tget_total_mem(void) {
char *str = SYSPAGE_ENTRY(strings)->data;
structasinfo_entry *as = SYSPAGE_ENTRY(asinfo);
uint64_t total = 0;
unsigned num;
for(num = _syspage_ptr->asinfo.entry_size /sizeof(*as); num > 0;--num) {
if(strcmp(&str[as->name],"ram") == 0) {
total += as->end - as->start+ 1;
}
++as;
}
return total;
}
QNX系统通过 /proc 文件夹的大小映射系统内存的占用情况,因此只需要读取这个文件夹的大小就可以获得总内存的使用情况
stat("/proc", &buf );
memused= buf.st_size;
(2)进程内存占用
可通过读取/proc/${pid}/vmstat 文件获取,读取该文件可获得以下数据
as_stats.map_size
as_stats.map_phys
as_stats.map_shared
as_stats.map_private
更多的详细信息可以通过读取 /proc/${pid} 文件夹获取,通过DCMD_PROC_PAGEDATA 参数可以获取进程的 page mapping
devctl(fd, DCMD_PROC_PAGEDATA, mp, mpsize, &num)
附录
(1)/proc/pid/stat
包含了所有CPU活跃的信息,该文件中的所有值都是从系统启动开始累计到当前时刻。
[root@localhost ~]# cat /proc/6873/stat
6873 (a.out) R 6723 6873 6723 34819 6873 8388608 77 0 0 0 41958 31 0 0 25 03 0 5882654 1409024 56 4294967295 134512640 134513720 3215579040 0 2097798 0 00 0 0 0 0 17 0 0 0 [root@localhost ~]#
每个参数意思为:
参数 解释
pid=6873 进程(包括轻量级进程,即线程)号
comm=a.out 应用程序或命令的名字
task_state=R 任务的状态,R:runnign, S:sleeping (TASK_INTERRUPTIBLE), D:disksleep (TASK_UNINTERRUPTIBLE), T: stopped, T:tracing stop,Z:zombie, X:dead
ppid=6723 父进程ID
pgid=6873 线程组号
sid=6723 c该任务所在的会话组ID
tty_nr=34819(pts/3) 该任务的tty终端的设备号,INT(34817/256)=主设备号,(34817-主设备号)=次设备号
tty_pgrp=6873 终端的进程组号,当前运行在该任务所在终端的前台任务(包括shell 应用程序)的PID。
task->flags=8388608 进程标志位,查看该任务的特性
min_flt=77 该任务不需要从硬盘拷数据而发生的缺页(次缺页)的次数
cmin_flt=0 累计的该任务的所有的waited-for进程曾经发生的次缺页的次数目
maj_flt=0 该任务需要从硬盘拷数据而发生的缺页(主缺页)的次数
cmaj_flt=0 累计的该任务的所有的waited-for进程曾经发生的主缺页的次数目
utime=1587 该任务在用户态运行的时间,单位为jiffies
stime=1 该任务在核心态运行的时间,单位为jiffies
cutime=0 累计的该任务的所有的waited-for进程曾经在用户态运行的时间,单位为jiffies
cstime=0 累计的该任务的所有的waited-for进程曾经在核心态运行的时间,单位为jiffies
priority=25 任务的动态优先级
nice=0 任务的静态优先级
num_threads=3 该任务所在的线程组里线程的个数
it_real_value=0 由于计时间隔导致的下一个 SIGALRM 发送进程的时延,以 jiffy 为单位.
start_time=5882654 该任务启动的时间,单位为jiffies
vsize=1409024(page) 该任务的虚拟地址空间大小
rss=56(page) 该任务当前驻留物理地址空间的大小
Number of pages the process has in real memory,minu 3 for administrativepurpose.
这些页可能用于代码,数据和栈。
rlim=4294967295(bytes) 该任务能驻留物理地址空间的最大值
start_code=134512640 该任务在虚拟地址空间的代码段的起始地址
end_code=134513720 该任务在虚拟地址空间的代码段的结束地址
start_stack=3215579040 该任务在虚拟地址空间的栈的结束地址
kstkesp=0 esp(32 位堆栈指针) 的当前值, 与在进程的内核堆栈页得到的一致.
kstkeip=2097798 指向将要执行的指令的指针, EIP(32 位指令指针)的当前值.
pendingsig=0 待处理信号的位图,记录发送给进程的普通信号
block_sig=0 阻塞信号的位图
sigign=0 忽略的信号的位图
sigcatch=082985 被俘获的信号的位图
wchan=0 如果该进程是睡眠状态,该值给出调度的调用点
nswap 被swapped的页数,当前没用
cnswap 所有子进程被swapped的页数的和,当前没用
exit_signal=17 该进程结束时,向父进程所发送的信号
task_cpu(task)=0 运行在哪个CPU上
task_rt_priority=0 实时进程的相对优先级别
task_policy=0 进程的调度策略,0=非实时进程,1=FIFO实时进程;2=RR实时进程
(2)/proc/pid/statm
[root@localhost proc]# cat /proc/1/statm
487 185 133 31 0 67 0
参数解释:
size:任务虚拟地址空间大小
Resident:正在使用的物理内存大小
Shared:共享页数
Trs:程序所拥有的可执行虚拟内存大小
Lrs:被映像倒任务的虚拟内存空间的库的大小
Drs:程序数据段和用户态的栈的大小
dt:脏页数量
公众号:itest_forever
CSDN:http://blog.csdn.net/itest_2016
QQ群:274166295(爱测未来2群)、610934609(爱测未来3群)