英创信息技术应用程序助手AppHelper使用介绍

为保证系统稳定运行,系统cpu应避免长时间满负荷运作,应用程序cpu占用不宜过高。客户需要在调试阶段监测应用程序各个进程线程占用情况,对占用过高的进程线程进行优化。因ce自身不带进程线程系统占用查看工具,我们增加了apphelper助手工具方便客户使用。
在之前的技术文章《ce应用程序助手简介》中简单介绍过英创apphelper应用程序助手,本文将详细介绍apphelper的使用方法。
apphelper查看方法
客户在自制底板上只要引出了网络,usbotg,debug调试串口,或板子其它串口任意之一便可以查看apphelper信息。
网络方式
通过telnet登录上板子,运行命令sysinfo,即可获得apphelper打印的进程线程信息。
telnet模式打印示例图
usbotg方式
使用ahc工具(使用方法见本文下一节)配置apphelper输出为com1。连接上板子usbotg口,板子将以虚拟串口形式被pc识别。使用任意串口工具向该串口输出任意三个字符(任意波特率),即可获得apphelper打印的进程线程信息。
usbotg,debug及其它串口打印示例图
debug调试串口方式
使用ahc工具(使用方法见本文下一节)配置apphelper输出为debug。连接板子的debug串口,pc端使用任意串口工具,设置波特率115200,向debug口输出任意三个字符,即可获得apphelper打印的进程线程信息。
串口方式
将底板上引出,且客户应用程序未使用的串口连接上pc。使用ahc工具(使用方法见本文下一节)配置好串口号及波特率。pc端使用任意串口工具,用设定的波特率向该串口输出任意三个字符,即可获得apphelper打印的进程线程信息。
ahc工具使用介绍
ahc工具即apphelper config工具,用于设置apphelper打印信息的输出位置。有两种办法进行设置。
控制面板方式
在板子控制面板中运行ahc工具。
选择好输出信息的串口及波特率(其中com1为usbotg),点击ok键保存配置,板子重启后配置生效。
telnet方式
通过telnet登录上板子,执行命令ahc port [baud]
参数port:串口号,值为0-6,0表示debug串口,1表示usbotg转虚拟串口,2-6分别表示板子的com2-com6。
参数baud:波特率,可选参数,如果不填表示保持原波特率,支持1200,2400,4800,9600,19200,38400,57600,115200。当port为0时,baud固定为115200,当port为1时,baud值不生效。
命令执行后,debug口可以看到打印提示信息。
打印格式说明
打印结果为数行,其中每行的格式均为:类型 id号 占用情况 名称
以下图一次打印的部分截图为例:
类型
pid表示为process进程。tid表示为上面进程下的thread线程。
id号
即进程id值或线程id值。
占用情况
显示格式为 k n% u m% total%
n值为该进程或线程在kernel系统层的占用
m值为该进程或线程在user用户层的占用
total值为总占用,它应当等于n+m的和
进程下各个线程total占用和应当等于进程的total占用
名称
进程名即exe的名称,线程默认没有名称,下一节会介绍如何给线程命名,从而能在apphelper中显示出来。
进程及线程监视说明
apphelper会打印系统下所有的进程的cpu占用信息。
只有在\nandflash目录下的exe生成的进程会额外打印出它下面所有线程的cpu占用信息。
默认情况下,生成的线程只有id号,没有名称,如果线程较多会不便于查看。我们可以通过简单代码给线程命名。
以光盘里的串口例程spt_hex为例:
添加一个结构体的定义
typedef struct _thread_index
{
dworddwsize;
dworddwthreadid;
tcharszthreadname[32];
_thread_index*pnext;
}thread_index;
在创建线程后给线程命名
这里把串口接收线程命名为commrecvtread
hrecvthread = createthread(0, 0, commrecvtread, this, 0, &m_dwtid);
handle hhlp;
dword dwlen;
hhlp = createfile(lhlp1:, generic_read | generic_write, 0, 0, open_existing, 0, 0);
thread_indexthreadindex;
wsprintf(threadindex.szthreadname, lcommrecvtread);
threadindex.dwthreadid = m_dwtid;
threadindex.dwsize = sizeof(thread_index);
writefile(hhlp, &threadindex, sizeof(thread_index), &dwlen, null);
closehandle(hhlp);
在结束线程后取消命名
线程结束后应当手动将命名取消掉,避免不必要的显示错误,设置线程名为空,即可取消原命名。
handle hhlp;
dword dwlen;
hhlp = createfile(lhlp1:, generic_read | generic_write, 0, 0, open_existing, 0, 0);
thread_indexthreadindex;
wsprintf(threadindex.szthreadname, l);
threadindex.dwthreadid = m_dwtid;
threadindex.dwsize = sizeof(thread_index);
writefile(hhlp, &threadindex, sizeof(thread_index), &dwlen, null);
closehandle(hhlp);
命名线程后再使用apphelper查看,启动接收线程后,就可以看到commrecvtread这个线程,另外个没有命名的线程为serialport程序的主线程。
计算原理及误差说明
cpu占用时间是通过计算一段时间内(apphelper设置为2000毫秒)cpu空闲tick值与这段时间里cpu运算周期tick值得出。
cpu空闲tick值 = cpu空闲tick计数t2 – cpu空闲tick计数t1
cpu总周期tick值 = cpu总周期tick计数t2 – cpu总周期tick计数t1
cpu占用 = 1 – (cpu空闲tick值/cpu总周期tick值)× 100%
进程或线程的cpu占用,是通过计算一段时间cpu运算周期tick值,和这段周期里kernel或user运行线程或进程的tick值,通过相除得到。
进程/线程kernel占用 = (进程/线程kernel运行tick值/cpu总周期tick值)× 100%
进程/线程user占用 = (进程/线程user运行tick值/cpu总周期tick值)× 100%
进程/线程cpu占用 = 进程/线程kernel占用 + 进程/线程user占用
打印结果可能会有少量误差,可能由于以下原因:
1、实验值计算到个位,小数部分四舍五入,所以可能产生细微的误差。
2、理想中的测量情况如下图
但是实际情况由于apphelper本身也会产生系统消耗,所以测量情况为下图
在δt值不为0的情况下,如果在δt期间各个tick值产生较大跳动时,测试结果可能产生误差。
3、各个进程或线程的运行tick值并非完全实时变化,而是在进程或线程完成一个时间片挂起后才加上,所以查询函数获得值不一定非常精确。
测试程序及说明
test_prc_thd是一个简单的程序,用来测试apphelper的进程线程监视功能。
“添加线程”按钮按下会创建一个新的线程。参数中传入线程编号,线程id等信息。
void ctest_prc_thddlg::onbnclickedbutton1()
{
dword dwtid;
handle htestthread;
m_threadparam[m_dwcnt].dwcnt = m_dwcnt;
m_threadparam[m_dwcnt].dwlv = m_dwcnt;
m_threadparam[m_dwcnt].bthreadstop = false;
htestthread = createthread(0, 0, testtread, &m_threadparam[m_dwcnt], create_suspended, &dwtid);
m_threadparam[m_dwcnt].dwthreadid = dwtid;
resumethread(htestthread);
closehandle(htestthread);
}
线程主函数里根据编号给线程自身命名,并且根据各自传入的参数执行负载率不等的计算。这里计算采用简单的循环计数,循环计算次数越多,线程cpu占用越多。
线程结束后,取消自身的命名。
dword ctest_prc_thddlg::testtread(lpvoid lparam)
{
thread_param *pthreadparam = (thread_param*)lparam;
dworddwlen;
thread_indexthreadindex;
handle hhlp;
volatile int n;
hhlp = createfile(lhlp1:, generic_read | generic_write, 0, 0, open_existing, 0, 0);
wsprintf(threadindex.szthreadname, lthread%d, pthreadparam->dwcnt);
threadindex.dwthreadid = pthreadparam->dwthreadid;
threadindex.dwsize = sizeof(thread_index);
writefile(hhlp, &threadindex, sizeof(thread_index), &dwlen, null);
closehandle(hhlp);
handlehfile;
wcharwsfilename[64];
inti;
while(!pthreadparam->bthreadstop)
{
for (i=0; idwcnt; i++ )
{
int j;
for (j=0;jdwthreadid;
threadindex.dwsize = sizeof(thread_index);
writefile(hhlp, &threadindex, sizeof(thread_index), &dwlen, null);
closehandle(hhlp);
return 0;
}
“结束所有线程”按钮按下,通知所有线程结束运行。
void ctest_prc_thddlg::onbnclickedbutton2()
{
int i;
for (i=0; i
{
m_threadparam[i].bthreadstop = true;
sleep(100);
}
m_dwcnt = 0;
}
测试例程,下图为该程序创建多个线程后的打印情况
该程序源码可以联系英创工程师获得,客户有疑问也可以向英创工程师咨询。

你见过机器人之间相互问答吗
锂电行业方形铝壳电芯在氦检方面存在以下痛点
金士顿DDR5内存通过英特尔内存解决方案_瑞虎8西伯利亚版上市发布
基于双口RAM的LonWorks智能通信节点设计
l3等级自动驾驶什么时候普及?
英创信息技术应用程序助手AppHelper使用介绍
2018版C-NCAP碰撞试验标准正式发布
中科创达与Autoware基金协会携手推进自动驾驶
基于SoftSSD的快速固态硬盘固件原型开发
国产FPGA低成本替代革命性Quantum架构助您摆脱芯片缺货
热电偶和热电阻,你真的都搞懂了吗?
柔性振动盘 视觉上料 anyfeeder
Nordic Nrf52840芯片为例看无线电传输和距离相关的两大关键因数
交叉极化干扰及其对抗措施
QA大战转折点?高通CEO:正在进行5G合作谈判,苹果能铁了心?
闭路电视监控系统防雷介绍
200G QSFP56高速线缆特性及发展前景
cpu怎么超频_超频后怎么帮cpu降热
Digi面向主流卫星开发推出业内首个M2M开发工具包
探讨工业互联网平台的发展现状和问题