资讯动态

Linux内核问题排查实战:从现象分类到工具链与根因定位

发布时间:2026/10/18 19:33:11 来源:尧图企业网站定制
1. 从一次深夜告警说起内核问题为什么总在凌晨爆发做基础架构运维的人大概都有类似的经历白天压测一切正常凌晨三点告警群突然炸锅某批机器负载飙升、业务进程被OOM Killer干掉、dmesg里刷出一屏看不懂的调用栈。等你爬起来连上机器现场已经凉了日志被轮转、复现条件消失只剩下一堆疑似内核问题的猜测。我处理过不少这类case最大的感受是内核层面的bug排查难点从来不在修而在定位。用户态程序崩了有core dump、有堆栈、有明确的函数名内核崩了往往只有一段十六进制地址加符号偏移甚至直接整机hang死连日志都来不及落盘。这篇内容就是把我这些年踩过的内核排查坑做一次系统梳理。它适合谁看如果你是会写业务代码但对内核只有模糊概念的开发看完至少能知道哪些现象该怀疑内核、该收集什么证据如果你是运维或SRE经常要面对线上疑难杂症这里面的工具链和排查链路可以直接抄作业如果你是刚接触内核模块开发的同学那些看起来能跑但一上量就炸的坑我基本都替你踩过一遍了。先把一个核心认知摆在前面内核bug的排查本质是一场证据保全的竞赛。现场消失得越快你越被动。所以整篇文章的暗线就是围绕如何在系统还活着的时候把关键信息抓下来来展开。下面我会从现象分类、工具选型、典型场景拆解、到根因定位的完整链路一层层往下讲。2. 先分清现象内核问题到底分哪几类很多人一遇到系统异常就往内核bug上靠其实大部分问题根本不在内核。我习惯先把现象做个粗分类因为不同类别的排查路径完全不同走错方向会浪费大量时间。2.1 崩溃类panic、oops、BUG_ON这类是最痛快的因为内核会主动打印信息。kernel panic是致命错误系统直接停摆oops相对温和某个执行路径出错但系统可能还能跑BUG_ON和WARN_ON是内核开发者主动埋的断言触发说明代码走到了不该走的分支。判断要点看dmesg或串口输出里有没有Call Trace。有调用栈说明至少能定位到函数级别。我见过不少人看到一屏地址就懵了其实只要开了CONFIG_DEBUG_INFO用gdb或者faddr2line就能把地址翻译成函数名行号这一步是基本功。2.2 卡死类soft lockup、hard lockup、RCU stall这类最折磨人因为系统看起来还活着——ping得通、SSH偶尔能连上但业务就是不动。典型特征是内核检测线程打印出watchdog: BUG: soft lockup - CPU#x stuck for Xs。这里有个关键区分soft lockup是某个CPU在内核态长时间不让出hard lockup是连中断都关了。前者通常还能抢救后者基本等于那颗CPU废了。RCU stall则是RCU机制检测到宽限期超时往往意味着有代码在RCU读临界区里睡眠了或者CPU被长时间占用。2.3 资源异常类内存泄漏、句柄耗尽、负载虚高这类最隐蔽因为系统不崩不卡就是慢慢不对劲。比如slab缓存只增不减、/proc/meminfo里Slab项持续膨胀、load average很高但CPU利用率很低大量进程处于D状态卡在不可中断睡眠。我个人的经验是负载虚高且CPU不高八成是IO或锁等待这时候要看/proc/*/stack里进程卡在哪个内核函数而不是盯着top发呆。2.4 数据损坏类静默错误最可怕最阴险的一类。系统不报错但文件内容莫名变了、网络包校验失败、内存里的数据结构被改写。这类问题往往由硬件内存位翻转、磁盘坏道或内核里的并发bug竞态、越界写引起。排查难度极高通常要靠KASAN、kmemleak这类动态检测工具或者干脆上硬件诊断。把现象分完类你会发现一个规律越靠前的类别证据越充分越靠后的类别越依赖主动埋点。所以接下来讲工具我会按被动收集和主动检测两条线来分。3. 工具链怎么选别一上来就上重型武器内核排查工具多如牛毛但很多人有个误区——觉得工具越高级越好上来就想用perf、eBPF、crash全套。实际上工具选型的第一原则是对现场侵入性最小。系统已经出问题了你再挂个重型工具可能直接把最后一点现场也压垮。3.1 被动收集三件套dmesg、/proc、sysrq这三个是零成本、零依赖的任何Linux系统都有应该成为你的第一反应。dmesg不用多说但有个细节很多人忽略默认的ring buffer大小可能只有几十KB高频日志场景下几分钟就冲掉了。我习惯在关键机器上把kernel.printk和log_buf_len调大比如启动参数加log_buf_len4M能多留住不少现场。/proc下面藏着大量宝藏。/proc/interrupts看中断分布能发现中断风暴/proc/softirqs看软中断/proc/slabinfo看内核对象分配/proc/PID/stack看进程卡在内核哪个位置。这些信息在系统还活着的时候抓价值极高。sysrq是救命稻草。当系统卡到SSH都连不上但键盘或串口还能响应时echo t /proc/sysrq-trigger能把所有进程的调用栈打到dmesg里echo m打内存信息echo w打D状态进程。这几个magic sysrq组合我建议每个运维都背下来关键时刻能救命。3.2 主动检测KASAN、kmemleak、lockdep如果问题能复现那就该上主动检测工具了。这几个都是内核自带的编译选项代价是性能下降KASAN可能降一半以上所以只在测试环境或问题机器上开。KASANKernel Address Sanitizer专治内存越界和use-after-free它会给每次内存访问加影子内存检查一旦越界立刻报出精确的访问地址和调用栈。我排查过一个内核模块的越界写就是靠KASAN直接定位到具体那一行。kmemleak专治内存泄漏它定期扫描内核内存找出没有指针引用的孤儿分配。缺点是误报率不低需要人工甄别。lockdep是锁依赖检测器能发现潜在的死锁比如AB-BA锁顺序问题。这个工具的价值在于它能在死锁真正发生前就报警属于预防性武器。3.3 动态追踪perf、ftrace、eBPF当问题既不复现也不崩溃只是性能不对劲时就轮到动态追踪上场了。perf是性能分析的主力perf top看热点函数perf record/report做采样分析perf trace跟踪系统调用。我常用perf record -g -p PID抓某个进程的调用链火焰图一画瓶颈一目了然。ftrace是内核自带的函数追踪器function_graph模式能画出完整的函数调用图对理解内核执行路径极有帮助。它比perf更内核原生开销也可控。eBPF是这几年的当红炸子鸡bcc和bpftrace工具集让内核追踪变得像写脚本一样简单。比如一行bpftrace就能统计某个内核函数的调用次数和延迟分布。但要注意eBPF对内核版本有要求老内核4.x以下支持有限别在老旧生产环境上硬上。工具选型的核心逻辑我总结成一张表场景首选工具侵入性备注系统已卡死sysrq 串口极低优先保现场崩溃有栈dmesg faddr2line无翻译地址定位函数内存越界KASAN高仅测试环境内存泄漏kmemleak中误报需甄别死锁风险lockdep中预防性性能瓶颈perf 火焰图低生产可用函数级追踪ftrace低内核原生灵活追踪eBPF/bpftrace低看内核版本4. 三个真实场景的完整排查链路光讲工具是纸上谈兵下面我用三个我实际处理过的场景细节做了脱敏和虚构处理把从现象到根因的完整链路走一遍。重点不是结论而是每一步为什么这么走。4.1 场景一凌晨批量OOM凶手却不在业务进程现象某批机器凌晨集中触发OOM Killer被杀的都是业务进程但业务本身内存占用平稳free看总内存也没明显增长。第一步先看OOM日志。dmesg里OOM Killer会打印被杀进程的内存占用和系统整体内存分布。我发现一个反常点被杀进程的RSS其实不大但系统Slab占用极高。这就把方向从业务内存泄漏转向了内核内存泄漏。第二步抓/proc/slabinfo。对比正常机器和问题机器发现某个kmalloc-xxx缓存的对象数量差了十几倍。slabinfo里能直接看到每个缓存的对象数和大小这一步能锁定是哪个内核对象在膨胀。第三步用kmemleak定位。在测试环境复现后开kmemleak等它扫描几轮cat /sys/kernel/debug/kmemleak直接列出了泄漏的分配调用栈。根因是某个内核模块在错误处理路径上漏了kfree正常路径没事只有特定错误分支才泄漏——这也解释了为什么白天压测正常、凌晨错误率高的时段才爆发。这个case的经验是OOM不一定是用户态内存问题Slab膨胀是重要信号。很多人一看OOM就盯着业务进程方向就错了。4.2 场景二soft lockup栈里全是无辜函数现象某机器周期性打印soft lockup卡住的CPU上调用栈看起来都是正常函数没有明显死循环。第一步看lockup的完整栈。soft lockup的栈往往显示的是卡住那一刻的快照不一定是根因。我连续抓了几次发现每次栈顶都停在同一个自旋锁相关的函数上。第二步怀疑锁竞争。用lockdep在测试环境跑果然报出潜在的死锁风险两个路径以相反顺序获取两把锁。但奇怪的是生产环境并没有真正死锁只是卡很久。第三步深挖发现是自旋锁持有时间过长。某段代码在持锁期间做了一个可能耗时的操作访问了慢速设备导致其他CPU在这把锁上自旋等待累积超过watchdog阈值就报soft lockup。根因不是死锁而是临界区设计不合理。修复方案是把耗时操作移出临界区或者改用可睡眠的互斥锁。这个case的教训是soft lockup不一定是死循环锁竞争和长临界区同样会触发看栈要结合锁的持有情况一起分析。4.3 场景三数据静默损坏靠KASAN揪出越界写现象某服务偶发返回错误数据重启后恢复过几天又出现。没有任何崩溃日志业务逻辑也查不出问题。第一步排除硬件。跑内存测试和磁盘检查都正常。这一步不能省因为硬件问题占静默损坏的相当比例。第二步怀疑内核并发bug。在测试环境开KASAN加压复现。KASAN在跑了几个小时后报出一次越界写某个内核缓冲区被写入了超出其长度的数据恰好覆盖了相邻的内存区域。第三步定位代码。KASAN的报错里带了完整的分配栈和访问栈直接指向某个驱动在处理网络包时长度校验有缺陷特定构造的包能触发越界写。这个bug平时不发作只有收到畸形包才触发所以极难复现。这个case让我深刻体会到静默数据损坏KASAN是性价比最高的武器。虽然它性能开销大但比起数据损坏带来的业务损失测试环境降点性能完全值得。5. 那些文档里不会写的排查心得工具和流程讲完了下面这些是我踩了无数坑才总结出来的软经验常规文档里基本看不到但往往决定排查效率。5.1 现场保全永远优先于赶紧恢复这是最反直觉的一条。系统出问题业务方催着恢复很多人的第一反应是重启。但重启等于销毁现场如果这是个反复出现的问题你重启一次就少一次定位机会。我的做法是如果系统还能响应先花30秒抓关键信息dmesg、sysrq的栈、slabinfo、进程状态再决定是否重启。这30秒的投入可能省下后面几天的排查时间。当然如果业务影响面极大该重启还得重启但要养成重启前先抓一把的习惯。5.2 版本和配置是第一嫌疑对象内核bug排查里有个高频规律最近改了什么往往就是答案。内核版本升级、内核参数调整、新加载的模块、新上的硬件驱动这些都是重点怀疑对象。我习惯维护一个变更时间线把内核相关的变更升级、参数、模块都记下来。出问题时先对齐时间线如果故障开始时间和某次变更吻合排查范围立刻缩小一大半。这个习惯帮我省过无数次事。5.3 别忽视复现的价值但要控制复现成本能稳定复现的问题排查难度直接降一个数量级。但生产环境不能随便复现所以要学会在测试环境构造最小复现条件。我的经验是从生产现场提取触发要素——是特定流量模式特定并发量特定硬件然后在测试环境逐个变量逼近。这个过程可能很枯燥但一旦复现成功配合KASAN、lockdep这些工具根因往往很快浮出水面。5.4 善用二分法但要知道二分什么内核版本二分git bisect是定位回归bug的利器但前提是你能稳定复现。如果问题偶发二分法会很痛苦。这时候可以换个思路二分配置、二分模块、二分硬件。比如逐个卸载非必要内核模块看问题是否消失或者换一批硬件看是否复现。5.5 社区和邮件列表是被低估的资源内核问题有个特点你遇到的坑大概率别人也遇到过。把关键的报错信息去掉敏感信息拿去搜索往往能在邮件列表、bug tracker里找到类似案例甚至直接找到补丁。我处理过的一个RCU stall问题就是在邮件列表里找到同款对方给出了明确的规避方案。但要注意引用社区方案前一定要验证适用性内核版本、配置、硬件不同同样的补丁可能不适用甚至引入新问题。6. 把排查能力沉淀成体系零散的技巧再多不成体系也难复用。我这些年慢慢把内核排查能力沉淀成了几个习惯分享出来供参考。第一建立自己的证据清单。针对不同现象崩溃、卡死、资源异常列出该抓哪些信息、用什么命令、存到哪里。出问题时照着清单走不会手忙脚乱。我的清单里sysrq的几个触发命令、slabinfo的对比方法、faddr2line的用法都是固定项。第二维护一个已知问题库。每次排查完把现象、根因、修复方案、规避措施记下来。时间长了这个库就是你的私人知识库下次遇到类似现象能快速匹配。我现在的库里已经攒了几十个case命中率相当高。第三定期做故障演练。在测试环境人为制造一些内核问题比如用fault injection框架注入错误练习排查流程。这比真出事时手忙脚乱强得多。演练的重点不是修好而是练熟抓现场的手感。第四关注内核社区的动态。订阅几个内核相关的邮件列表或资讯源了解最新的bug和修复。很多问题在爆发前社区已经有预警了。这不是让你追新而是让你对哪些地方容易出问题保持敏感。最后分享一个我个人的小技巧给关键机器的内核日志加个黑匣子。用pstore或者网络日志把内核日志实时外送即使机器彻底hang死日志也已经落到远端了。这个投入不大但在处理那些死无对证的case时价值无可估量。内核排查这条路没有捷径但有方法。工具会更新版本会迭代但保现场、分现象、选工具、走链路、沉经验这套底层逻辑是不变的。希望这些从实战里抠出来的经验能让你下次面对内核告警时少一点慌乱多一点底气。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