1,主要是使用top命令,可看出哪个进程CPU占有率过高,但嵌入式linux的top是由busybox编译出来的,属于轻量级。PC上有的功能,嵌入式平台上可能没有,譬如“top -H”等。
2,另外ps命令可以看到各个进程的pid,包括内核线程,内核线程看起来更像用户态的进程
3,知道哪个进程占用过高后,可试着在用户态上动态修改进程的优先级,如:renice -19 383,其中-19是新的nice值,该值范围为[-20:19],383是进程pid号
4,如果是内核的驱动,可使用set_user_nice()改变nice值
5,如果是用户态,可使用改变调度策略,如FIFO或者RR,并设置优先级
6,这样就把重要性较弱的进程调度优先级降下来,CPU占用率会有一定的降幅。在开发软件时,最好是根据linux thread api封装一个好用的接口,可以方便设置优先级,调度策略等,并用一个*.h头文件把各个模块的优先级使用宏来定义,方便修改和阅读各种任务的优先级以及实时性。
7,此时需要进一步定位该进程(假设进程ID为
)的哪些线程占用率过高。为了调试,最好是使用上述说的“自己封装并且好用的接口”来为新创建的线程起名字,内部或许使用了pthread_setname_np()等系统api来实现。此时使用pstree -p 可以查看该进程创建的所有线程。
8,使用proc文件系统进行统计线程的实际cpu占用。主要用到/proc/stat以及/proc//task//stat,下面是一个很好的例子,代码不是我写的,我改动了部分,仅做参考
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define VALID_PROCSTATE 0xa84b62fc
struct procState
{
char procname[32];
FILE * fp;
int pid;
unsigned int utimeold;
unsigned int utimenew;
unsigned int stimeold;
unsigned int stimenew;
unsigned int allold;
unsigned int allnew;
int valid;
};
#define MAX_THREADS_NUM 100
struct procState lanProc[MAX_THREADS_NUM];
int procTotal=0;
char targetProcName[64]="";
int targetProcPid=0;
char taskName[64]="";
FILE *taskfp;
FILE *statfp;
char *version = "1.1.0";
char *procdevice = "/proc/stat";
struct dirent *taskdirent;
DIR * taskdir;
struct stats
{
unsigned int user;
unsigned int nice;
unsigned int system;
unsigned int idle;
unsigned int total;
};
void readprocessstat( void );
int getdata(struct stats *st)
{
unsigned char buf[80];
int i;
for(i=0;iuser, &st->nice,&st->system, &st->idle);
fclose(statfp);
st->total = st->user + st->nice + st->system + st->idle;
for(i=0;i] [-c ] [-s seconds]
");
fprintf(fp, " -h? this help
");
fprintf(fp, " -v print version info
");
fprintf(fp, " -p pid of target proccess
");
fprintf(fp, " -c count repeat count times
");
fprintf(fp, " -s seconds seconds between output
");
exit(rc);
}
int main(int argc, char *argv[])
{
struct stats st, stold;
unsigned int curtotal;
int c = 0;
int cnt = 1;
int loop = 1;
int delay = 1;
int busy = 0;
int max_busy=0;
int i = 0;
if(argc < 2)
usage(stdout, 0);
while ((c = getopt(argc, argv, "h?vc:s:p:")) > 0)
{
switch (c) {
case 'v':
printf("%s: version %s
", argv[0], version);
exit(0);
case 'c':
loop = 0;
cnt = atoi(optarg) + 1;
break;
case 's':
delay = atoi(optarg);
break;
case 'h':
case '?':
usage(stdout, 0);
break;
case 'p':
//struct dirent {
// ino_t d_ino; /* inode number */
// off_t d_off; /* offset to the next dirent */
// unsigned short d_reclen; /* length of this record */
// unsigned char d_type; /* type of file */
// char d_name[256]; /* filename */
//};
sprintf(targetProcName, "%s", optarg);
sprintf(taskName, "/proc/%s/task", optarg);
if((taskdir = opendir(taskName))==NULL)
{
fprintf(stderr, "ERROR: failed to open %s, errno=%d
", taskName,errno);
exit(-1);
}
targetProcPid=atol(targetProcName);
for(i=0;id_name);// /proc/%s/task 下的文件名即为线程tid号
if( lanProc[procTotal].pid < targetProcPid ) //一般不会小于父进程id
{
continue;
}
sprintf(lanProc[procTotal].procname, "/proc/%s/task/%s/stat", optarg,taskdirent->d_name);
lanProc[procTotal].valid = VALID_PROCSTATE;
procTotal++;
}
break;
default:
fprintf(stderr, "ERROR: unkown option '%c'
", c);
usage(stderr, 1);
break;
}
}
getdata(&st);
for (c = 0; (loop || (c < cnt)); c++)
{
sleep(delay);
stold = st;
getdata(&st);
curtotal = st.total - stold.total;
busy = ((st.system + st.user + st.nice)-(stold.system + stold.user + stold.nice)) * 100 / curtotal;
if (max_busy < busy)
max_busy = busy;
if(c) //第一次不打印
printf("busy %3d%%:max=%3d%%(system %3d%%, user %3d%%, nice %3d%%, idle %3d%%)
",
busy,max_busy,
(st.system - stold.system) * 100 / curtotal,
(st.user - stold.user) * 100 / curtotal,
(st.nice - stold.nice) * 100 / curtotal,
(st.idle - stold.idle) * 100 / curtotal);
for(i=0;i
9,因proc下面的stat文件内容可读性比较差,上面的源码把stat文件解析出来,并算出总CPU时间,以及各线程的stime和utime,并在一段时间后(如1s后)再统计上述参数,最后将上述参数均求差(deta),类似于求斜率,(utime2-utime1)/(total_time2-total_time1)等。编译成嵌入式板可执行文件,如thread_cpu,运行./thread_top -p 可计算出ID为的进程内所有线程内核态cpu占用率和用户态占用率。pstree -p 能看到所有线程tid和线程名称。
10,通过运行上述命令,进程中cpu占用较高的线程被一一统计出来,根据tid号和pstree出来的线程名称对比,即可定位源代码了。当然使用pthread_setname_np()为线程起名字是为了给人看,但限制是名字最大不超过16个字符。如果是一般的调试也可以根据线程创建时返回的pthread_t类型id,使用系统api将其转换为tid号,如:
printf("proc_name=%s,tid=%ld
",__func__,syscall(224));
,这样也能对比出来,定位源代码!