暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

CPU武器库

济南小老虎 2025-04-26
167

背景

应用出问题排除网络和低级的程序bug.
最多的是两种情况: 
CPU爆掉了和内存爆掉了. 

CPU爆掉可以理解为是 指令周期的泄露/溢出
也就是未合理控制CPU的使用量. 

内存爆掉可以理解为是 服务内存使用过多.
也就是未合理控制内存的用量

两者都可以理解为 信用卡刷爆了,并且没有能力还上
只能破产(宕机)

本次先讲解一下判断CPU问题的武器库.


基础工具

ps && top 
Linux 系统里面最基础的进行CPU诊断的是 ps 和 top 
两者其实不仅可以判断CPU的使用, 也可以判断内存的使用.

ps -eo <列名>
其中,<列名> 是你希望显示的列的名称,可以是以下任意组合:
pid:进程ID
ppid:父进程ID
user:进程所有者
%cpu:CPU使用率
%mem:内存使用率
vsz:虚拟内存大小
rss:常驻内存大小
tty:终端设备
stat:进程状态
start:进程启动时间
time:进程运行时间
command:启动命令

top 是另外一个很常见的命令. 


ps查看信息

ps -eo pid,lstart,time,rss,cmd |grep 223148
223148 Fri Nov 15 18:01:15 2024 00:23:41 6856340 java 
分别说明:
grep 和 显示的第一个都是进程pid 
第一个是启动时间: 
Fri Nov 15 18:01:15 2024
第三个是进程消耗的CPU周期时间.
00:23:41
第四个是进程使用的rss内存信息,但是是KB
6856340,约合不到7GB的空间.
最后一列是 java进程具体命令, 这里么有展示. 


top简介

样例: 
top - 15:44:21 up 2 days,  7:24,  3 users,  load average: 0.01, 0.04, 0.07
Tasks: 331 total,   1 running, 330 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.4 sy,  0.0 ni, 99.6 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :  47003.4 total,  17973.3 free,  23365.7 used,   5664.4 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.  23016.3 avail Mem

第一行: 
15:44:21  当前系统时间
up 2 days,  7:24,  运行了 两天7小时24分钟
3 users,  load average: 0.01, 0.04, 0.07  有3个用户登录, 最近系统的压力信息分别为 0.01等. 

第二行:
正在运行的进程信息. 
一共331个进程, 其中一个正在运行 330个在睡眠状态. 

第三行:
CPU的使用信息
分别是用户态:  内核态:  通过改变优先级占用的比率: 空闲CPU: io等待时间: 硬中断时间: 软中断时间: 虚拟机被偷的运行时间.
需要说明, 一个好的应用程序, 肯定尽量使用用户态的CPU
如果内核态CPU比较高, 说明系统不停的进入内核态高权限进行运行,存在风险
数据库要重视 wa 指标, 太高了, 说明磁盘有问题. 他比io的util 更有用. 
id 其实是另外应该很重要的事项, 100 - id 可以理解为cpu在干活的时间比率. 

第四行:
内存的信息: 
比如这个是 MB 的信息. 总计内存, free的内存, 使用的内存, 以及 buffer/cache的内存. 
最后一行是包含了 swap 的内存信息, 其中 avail Mem 最关键. 
可以理解为是  free + buffer/cache 


top的使用方式之一

第一个 top -Hp $pid 
样例: top -Hp 223148
结果为: 
top - 16:01:15 up 2 days,  7:41,  3 users,  load average: 0.00, 0.02, 0.01
Threads: 310 total,   0 running, 310 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.1 us,  0.4 sy,  0.0 ni, 98.4 id,  0.0 wa,  0.1 hi,  0.1 si,  0.0 st
MiB Mem :  47003.4 total,  18072.3 free,  23265.5 used,   5665.6 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.  23116.6 avail Mem

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
 223149 root      20   0   19.0g   6.5g 107208 S   0.0  14.2   3:15.21 java
 223209 root      20   0   19.0g   6.5g 107208 S   0.0  14.2   0:52.80 C2 CompilerThre
 223210 root      20   0   19.0g   6.5g 107208 S   0.0  14.2   0:51.07 C2 CompilerThre
 223204 root      20   0   19.0g   6.5g 107208 S   0.0  14.2   0:50.91 C2 CompilerThre
 223205 root      20   0   19.0g   6.5g 107208 S   0.0  14.2   0:49.89 C2 CompilerThre
 223203 root      20   0   19.0g   6.5g 107208 S   0.0  14.2   0:49.47 C2 CompilerThre

