Linux CPU占用率100%排查:从top到perf的实战路径
简介这份PDF资料面向Linux运维工程师与后端开发人员聚焦服务器CPU占用率居高不下这一高频故障场景系统梳理从定位到解决的完整排查思路。内容涵盖两种主流排查方法借助top按CPU排序锁定高占用进程再通过top -H或ps -mp定位具体线程并将线程ID转为16进制后用jstack打印堆栈从而追溯到问题代码同时结合一个Java进程CPU飙至300%的生产案例完整演示定位线程3626并分析堆栈的过程。资源还延伸讨论了Zabbix、Nagios、阿里云监控等监控告警手段以及被动接收告警的运维工具思路。压缩包内为1个PDF文件约147KB篇幅精炼、示例代码详实适合需要快速掌握Linux查看CPU占用率与排错流程的读者。目前已有4449人学习可作为日常运维排查的实用参考手册。1. 线上告警响了CPU 占用率 100% 到底谁在捣鬼凌晨两点收到告警一台跑了三年没出过事的业务机 CPU 占用率飙到 100%SSH 能连上但敲命令卡半秒才回显。这种场景下top第一屏往往看不出真凶——可能是某个进程在疯狂刷日志也可能是内核态软中断在偷偷吃 CPU甚至只是iowait被误读成了计算负载。Linux 系统中 CPU 占用率较高问题的排查思路与解决方法核心不是背命令而是建立一条从「确认现象」到「定位进程」再到「区分用户态/内核态/等待态」的收敛路径。这套方法适合所有需要登服务器干活的运维和后端不管跑的是 CentOS、Ubuntu 还是国产 Linux 发行版命令基本通用。下面按我实际排障的顺序拆开讲每一步都给出可复制的命令和判读标准。2. 先分清是真忙还是假忙CPU 指标的三个读数陷阱2.1 top 第一行的 load average 不等于 CPU 使用率很多人一看load average: 12.5就断定 CPU 爆了这是最常见的误判。load average 统计的是「运行队列 不可中断睡眠」的进程数磁盘 I/O 卡住时它照样飙高但 CPU 可能闲得很。正确做法是同时看top第一行的%Cpu(s)和第三行的进程状态。top -b -n 1 | head -5 # 输出示例 # top - 02:14:33 up 120 days, 3:22, 2 users, load average: 12.50, 8.30, 4.10 # Tasks: 312 total, 2 running, 310 sleeping, 0 stopped, 0 zombie # %Cpu(s): 95.2 us, 3.1 sy, 0.0 ni, 1.2 id, 0.3 wa, 0.0 hi, 0.2 si, 0.0 st判读逻辑us高说明用户态进程在算sy高说明内核态在忙系统调用、上下文切换wa高说明在等 I/Oid才是真正空闲。如果wa超过 20% 而us不高问题在磁盘不在 CPU。st是虚拟机被宿主机偷走的时间云主机上这个值高说明邻居在抢资源你本地怎么优化都没用。参数说明-b批处理模式-n 1只刷一次适合脚本采集。生产环境建议用top -b -n 1 -o %CPU直接按 CPU 排序省去交互操作。2.2 用 vmstat 看上下文切换和运行队列top是快照vmstat能看趋势。重点盯r运行队列长度、cs上下文切换次数、in中断次数。vmstat 1 5 # procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- # r b swpd free buff cache si so bi bo in cs us sy id wa st # 8 0 0 120000 30000 500000 0 0 10 20 5000 8000 90 8 1 1 0r列持续大于 CPU 核数说明有进程在排队等 CPU这才是真忙。cs每秒超过 10 万次通常是锁竞争或线程频繁唤醒即使us不高也会拖慢响应。in异常高要怀疑网卡中断或定时器风暴。2.3 单核打满和多核打满的处理方向完全不同一台 8 核机器top显示总 CPU 30%但某个核 100%业务照样卡。这时候必须按1展开每核视图或者用mpstat -P ALL 1。mpstat -P ALL 1 3 # 关注 %usr 和 %sys 在哪个 CPU 上异常 # 如果只有 CPU 3 的 %sys 高大概率是某个中断绑在了这个核单核打满常见于单线程死循环、中断亲和性绑定、自旋锁竞争。多核均匀打满才是真的计算密集型任务。这个区分决定了你是去查代码还是去调中断。3. 定位吃 CPU 的进程从 top 到 pidstat 的收敛路径3.1 top 交互模式下的四个关键按键连上机器先跑top然后按顺序操作按P按 CPU 排序按1展开每核按H显示线程按c显示完整命令行。这四步做完90% 的案例能直接看到可疑进程名。top -H -p $(pgrep -d, -f java) # 只看 java 进程的所有线程按 CPU 排序-H是关键很多高 CPU 问题出在某个线程而不是进程整体。比如 Java 应用里一个 GC 线程或某个业务线程死循环进程级top看不出来线程级一目了然。3.2 pidstat 按进程和线程拆解 CPU 时间top只能看瞬时值pidstat能按秒采样并区分用户态/内核态。pidstat -u -t -p 12345 1 5 # 输出每个线程的 %usr 和 %system # 如果某线程 %system 接近 100%说明它在疯狂做系统调用参数说明-u报告 CPU 使用-t显示线程级-p指定 PID1 5表示每秒采样一次共五次。如果%system高下一步用strace -c -p PID统计系统调用分布看是read、write还是futex在刷。3.3 用 /proc/PID/stack 和 perf 抓内核态热点用户态进程好办内核态高 CPU 才是玄学。先看内核栈cat /proc/12345/stack # 如果输出显示在某个内核函数上自旋比如 _raw_spin_lock再用perf top -p 12345实时看热点函数。没有perf就装linux-tools-common或对应内核版本的perf包。这一步能直接告诉你 CPU 时间花在哪个函数上比猜快得多。4. 常见高 CPU 场景的针对性排查手法4.1 用户态死循环gdb 附加看调用栈进程 CPU 100% 且%usr高先别杀用gdb附加进去看它在干什么。gdb -p 12345 -batch -ex thread apply all bt 2/dev/null | head -50 # 看哪个线程的栈顶在循环调用同一个函数如果栈顶反复出现同一个业务函数基本就是死循环或无限重试。注意gdb附加会暂停进程几秒生产环境慎用或者先kill -STOP再gdb看完kill -CONT恢复。4.2 频繁 GCjstat 和 jmap 组合拳Java 应用 CPU 高先看 GC 频率jstat -gcutil 12345 1000 10 # 关注 YGC 和 FGC 的次数增长以及 GCT 总时间 # 如果 FGC 每秒好几次CPU 全花在 GC 上了然后jmap -histo:live 12345 | head -20看哪些对象占内存最多。常见原因是缓存无上限、大对象频繁创建、或者内存泄漏导致老年代快速填满。调大堆内存只是缓解找到泄漏点才是根治。4.3 中断和软中断/proc/softirqs 定位网卡风暴%si高说明软中断在吃 CPU通常是网卡收包太猛。cat /proc/softirqs | grep -E NET_RX|NET_TX # 对比每个 CPU 的计数如果某个核的 NET_RX 增长极快 watch -n 1 cat /proc/softirqs | grep NET_RX解决方向开 RPS/RFS 把软中断分散到多核或者用ethtool -L eth0 combined 8增加网卡队列。如果是单核被打满检查/proc/irq/*/smp_affinity的中断亲和性设置。5. 避坑与排查那些让我加班到天亮的误判5.1 把 iowait 当成 CPU 忙现象top显示%Cpu(s)里wa90%us只有 5%但 load average 很高业务卡顿。原因iowait是 CPU 等待磁盘 I/O 的时间本质是磁盘慢不是 CPU 忙。此时优化 CPU 毫无意义。解决用iostat -x 1看%util和await如果磁盘%util接近 100%去查是哪个进程在刷盘或者换 SSD、加缓存。5.2 杀错进程高 CPU 的是受害者不是元凶现象一个进程 CPU 100%杀掉后另一个进程接着 100%像打地鼠。原因可能是上游服务超时导致下游疯狂重试或者锁竞争让多个进程自旋。解决别急着杀先strace -f -p PID看它在等什么、重试什么。用ss -s看连接状态netstat -anp | grep PID看它在跟谁通信。5.3 容器里 top 读数不准现象容器内top显示 CPU 很高但宿主机上看这个容器 CPU 并不高。原因容器内top读的是宿主机/proc没有按 cgroup 隔离看到的是全局数据。解决用cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us和cpuacct.usage算真实使用量或者直接docker stats。K8s 环境用kubectl top pod。5.4 定时任务叠加导致周期性尖峰现象每天凌晨 CPU 飙高白天正常。原因多个 cron 任务撞在同一时间备份、日志切割、数据同步一起跑。解决crontab -l列出所有任务错开执行时间。用grep CRON /var/log/syslog确认触发时间点。5.5 内核版本 bug 导致 sy 异常高现象%sy持续 30% 以上但strace看不出异常系统调用。原因某些内核版本在特定硬件上有调度或中断处理的 bug。解决uname -r查版本对比发行版 errata。升级内核或打补丁别在应用层瞎折腾。6. 把排查做成肌肉记忆一个可复用的采集脚本每次手动敲命令太慢我习惯在每台机器上放一个采集脚本出问题时一键抓现场。下面这个脚本跑 30 秒把关键指标落到文件里事后慢慢分析。#!/bin/bash # cpu_snapshot.sh - 采集 CPU 相关现场信息 OUTDIR/tmp/cpu_$(date %Y%m%d_%H%M%S) mkdir -p $OUTDIR # 1. 全局快照 top -b -n 1 $OUTDIR/top.txt vmstat 1 10 $OUTDIR/vmstat.txt mpstat -P ALL 1 5 $OUTDIR/mpstat.txt # 2. 按 CPU 排序的进程和线程 ps -eo pid,tid,pcpu,pmem,comm --sort-pcpu | head -30 $OUTDIR/ps_top.txt top -b -H -n 1 -o %CPU | head -40 $OUTDIR/top_threads.txt # 3. 中断和软中断 cat /proc/interrupts $OUTDIR/interrupts.txt cat /proc/softirqs $OUTDIR/softirqs.txt # 4. 对 TOP5 进程抓栈 for pid in $(ps -eo pid --sort-pcpu | sed -n 2,6p); do echo PID $pid $OUTDIR/stacks.txt cat /proc/$pid/stack $OUTDIR/stacks.txt 2/dev/null cat /proc/$pid/status | grep -E Name|Threads|VmRSS $OUTDIR/stacks.txt done echo 快照已保存到 $OUTDIR逻辑说明先抓全局趋势再抓进程和线程排名然后抓中断分布最后对最耗 CPU 的五个进程抓内核栈。这样即使问题几分钟后自己消失了现场数据还在。参数调整vmstat 1 10可以根据需要改成1 60采集更长时间。ps的--sort-pcpu在部分老版本不支持换成--sort -pcpu。如果机器上没有mpstat装sysstat包。拿到快照后按这个顺序看先看mpstat确认是单核还是多核问题再看top_threads找到具体线程然后看stacks判断是用户态还是内核态最后结合interrupts排除中断因素。这套流程走下来大部分 CPU 问题都能定位到具体函数或系统调用。我自己的习惯是每台新机器上线先跑一次这个脚本存个基线出问题时对比基线一眼就能看出哪个指标异常。排查 CPU 问题最怕的不是技术难而是现场没了只能靠猜。留好后悔药比事后复盘有用得多。希望帮到你。本文还有配套的精品资源点击获取