简介:这份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/O,id才是真正空闲。如果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指定 PID,1 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 频繁 GC:jstat 和 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 问题最怕的不是技术难,而是现场没了只能靠猜。留好后悔药,比事后复盘有用得多。希望帮到你。
本文还有配套的精品资源,点击获取