1.java语言
通过监控系统观察到CPU占比高达400%,满负荷工作。4个CPU,每个CPU运行占比都到达100%了。于是就开始了我们的问题查找。
步骤一:通过top命令查找CPU占比较大的进程号,如图所示:
步骤二:top -H -p 5363 通过这个命令查看5363这个进程中的线程的cpu使用情况,如图所示:
第一列PID是线程号,10进制的,%CPU列是线程占用的CPU比例。
在计算机中,线程号是用十六进制表示,如果要查看线程号5484的调用链,需要把十进制的5484转为十六进制,可以执行这个命令:echo “obase=16;5484” | bc 执行完之后可以看到打印出 156C,表明5484这个十进制数对应的16进制是156C。如图所示:
注意:jstack是java的命令,只适用于java
接下来执行命令 jstack 5363 | grep -A 50 156C 就可以看到线程156C的此刻瞬间调用链。差不对类似下面的这个图吧(下面的图是线程号为0x32c9的瞬间调用链,找到nid=0x32c9,nid后面的值就是16进制的线程号)。
步骤三:执行这段命令 jstack 5363 | grep -A 50 async-task
就可以看到进程5363下所有的线程名字带有async-task的线程的调用链情况,类似下图这个样子吧(下图是进程24671的线程调用链情况,只是截了部分图)。
看到线程的调用链,进一步可以找到线程正在执行的问题代码、进而对问题代码进行更改。
2.c语言
1 先查看进程pid
ps aux | grep xxx
2 查看线程占用率情况
top -H -p pid
linux中的线程是一个轻量级进程,每个线程都有自己的pid。执行这个命令后可以看出占用率最高的线程的PID。
单看进程pid我们很难知道我们代码中的哪个线程出了问题,我们可以利用prctl(PR_SET_NAME, “xxx”)给线程起名。至此我们通过线程名,大概可以猜到问题出现在哪个线程了。
然而还不够。
3 分析出问题的接口
strace -f -p PID
或者pstack PID
PID为上面的命令查看到的占用CPU最高的线程的PID。
之后可以看出调用最频繁的接口。
举个例子吧。这是一段C代码
#include <pthread.h>
#include <stdio.h>
#include <sys/prctl.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/syscall.h>
void *thread_hndl1(void *arg)
{
prctl(PR_SET_NAME, "thread_1");
printf("i am thread_1 tid=0x%lx, pid=%d\n",
pthread_self(), syscall(SYS_gettid));
while(1)
{
usleep(10);
}
}
void *thread_hndl2(void *arg)
{
prctl(PR_SET_NAME, "thread_2");
printf("i am thread_2 tid=0x%lx, pid=%d\n",
pthread_self(), syscall(SYS_gettid));
while(1)
{
sleep(1);
}
}
int main()
{
pthread_t pid1,pid2;
pthread_create(&pid1, NULL, thread_hndl1, NULL);
pthread_create(&pid2, NULL, thread_hndl2, NULL);
pthread_join(pid1, NULL);
pthread_join(pid2, NULL);
}
先编译上面代码。
gcc -g -o debug_thread_mode debug_thread_mode.c -lpthread
运行程序
./debug_thread_mode
新开一个窗口,先查看debug_thread_mode的进程PID
ps aux | grep debug_thread_mode
进程号是21308,接着执行
top -H -p 21308
PID为21309,名字为thread_1(上面用prctl设置的)的线程cpu占用率最高。
接着执行
watch -n 1 pstack 21309
输出
问题大概出在nanosleep()接口
进一步验证,执行如下命令
strace -f -p 21309
输出
验证我们的猜想。