Linux 服务器 CPU 占用 100% 排查指南
版本: 1.0
更新时间: 2025-06-06
适用对象: 运维工程师、系统管理员、开发人员
前言
CPU 占用 100% 是什么?
想象一下:一个厨师(CPU)同时要做 4 道菜(任务),但突然来了 100 份订单,他忙得团团转,其他顾客(服务请求)都只能排队等着。这就是 CPU 满载的状态。
当 CPU 被某个进程完全占满时,系统响应会变慢,服务可能超时甚至崩溃。
本文档会告诉你:
- 如何快速定位罪魁祸首进程
- 如何分析进程为什么占用高 CPU
- 如何安全地解决问题
- 如何预防未来再次发生
目录
一、快速诊断
最常见原因:某个进程失控(死循环、计算密集型任务、挖矿木马)
快速验证:执行
top按P(按 CPU 排序)查看最耗 CPU 的进程
一键定位命令
# 👀 查看占用 CPU 最高的前 10 个进程
ps aux --sort=-%cpu | head -11
# 参数解释:
# aux:显示所有进程的详细信息
# --sort=-%cpu:按 CPU 使用率降序排序(- 表示降序)
# head -11:显示前 11 行(1 行表头 + 10 个进程)
# 输出示例:
# USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
# app 1234 99.8 2.5 500000 80000 ? Rl 10:00 15:30 python3 /var/www/myapp/scripts/data_process.py
# nginx 5678 1.2 0.6 100000 20000 ? S 09:00 0:05 nginx: worker process
# root 9012 0.5 0.1 50000 8000 ? Ss 08:00 0:02 /usr/lib/systemd/systemd
# 解读:
# - USER:运行进程的用户(app 用户运行的脚本)
# - PID:进程 ID(1234,后续操作要用到)
# - %CPU:CPU 使用率(99.8%,几乎占满)
# - %MEM:内存使用率(2.5%,内存正常)
# - STAT:进程状态(R=运行中,l=多线程,s=会话领导)
# - TIME:累计使用 CPU 的时间(15:30,已经跑了 15 分 30 秒)
# - COMMAND:完整命令行(明确看到是哪个脚本)
二、现状确认
2.1 确认操作系统环境
# 查看系统版本
cat /etc/os-release
# 输出示例:
# NAME="Ubuntu"
# VERSION="20.04.6 LTS (Focal Fossa)"
# ID=ubuntu
# PRETTY_NAME="Ubuntu 20.04.6 LTS"
# 解读:
# NAME:操作系统名称(Ubuntu)
# VERSION:具体版本(20.04.6 长期支持版)
# 不同系统的命令可能略有差异(如 systemctl vs service)
# 👀 查看 CPU 核心数
nproc
# 输出:4
# 解读:
# 4 核 CPU 意味着:
# - 单核 100% = 总体 25%
# - 4 核都 100% = 真满载(top 会显示 400%)
# - Load Average > 4 表示 CPU 超负荷
2.2 确认系统负载
# 查看系统负载
uptime
# 输出:
# 10:30:00 up 5 days, 2:15, 1 user, load average: 4.20, 3.80, 2.90
# 解读:
# - 10:30:00:当前时间
# - up 5 days, 2:15:系统运行了 5 天 2 小时 15 分钟
# - load average:负载平均值(像超市排队人数)
# - 4.20:最近 1 分钟平均排队任务数
# - 3.80:最近 5 分钟平均
# - 2.90:最近 15 分钟平均
# - 趋势分析:2.90 → 3.80 → 4.20,问题在恶化!
# - 如果数字 > CPU 核心数(4),说明 CPU 已经忙不过来了
三、分层排查
3.1 应用层排查(最常用)
# 👀 实时查看进程动态(推荐交互式)
top
# 进入 top 后按:
# P:按 CPU 使用率排序(默认)
# M:按内存使用率排序
# 1:展开每个 CPU 核心的使用情况
# q:退出
# 输出示例:
# top - 10:30:00 up 5 days, 2:15, 1 user, load average: 4.20, 3.80, 2.90
# Tasks: 120 total, 3 running, 117 sleeping, 0 stopped, 0 zombie
# %Cpu(s): 98.5 us, 1.2 sy, 0.0 ni, 0.2 id, 0.0 wa, 0.0 hi, 0.1 si, 0.0 st
#
# PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
# 1234 app 20 0 500000 80000 10000 R 99.8 2.5 15:30.50 python3
# 5678 root 20 0 100000 20000 8000 S 1.2 0.6 0:05.20 nginx
CPU 占比详细解读:
| 指标 | 含义 | 正常值 | 异常说明 |
|---|---|---|---|
| us (user) | 用户空间占用 | < 70% | 应用代码在计算 |
| sy (system) | 内核空间占用 | < 20% | 内核调用过多 |
| ni (nice) | 低优先级进程 | < 10% | 后台任务影响 |
| id (idle) | 空闲率 | > 20% | 越低越忙 |
| wa (wait) | IO 等待 | < 5% | 高说明是磁盘瓶颈 |
| hi/si | 硬/软中断 | < 5% | 高说明网络/中断频繁 |
| st (steal) | 被虚拟化偷走 | 0% | 非零说明虚拟机资源被抢占 |
3.2 进程详情排查
# 👀 查看进程的完整命令行和父进程
ps -p 1234 -o pid,ppid,user,%cpu,%mem,stat,start,time,command
# 参数解释:
# -p 1234:指定进程 ID
# -o:自定义输出字段
# 输出示例:
# PID PPID USER %CPU %MEM STAT STARTED TIME COMMAND
# 1234 890 app 99.8 2.5 Rl 2025-06-06 15:30 python3 /var/www/myapp/scripts/data_process.py
# 解读:
# - PPID 890:父进程 ID(谁启动它的)
# - STAT Rl:R=运行中,l=多线程
# - STARTED:启动时间(2025-06-06,今天刚启动的)
# - TIME 15:30:累计占用 CPU 15 分 30 秒
# 👀 查看这个进程打开了哪些文件和连接
lsof -p 1234 | head -30
# 参数解释:
# -p 1234:指定进程 ID
# head -30:只看前 30 行
# 输出示例:
# COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
# python3 1234 app cwd DIR 8,1 4096 12345 /var/www/myapp
# python3 1234 app txt REG 8,1 1000000 23456 /usr/bin/python3.8
# python3 1234 app 0r REG 8,1 500000 34567 /var/www/myapp/input.csv
# python3 1234 app 3u IPv4 0 0t0 56789 TCP *:8080 (LISTEN)
# 解读:
# - cwd:当前工作目录(/var/www/myapp)
# - txt:程序文件(/usr/bin/python3.8)
# - 0r:打开的文件(input.csv 正在被读取)
# - 3u:网络连接(监听 8080 端口)
# - 这些信息能帮你理解这个进程在干什么
3.3 资源检查(USE 方法)
Utilization(利用率)
# 👀 每个 CPU 核心的使用情况
mpstat -P ALL 1 3
# 参数解释:
# -P ALL:显示所有 CPU 核心
# 1 3:每秒采样一次,采样 3 次
# 输出示例:
# 10:30:00 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle
# 10:30:01 all 98.50 0.00 1.00 0.00 0.00 0.50 0.00 0.00 0.00
# 10:30:01 0 99.00 0.00 1.00 0.00 0.00 0.00 0.00 0.00 0.00
# 10:30:01 1 98.00 0.00 1.00 0.00 0.00 1.00 0.00 0.00 0.00
# 10:30:01 2 99.00 0.00 0.00 0.00 0.00 1.00 0.00 0.00 0.00
# 10:30:01 3 98.00 0.00 1.00 0.00 0.00 1.00 0.00 0.00 0.00
# 解读:
# - 4 个核心都接近 100% → 真正的满载
# - %usr 很高:用户代码在计算(不是内核问题)
# - %iowait 很低:不是磁盘 IO 问题
# - 如果只有 1 个核心 100%,其他空闲 → 单线程程序
Saturation(饱和度)
# 📊 查看进程调度情况(有多少任务在排队等 CPU)
vmstat 1 5
# 参数解释:
# 1 5:每秒采样一次,采样 5 次
# 输出示例:
# procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
# r b swpd free buff cache si so bi bo in cs us sy id wa st
# 5 0 0 500000 80000 1000000 0 0 10 20 100 200 98 1 0 0 0
# 4 0 0 499000 80000 1000000 0 0 10 20 100 200 98 1 0 0 0
# 解读关键列:
# - r(running):正在等待 CPU 的进程数
# - r > CPU核心数 → CPU 饱和了(像超市 4 个收银台,却有 5 个人排队)
# - 这里 r=5,CPU=4核 → 已经超载
# - b(blocked):等待 IO 的进程数
# - 如果 b 很高,说明是磁盘问题,不是 CPU 问题
# - us/sy/id/wa:同 top 的 CPU 占比
Errors(错误)
# ⚠️ 检查内核错误(硬件/CPU 异常)
dmesg | tail -50
# 输出示例(如果有问题):
# [12345.678] CPU0: Core temperature above threshold, cpu clock throttled
# [12345.679] CPU1: Core temperature above threshold, cpu clock throttled
# 解读:
# - 如果出现这些错误 → CPU 过热,需要检查散热
# - 如果没有 → 排除硬件问题
四、常见原因分析
根据前面的排查,判断具体原因:
| 现象 | 可能原因 | 解决方案 | 紧急程度 |
|---|---|---|---|
| 某个 python/java 进程 99% CPU | 代码死循环或计算密集型任务 | 检查代码逻辑或限制资源 | 🔴 高 |
kworker 进程占用高 |
内核任务(中断处理、IO 等待) | 检查 dmesg 和驱动 |
🟡 中 |
kswapd 占用高 |
内存不足触发交换 | 检查内存,增加 swap | 🟡 中 |
| 陌生进程名 + 高 CPU | 可能是挖矿木马 | 立即隔离,检查入侵痕迹 | 🔴 高 |
systemd-journald 占用高 |
日志写入过多 | 清理日志,检查应用 | 🟡 中 |
| 所有核心都 100% + 负载高 | 真的业务压力大 | 考虑扩容或限流 | 🟠 中高 |
五、解决方案
⚠️ 生产环境高风险操作,请谨慎执行!
在杀死进程前,建议先保存相关日志和状态信息。
操作前:保存现场证据
# 📋 保存当前状态(用于后续分析)
mkdir -p /tmp/cpu_high_reports
ps aux --sort=-%cpu > /tmp/cpu_high_reports/snapshot_$(date +%Y%m%d_%H%M%S).txt
top -bn1 > /tmp/cpu_high_reports/top_snapshot_$(date +%Y%m%d_%H%M%S).txt
# 参数解释:
# mkdir -p:创建目录(幂等,不存在才创建)
# --sort=-%cpu:按 CPU 使用率降序排序
# $(date +%Y%m%d_%H%M%S):当前时间戳,如 20250606_103000
# >:重定向输出到文件
# 验证:
ls -la /tmp/cpu_high_reports/
方案一:优雅停止进程(推荐)
就像正常关机一样,让进程有机会保存数据、关闭连接。
# ⏹️ 发送 SIGTERM 信号(让进程有机会清理后退出)
kill 1234
# 参数解释:
# 1234:进程 PID
# kill 默认发送 SIGTERM(信号 15),进程可以捕获并优雅退出
# 就像告诉进程:"请你自己关机"
# 等待 10 秒后检查
sleep 10
ps -p 1234
# 如果输出为空 → 进程已正常退出 ✅
# 如果进程还在 → 说明进程卡死了,需要方案二
方案二:强制杀死进程(谨慎使用)
⚠️ 危险操作:强制杀进程可能导致数据丢失或文件损坏。
就像直接拔电源,进程没有任何机会保存数据。
# ⏹️ 发送 SIGKILL 信号(强制杀死,进程无法捕获)
kill -9 1234
# 参数解释:
# -9:SIGKILL 信号,内核直接杀死进程
# 进程无法捕获这个信号,无法做任何清理工作
# 确认进程已消失
ps -p 1234
# 应该输出空
方案三:限制进程 CPU(适合正常业务)
如果这个进程是正常业务,但不能让它占满 CPU:
# 方法 1:使用 cpulimit 限制
cpulimit -p 1234 -l 50 &
# 参数解释:
# -p 1234:指定进程
# -l 50:限制最多使用 50% CPU
# &:后台运行
# 如果没安装 cpulimit:
# Ubuntu/Debian
sudo apt install cpulimit
# CentOS/RHEL
sudo yum install cpulimit
# 方法 2:使用 systemd 临时限制(推荐,更专业)
systemctl set-property system.slice CPUQuota=50%
# 参数解释:
# set-property:临时修改属性
# system.slice:系统服务组
# CPUQuota=50%:限制 CPU 配额为 50%
# 恢复:
systemctl set-property system.slice CPUQuota=
# 空值表示恢复默认
方案四:排查代码问题(治本)
如果进程是你自己的应用:
# 查看进程在调用什么函数(需要安装 python3 的 py-spy)
pip3 install py-spy
# 查看进程的调用栈
py-spy top -p 1234
# 参数解释:
# top:实时查看调用栈
# -p 1234:指定进程
# 这会显示进程当前在执行哪行代码
# 输出示例:
# Total Samples: 10000
# GIL: 100.00%, Active: 100.00%
#
# %Own %Total OwnTime TotalTime Function (filename:line)
# 98.00% 98.00% 10.0s 10.0s process_data (data_process.py:45)
# 2.00% 2.00% 0.2s 0.2s <module> (data_process.py:10)
# 解读:
# - process_data 函数在第 45 行占用了 98% 的 CPU
# - 打开这个文件检查第 45 行是否有死循环
六、验证修复
6.1 确认 CPU 恢复正常
# ✅ 检查 CPU 使用率
top -bn1 | grep "%Cpu"
# 输出示例(正常):
# %Cpu(s): 5.2 us, 1.0 sy, 0.0 ni, 93.5 id, 0.0 wa, 0.0 hi, 0.3 si, 0.0 st
# 解读:
# - id (idle) = 93.5% → CPU 93.5% 空闲,恢复正常 ✅
# - us = 5.2% → 只有 5.2% 在跑用户程序,很轻松
6.2 确认负载下降
# ✅ 检查 Load Average
uptime
# 输出示例:
# 10:35:00 up 5 days, 2:20, 1 user, load average: 0.50, 1.20, 2.00
# 解读:
# - 趋势:2.00 → 1.20 → 0.50 → 正在恢复
# - 0.50 < 4(核心数)→ CPU 不忙了 ✅
6.3 确认问题进程已消失
# ✅ 确认进程已不存在
ps -p 1234
# 输出(空):
# PID TTY TIME CMD
# 解读:没有输出进程信息 → 已消失 ✅
七、预防措施
7.1 设置 CPU 监控告警
#!/bin/bash
# /usr/local/bin/cpu_monitor.sh
# CPU 使用率超过 90% 持续 3 次采样,记录告警日志
CPU_USAGE=$(top -bn1 | grep "%Cpu" | awk '{print $2}' | cut -d'.' -f1)
THRESHOLD=90
LOG_FILE="/var/log/cpu_alert.log"
mkdir -p "$(dirname "$LOG_FILE")"
if [ "$CPU_USAGE" -gt "$THRESHOLD" ]; then
echo "⚠️ [$(date)] CPU usage is ${CPU_USAGE}% (threshold: ${THRESHOLD}%)" >> "$LOG_FILE"
echo "📊 Top 3 CPU consumers:" >> "$LOG_FILE"
ps aux --sort=-%cpu | head -4 >> "$LOG_FILE"
echo "---" >> "$LOG_FILE"
# 可选:发送邮件告警
# echo "CPU high: ${CPU_USAGE}%" | mail -s "Server Alert" admin@example.com
fi
# 参数解释:
# awk '{print $2}':提取 CPU 使用率的数字部分
# cut -d'.' -f1:去掉小数部分
# $():命令替换,把命令结果赋值给变量
# >>:追加到文件(不覆盖)
# 添加执行权限
chmod +x /usr/local/bin/cpu_monitor.sh
# 设置定时任务(每 5 分钟检查一次)
(crontab -l 2>/dev/null; echo "*/5 * * * * /usr/local/bin/cpu_monitor.sh") | crontab -
# 参数解释:
# crontab -l:列出当前定时任务
# 2>/dev/null:忽略错误(首次执行时没有任务)
# echo "*/5...":添加新的定时任务
# crontab -:写入定时任务列表
7.2 设置进程资源限制
# 使用 systemd 服务文件限制资源
# /etc/systemd/system/myapp.service
[Service]
# 限制 CPU 使用率不超过 50%
CPUQuota=50%
# 限制内存使用
MemoryMax=1G
# 限制打开文件数
LimitNOFILE=65536
# 重新加载配置
systemctl daemon-reload
systemctl restart myapp
# 验证限制是否生效
systemctl show myapp -p CPUQuota
# 输出:CPUQuota=50%
八、总结
核心排查步骤
| 步骤 | 关键命令 | 目的 |
|---|---|---|
| 1. 找进程 | ps aux --sort=-%cpu | head -11 |
找到 CPU 最高的进程 |
| 2. 看详情 | ps -p PID -o command |
确认进程在干什么 |
| 3. 查资源 | top + vmstat + mpstat |
确认是 CPU 问题还是 IO 问题 |
| 4. 查文件 | lsof -p PID |
查看打开的文件和连接 |
| 5. 先优雅 | kill PID |
尝试正常退出 |
| 6. 后强制 | kill -9 PID |
卡死时强制杀死 |
| 7. 验证 | uptime + top |
确认 CPU 恢复正常 |
黄金法则
- 先诊断,后行动 - 不要盲目 kill 进程,先搞清楚它是什么
- 先优雅,后强制 - 先
kill,无效再kill -9 - 留证据,方便追溯 - 操作前保存状态快照
- 治标更要治本 - 找到代码层面的根本原因
- 预防胜于治疗 - 设置监控告警和资源限制
速查表
# 一键诊断命令
ps aux --sort=-%cpu | head -11 && uptime && echo "---" && top -bn1 | grep "%Cpu"
# 这行命令会同时显示:
# 1. CPU 占用最高的进程
# 2. 系统负载
# 3. CPU 使用率详情
文档版本:1.0
最后更新:2025-06-06
维护者:guobin
评论区