查看具体进程下面的线程信息: 
跟top 一样, 其实有很多方式方法: 
输入 大写M 按照内存排序, 输入大写P 按照当前使用的CPU 排序 输入 大写 T 按照线程使用的CPU时间排序.
注意这个是进行性能检测诊断的很重要和快捷的方法. 

jdk 1.8. 202 + 以上的版本 可以显示 15个字节的线程信息. 可以区分线程的用途
与 jstack -l 里面的十六进制的线程号可以一一对应起来. 
但是他不需要jstack的处理, 并且可以看到具体线程的执行消耗的CPU的时间. 非常简洁.


top的使用方式之二

第二个: top -bn 1 
top 默认是显示不全的, 很多内容会被忽略掉
此时使用 top -bn 1 的方式可以将 top 的结果完整的打印出来,便于分析
跟 Hp 关联在一起, 可以看到所有进程的信息. 

跟计划任务或者是 for 循环关联起来, 可以实现动态监控. 
市面上的监控做不到 进程级别和线程级别的监控, 可以使用top的方式来实现. 

监测间隔时间内的CPU使用差异, 线程CPU的时间增量如果有自然时间增长相近
那大概率是最近卡顿的一个原因, 如果有大量的fullGC大概率是因为这个线程导致的. 

可以实现快速的大内存线程定位. 


perf

yum install perf -y
然后执行命令为: 
perf top -p 223148
部分结果为: 

Samples: 110  of event 'cpu-clock', 4000 Hz, Event count (approx.): 22151010 lost: 0/0
Overhead  Shared Object       Symbol
  28.27%  [kernel]            [k] finish_task_switch
   6.40%  libpthread-2.28.so  [.] pthread_cond_timedwait
   3.88%  [kernel]            [k] __lock_text_start
   3.60%  libjvm.so           [.] ObjectMonitor::wait
   2.84%  [kernel]            [k] _raw_spin_lock
   2.57%  libpthread-2.28.so  [.] 0x000000000000e5e0
   2.39%  libjvm.so           [.] Monitor::unlock
   2.32%  libjvm.so           [.] os::sleep
   2.31%  libjvm.so           [.] os::PlatformEvent::park
   2.26%  [kernel]            [k] do_futex

一般可以根据 这个 信息进行判断, 是否存在严重的性能风险. 


perf之二

perf stat -e task-clock -p $pid I 1000
类似结果为: 
perf stat -e task-clock -p 223148 -I 1000
#           time             counts unit events
     1.004622901               2.16 msec task-clock                #    0.002 CPUs utilized
     2.005543635               2.33 msec task-clock                #    0.002 CPUs utilized
     3.006027641               1.88 msec task-clock                #    0.002 CPUs utilized
     4.006799272               2.90 msec task-clock                #    0.003 CPUs utilized
     5.007645147               1.80 msec task-clock                #    0.002 CPUs utilized

参数说明 
-e 指定监控某些event
-I 指定报告间隔, 单位秒钟.
-p 指定进程号

需要说明 不指定 -e 的话 还能看到 io 和 cs 的信息
也比较关键.

然后可以使用excel将CPU使用情况展示出来.


pstack

pstack $pid 
可以查看 进程的堆栈信息. 
能够仔细查看部分内容. 

pstack  223148 > 1.txt

cat  1.txt  |grep '#0' |awk '{print $4}' |sort |uniq -c |sort -k1hr

    154 pthread_cond_wait
     98 epoll_wait
     53 pthread_cond_timedwait
      2 ??
      2 recv
      1 accept


sar

yum install sysstat -y 

使用 sar 直接查看当天的CPU使用情况. 
结果为默认十分钟的 平均值
可以通过
sar -f var/log/sa/sa15 查看15号的信息. 

sar -f var/log/sa/sa15 -b 可以查看io信息. 
-B 是page in 和out 的
-r 是内存相关

