文章

Linux 排错手册

Linux 排错手册

[!abstract] 定位 SRE 面试必考科目。覆盖 CPU、内存、磁盘 IO、进程、文件描述符五大维度的排错命令、标准排查链路和面试话术。


一、CPU 飙高排查

标准排查链路

graph TB
    A[CPU 飙高告警] --> B[top 看整体]
    B --> C{us 高还是 sy 高?}
    C -->|us 用户态高| D[应用层问题]
    C -->|sy 内核态高| E[系统调用/IO 问题]
    C -->|ni 高| F[其他高优先级进程抢占]
    C -->|wa 高| G[IO 等待 → 看磁盘]

    D --> D1[top -Hp PID 找线程]
    D1 --> D2[线程 TID 转十六进制]
    D2 --> D3[jstack PID 查线程栈]

    E --> E1[perf top / strace -p PID]
    E1 --> E2[看系统调用分布]

逐层命令

# 1. 宏观:看整体 CPU 分布
top
# us = 用户态(业务代码、JVM GC)
# sy = 内核态(系统调用、上下文切换)
# wa = IO 等待(磁盘瓶颈)
# id = 空闲

# 2. 找进程
top -bn1 | head -20
# 或更精确
ps aux --sort=-%cpu | head -10

# 3. 找线程(关键!)
top -Hp <PID>
# 记录占用最高的线程 TID

# 4. 转十六进制
printf '%x\n' <TID>
# 例如:printf '%x\n' 12345 → 3039

# 5. 查 Java 线程栈
jstack <PID> | grep -A 30 '0x3039'
# 能直接看到哪个方法在狂跑 CPU

# 6. 非 Java 进程?用 strace/perf
strace -p <PID> -c         # 统计系统调用耗时分布
perf top -p <PID>          # 实时看 CPU 热点函数
perf record -p <PID> -g    # 采样,生成火焰图

常见根因速查

症状排查命令常见原因
us 高,单线程top -Hpjstack死循环、正则回溯、无限递归
us 高,多线程均匀jstack 多线程都是同一个方法GC 频繁、业务计算密集
systrace -c 看系统调用大量上下文切换、频繁 futex、短连接风暴
wa 高 (>10%)iostat -x 1 看磁盘磁盘 IO 瓶颈

二、内存排查

标准排查链路

# 1. 整体看
free -h
#            total   used   free   shared   buff/cache   available
# 关注 available(实际可用),不是 free

# 2. 进程级
top -o %MEM                          # 或 ps aux --sort=-%rss
pmap -x <PID> | tail -1              # 进程内存明细
cat /proc/<PID>/status | grep Vm     # VmRSS, VmSize, VmData

面试高频:free vs available

[!important] 面试金句 「free 是完全没有被使用的内存。available 是包括可以回收的缓存和 buffer 以后的实际可用内存。Linux 会尽可能用空闲内存做文件缓存(buffer/cache),这是正常行为,不是内存泄漏。只有当 available 持续下降且不回升,才是真正的内存压力。」

Java 进程内存排查

# 堆内存
jmap -heap <PID>

# 堆外内存(容易被忽略的内存泄漏来源)
# 堆外 = 总 RSS - 堆
# 如果堆外持续增长 → DirectByteBuffer 泄漏、JNI 泄漏、thread stack 过多
jcmd <PID> VM.native_memory summary

# OOM 时自动 dump(JVM 参数)
# -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/dump/

常见根因速查

症状排查原因
OOM Killdmesg | grep -i oom 看被杀的进程内存超限,K8s limits 太低或真的泄漏
available 持续下降长期监控趋势真内存泄漏或缓存膨胀
Swap 使用 > 0vmstat 1 看 si/so 列内存不足在换页,严重性能问题
OOMKilled (K8s)kubectl describe pod → Last State 的 Exit Code 137137 = SIGKILL = OOM

三、磁盘 IO 排查

# 1. 整体 IO 分布
iostat -x 1

# 关注:
# %util   → 磁盘繁忙度,接近 100% 表示瓶颈
# await   → IO 平均等待时间 (ms)
# r/s, w/s → 读写 IOPS
# rkB/s, wkB/s → 读写吞吐量

# 2. 找元凶进程
iotop -o              # 只显示有 IO 的进程
pidstat -d 1          # 按进程统计 IO

# 3. 深挖:这个进程在读什么文件?
lsof -p <PID> | grep REG    # 打开的文件
strace -p <PID> -e trace=read,write 2>&1 | head -50

常见根因速查

症状排查原因
%util 100%, r/sw/s 都不高iostatavgrq-sz顺序大 IO(如日志切割、备份),带宽打满了
await > 50msiostat + iotop磁盘性能不足或坏道
%util 高但 CPU wa 不高可能是 SSDSSD 的等待时间短,CPU 不阻塞
某个进程 IO 大lsof 看是哪个文件应用日志没轮转、数据库 binlog 刷盘

四、进程与文件描述符

# 查进程(不只 ps aux)
ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%cpu | head
pstree -p <PID>           # 进程树

# 查文件描述符泄漏
lsof -p <PID> | wc -l                     # 当前进程打开的 fd 数
lsof -p <PID> | awk '{print $9}' | sort | uniq -c | sort -rn | head  # 最多的是什么类型
ls -la /proc/<PID>/fd | wc -l             # 同上

# 系统级 fd 限制
ulimit -n                                 # 当前 shell
cat /proc/<PID>/limits | grep 'open files' # 进程级

# 查僵尸进程
ps aux | grep 'Z'
# 如果有 zombie → 父进程没 wait() 回收子进程

Fd 泄漏的典型场景

  • HTTP 连接池没关 → ESTABLISHED socket 堆积
  • 文件没 close → 打开的文件句柄持续增长
  • 管道/FIFO 没释放 → Java NIO 的 Pipe 未关闭

五、性能分析进阶工具

# 火焰图(Brendan Gregg 的 perf-tools)
perf record -F 99 -p <PID> -g -- sleep 30
perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > flame.svg

# eBPF 工具(bcc-tools)
execsnoop       # 追踪 exec 调用(谁在执行命令)
opensnoop       # 追踪 open 调用(谁在打开文件)
tcptop          # 实时 TCP 流量 Top
biolatency      # 磁盘 IO 延迟分布直方图

# 快速看系统瓶颈
vmstat 1        # CPU、内存、IO 全景 1s 刷新
sar -n DEV 1    # 网络流量历史

六、面试话术模板

[!quote] 被问「你常用的 Linux 排查命令」

「我一般按五个维度来排查:CPU、内存、磁盘 IO、网络、文件描述符。」

「CPU 的话,先 top 看整体,如果是用户态高就用 top -Hp 找线程,Java 用 jstack 定位到代码行,非 Java 用 perfstrace。如果是内核态高说明系统调用或上下文切换太频繁。」

「内存的话注意 freeavailable 而不是 free,buffer/cache 是正常行为。进程级用 pmap/proc/PID/status,Java 额外关注堆外内存。」

「磁盘 IO 看 iostat -x%util 接近 100% 就是瓶颈,iotop 找元凶进程,lsof 确认在写什么文件。」

「文件描述符 lsof -p PID | wc -l,接近 ulimit -n 就要排查泄漏。」

#sre #linux #面试