Linux CPU 占用率 100% 排查实战:从 top 到 perf 的完整路径 简介这份PDF资料面向Linux运维工程师与后端开发人员聚焦生产环境中CPU占用率居高不下的排查与解决属于实战型排错指南。内容围绕top、ps、jstack等常用工具展开给出两套定位高CPU线程的完整思路并附一个Java进程CPU飙至300%的真实故障案例从进程排序、线程定位到十六进制转换、堆栈分析逐步演示同时补充Zabbix、Nagios及云监控告警的选型建议。资源包共1个PDF文件约147KB篇幅精炼、便于随查随用。目前已有4449人学习下载适合需要快速掌握Linux查看CPU占用率、定位问题线程与堆栈分析方法的读者可作为日常运维排障的参考手册。1. 线上告警响了CPU 占用率 100% 到底该从哪一刀切进去凌晨两点收到告警一台跑了业务容器的 Linux 机器 CPU 占用率顶到 100%SSH 还能连上但敲命令已经开始卡顿。这种场景下最忌讳的就是上来就reboot——重启能解决 90% 的问题但也会把现场证据一起清掉下次同样的故障还会再来一遍。CPU 占用率较高问题的排查核心不是背命令而是建立一条从「确认现象」到「定位进程」再到「定位代码/系统调用」的收敛路径。这篇笔记面向的是需要在生产环境里真正把问题摁下去的运维和开发不是面试八股。我会按我实际处理故障的顺序把每个环节用什么命令、看哪个字段、参数怎么调、哪里容易翻车讲清楚。国产 Linux 发行版麒麟、统信等和主流发行版在排查工具上基本通用差异我会在涉及的地方点出来。2. 先搞清楚 CPU 占用率是怎么算出来的别被 top 的第一行骗了2.1 用户态、内核态、iowait、软中断四个数字对应四类病因top第一行%Cpu(s)那一串数字很多人只盯着us实际上它拆成了好几个维度每个维度指向完全不同的排查方向字段含义偏高时优先怀疑us用户态 CPU 时间应用代码死循环、计算密集任务、正则回溯sy内核态 CPU 时间频繁系统调用、上下文切换、锁竞争ni调整过优先级的进程占用被nice调过优先级的批处理任务wa等待 I/O 完成的时间磁盘慢、NFS 挂载卡住、数据库刷盘hi硬中断网卡收包量大、驱动问题si软中断网络协议栈处理、ksoftirqd跑满st被虚拟化层偷走的时间云主机/虚拟机超卖宿主机抢资源一个常见的误判看到wa高就以为是 CPU 问题其实 CPU 根本没在干活是磁盘拖后腿。反过来st高的时候你在虚拟机里怎么优化都没用得找云厂商。所以第一步永远是先分清是「真忙」还是「假忙」。2.2 用 vmstat 和 mpstat 交叉验证避免单点误判top是采样快照有抖动。我一般会同时开vmstat看趋势# 每 2 秒采样一次共采 10 次观察 r 列和 cs 列 vmstat 2 10关键列解读r是运行队列长度持续大于 CPU 核数说明有任务在排队等 CPUcs是上下文切换次数每秒几万次以上要警惕in是中断次数。如果r很大但us不高可能是大量短命进程在抢 CPU。# 按核看定位是哪颗核在忙 mpstat -P ALL 2 5mpstat的价值在于单核跑满和多核均匀跑满病因完全不同。单核 100% 常见于单线程死循环、绑核任务多核均匀高则是整体负载问题。这一步做完你心里应该已经有了「是用户态还是内核态、是单核还是多核、有没有 I/O 掺和」的判断。提示容器环境里top看到的 CPU 是宿主机视角容器实际可用核数受 cgroup 限制用cat /sys/fs/cgroup/cpu.maxcgroup v2或cpu.cfs_quota_usv1确认配额否则会把配额打满误判成机器整体过载。3. 定位到具体进程和线程从 top 到 pidstat 的收敛路径3.1 top 交互模式里那几个必须会的按键很多人用top只看默认排序其实交互模式里几个键能省大量时间按P按 CPU 占用排序默认就是但排序被打乱后按回来按H切换显示线程这一步是关键进程级看不到的线程级热点在这里暴露按1展开每颗逻辑核的占用按c显示完整命令行避免只看到java看不到启动参数按u再输入用户名只看某个用户的进程找到可疑 PID 后如果开了H直接能看到是哪个 TID线程 ID在烧 CPU。Java 应用里这个 TID 转成十六进制就是 jstack 里的 nid这是后面关联代码的桥梁。3.2 pidstat 做进程级和线程级的定量采样top是实时的pidstat能给你一段时间的平均值更适合写进故障报告# 每 1 秒采样共 5 次-u 看 CPU-t 看线程-p 指定进程 pidstat -u -t -p 12345 1 5输出里%usr和%system分开列能直接看出这个进程是用户态烧还是内核态烧。%CPU超过 100% 说明是多线程合计别惊讶。如果%system特别高八成是系统调用太频繁下一步就该上strace。# 统计进程在一段时间内各系统调用的耗时分布 strace -c -p 12345strace -c会汇总每个 syscall 的调用次数和耗时。如果看到某个 syscall 被调了几百万次基本就锁定问题了。注意strace本身有性能开销生产环境别长时间挂着采几秒就CtrlC。3.3 内核态占用高时perf 是最后的黑匣子当sy高、strace又看不出明显异常时用perf抓内核调用栈# 采样 30 秒记录调用链 perf record -g -p 12345 -- sleep 30 # 生成报告看热点函数 perf reportperf report里按开销排序能看到具体是哪个内核函数在消耗 CPU。常见的热点有锁竞争mutex、spinlock、内存回收kswapd、网络协议栈。这一步需要内核符号表国产 Linux 上如果提示找不到符号装对应的kernel-debuginfo包。注意perf在部分虚拟化环境和受限容器里权限不足报perf_event_paranoid相关错误时临时sysctl -w kernel.perf_event_paranoid-1可以放开但排查完记得改回去。4. 高频场景对症下药死循环、频繁 GC、软中断、僵尸进程4.1 用户态单线程死循环从 TID 到代码行这是最经典的一类。现象是某颗核 100%、us高、sy正常。定位链路top -H拿到高占用 TID → 转十六进制 → 在对应语言的栈工具里找。Java 应用# 把十进制 TID 转成十六进制jstack 里的 nid 是十六进制 printf %x\n 12345 # 抓栈grep 刚才的 nid jstack 12340 | grep -A 30 nid0x3039C/C 或 Go 应用直接用gdbattach 上去bt看栈或者用perf top -p PID实时看热点函数。找到函数后再回去看代码通常是循环条件写错、正则 catastrophic backtracking、或者某个while忘了退出条件。4.2 Java 频繁 Full GC 导致的 CPU 飙高Java 服务 CPU 高但栈上看不出死循环时先看 GC# 每 1 秒打印一次 GC 统计连续 10 次 jstat -gcutil 12340 1000 10重点看FGCFull GC 次数和FGCTFull GC 总耗时是否在短时间内快速增长。如果 Full GC 频繁CPU 大量花在垃圾回收上us自然高。这时候要 dump 堆看是谁在制造垃圾# 生成堆快照注意会 STW生产环境挑低峰期 jmap -dump:formatb,file/tmp/heap.hprof 12340堆文件拿回本地用 MAT 或 VisualVM 分析找占用最大的对象和它的引用链。常见根因是缓存没设上限、大对象频繁创建、ThreadLocal没清理。4.3 软中断 si 高网卡收包把 CPU 吃光si高、ksoftirqd进程占用靠前多半是网络流量大或网卡中断分配不均。先看中断分布# 查看每个 CPU 核上的中断次数 cat /proc/interrupts | grep -i eth如果所有网卡中断都压在 CPU0 上就是中断亲和性没配好。用irqbalance服务自动均衡或者手动绑# 把中断号 128 绑到 CPU 2 和 3掩码 0xc echo c /proc/irq/128/smp_affinity再配合ethtool -L eth0 combined 8调整多队列让收包分散到多核。这类问题在万兆网卡和高并发网关机器上特别常见。4.4 僵尸进程和 D 状态进程不占 CPU 但拖垮系统严格说僵尸进程Z 状态不占 CPU但大量僵尸会耗尽 PIDD 状态不可中断睡眠进程通常卡在 I/O 上会让wa升高、负载虚高。排查# 找出 D 状态进程 ps -eo pid,stat,wchan:20,comm | awk $2 ~ /D/wchan列显示进程卡在哪个内核函数配合cat /proc/PID/stack看内核栈能定位是卡在磁盘、NFS 还是锁上。僵尸进程则要找它的父进程父进程不wait就会一直留着处理方式是修复父进程或重启父进程。5. 排查路上的避坑清单这些坑我踩过不止一次5.1 坑一直接 kill -9 把现场毁了现象定位到高 CPU 进程后手快kill -9问题暂时消失但根因没找到第二天复发。原因-9不给进程清理机会堆栈、临时文件、连接状态全丢。解决先kill -15让它优雅退出并留日志实在不行再-9排查阶段优先用gdb/jstack/perf抓现场别急着杀。5.2 坑二把 iowait 当成 CPU 忙现象top显示 CPU 使用率高实际us很低wa很高优化代码毫无效果。原因wa是 CPU 空闲等 I/O 的时间被某些监控工具算进了「使用率」。解决看vmstat的b列阻塞进程数和iostat -x 1的%util确认是磁盘瓶颈就换 SSD、调 I/O 调度器或优化读写逻辑别在 CPU 上使劲。5.3 坑三容器里看到的是宿主机的 CPU现象容器内top显示 32 核实际 cgroup 只给了 2 核按 32 核去评估负载完全错。原因/proc/cpuinfo和top默认读宿主机信息。解决用cat /sys/fs/cgroup/cpu.max确认配额监控采集时用 cgroup 数据而非/proc/stat评估负载按配额核数算。5.4 坑四strace 挂太久把生产拖垮现象为了抓 syscall 长时间strace -p结果被跟踪进程性能下降几倍引发二次故障。原因strace通过 ptrace 拦截每次系统调用开销巨大。解决只在必要时短时采样几秒或用perf trace替代生产环境优先用perf这种低开销工具。5.5 坑五忽略 st 偷走的时间现象云主机上 CPU 高但进程自身占用不高怎么优化都没用。原因ststeal time是虚拟化层把 CPU 分给了别的租户你的虚拟机在排队。解决vmstat看st列持续大于 5% 就找云厂商或者升级实例规格、换独享型实例本地优化无解。6. 把排查做成可复用的脚本和监控下次告警 5 分钟定位单次排查靠手速长期稳定靠自动化。我现在的习惯是每台关键机器上放一个采集脚本告警触发时自动把现场打包省得半夜手忙脚乱。核心思路是一次性抓齐 CPU、进程、线程、栈、I/O 五类信息时间戳对齐。#!/bin/bash # cpu_snapshot.sh - 一键采集 CPU 高占用现场 TS$(date %Y%m%d_%H%M%S) DIR/tmp/cpu_diag_$TS mkdir -p $DIR # 1. 整体 CPU 趋势采 10 次 vmstat 1 10 $DIR/vmstat.txt # 2. 按核分布 mpstat -P ALL 1 5 $DIR/mpstat.txt # 3. 进程级 CPU 排序取前 20 ps -eo pid,ppid,stat,pcpu,pmem,comm --sort-pcpu | head -20 $DIR/top_proc.txt # 4. 线程级找出具体烧 CPU 的线程 top -b -H -n 1 | head -40 $DIR/top_thread.txt # 5. 中断分布排查软中断 cat /proc/interrupts $DIR/interrupts.txt # 6. I/O 情况排除 iowait 干扰 iostat -x 1 3 $DIR/iostat.txt 2/dev/null echo 现场已保存到 $DIR脚本逻辑说明vmstat和mpstat负责趋势和按核分布ps和top -H负责进程与线程定位/proc/interrupts和iostat负责排除中断与 I/O 干扰。参数上采样次数别太多10 次以内否则脚本本身跑太久错过最佳处置时机。这个脚本不依赖任何第三方工具主流发行版和国产 Linux 都自带。更进一步把关键指标接进监控node_exporter采集node_cpu_seconds_total按 mode 拆分配一条告警规则——us或sy持续 5 分钟超过 80% 就触发同时自动执行上面的采集脚本。这样从告警到拿到现场是秒级的不用等人登录。最后说个我自己的教训早期我总想一步到位找到根因结果在perf上耗太久业务已经受影响。后来改成「先止血再根治」——先用cpulimit或renice把高占用进程压下去保住业务再慢慢分析现场。renice -n 19 -p PID把优先级降到最低或者cpulimit -p PID -l 50限制到 50%都是应急手段但记住这些只是争取时间根因还得回到第 3、4 章的路径上找。排查 CPU 问题没有银弹靠的是把每个数字对应到具体病因的肌肉记忆多处理几次自然就快了。希望帮到你。本文还有配套的精品资源点击获取