可以使用命令进行查看: sar --help
        -B      分页状况 [A_PAGE]
        -b      I/O 和传输速率信息状况 [A_IO]
        -d      块设备状况 [A_DISK]
        -F [ MOUNT ]
                文件系统统计信息 [A_FS]
        -H      巨大页面利用率 [A_HUGE]
        -I { <中断列表> | SUM | ALL }
                中断信息状况 [A_IRQ]
        -m { <关键字> [,...] | ALL }
                电源管理统计信息 [A_PWR_...]
                关键字:
                CPU     CPU 瞬时时钟频率
                FAN     风扇速度


ebpf

ebpf 是一套很好的工具
通过动态修改内核 能够实现低侵入的性能监控. 

主要工具有bcc 以及 直接python代码的写的工具
这一块需要后续完善. 


Grafana_Prometheus

如果技术能力比较弱的话
可以部署node-exporter 加上grafana_prometheus

可以通过网页查看一些监控信息. 

但是需要说明, 监控最多是趋势,可能较难将信息完全理解清晰. 


numactl

可以通过numactl -H 查看不同numa节点的内存使用情况
命令:  numactl -H
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 8
node 0 size: 515021 MB
node 0 free: 35960 MB
node 1 cpus: 24 25 26 27 28 29 30 31 32 
node 1 size: 515028 MB
node 1 free: 36516 MB
node distances:
node   0   1
  0:  10  20
  1:  20  10

注意 机器的CPU 其实核心是 2路24核心,共计 96个线程
我这边删除了部分CPU的ID

如果不同numa节点的内存数量不一致的话. 可能会有一定的性能问题. 


查看CPU的实际频率

find sys/devices/system/cpu -iname cpuinfo_cur_freq  -exec cat {} \; \
|awk '{sum += $1} END {printf "NR = %d,Average = %3.3f\n",NR,sum/NR}'

NR = 96,Average = 2801000.000
可以看到运行的频率, 如果是物理机 并且压力比较小 他的频率会比较低一些. 


proc的学习

cat > checkprocess.sh << EOF
let  uptime=\`cat proc/sched_debug  |grep ktime |awk '{print \$3}' |awk -F '.''{print \$1}'\`
let  uptime=uptime/10
let  pid=\`ps -ef |grep java |grep caf |head -n 1 |awk '{print \$2}'\`
for i in \`ls proc/\$pid/task \` ;
doecho -n \`cat proc/\$pid/task/\$i/stat |awk -F '(''{print \$2}' |awk -F ')''{print \$1}' \`  ;
echo -n " 线程号: "
echo -n \`cat proc/\$pid/task/\$i/stat |awk  '{print \$1}' \`  ;
echo -n " 线程的启动后的时间(秒钟): "
let threadtime=\`cat proc/\$pid/task/\$i/stat | awk -F ')''{print \$2}' |awk '{print \$20}'\` ;
let threadtime=(uptime-threadtime)/100 ;
echo -n  \$threadtime
echo -n " 线程占用的CPU时间(毫秒): "
let runingtime=\`cat proc/\$pid/task/\$i/sched |grep se.sum_exec_runtime |awk '{print \$3}' |awk -F '.''{print \$1}'\`
let runingtime=runingtime
echo \$runingtime
done
EOF


proc的说明

cat proc/sched_debug  |grep ktime 
查看开机之后的毫秒数.

cat proc/\$pid/task/\$i/stat |awk  '{print \$1}
java进程内线程的相对于开机节点的毫秒数

let threadtime=\`cat /proc/\$pid/task/\$i/stat | awk -F '
)' '{print \$2}' |awk '{print \$20}'\` ;
let threadtime=(uptime-threadtime)/100 ;
通过减法,计算出 java 特定线程的 运行市场, 也就是实际的存在时长. 

let runingtime=\`cat /proc/\$pid/task/\$i/sched |grep se.sum_exec_runtime |awk '
{print \$3}' |awk -F '.' '{print \$1}'\`
let runingtime=runingtime
echo \$runingtime
计算该进程的实际占用CPU的时间. 如果一个进程一直在占用CPU时间的话, 说明存在问题. 


文章转载自济南小老虎,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论