linux内核调试需五步:一、启用config_debug_kernel等选项编译带符号内核;二、用kdump+crash分析vmcore转储;三、以ftrace动态跟踪函数路径;四、调高loglevel并用dmesg捕获带时间戳日志;五、通过kgdb串口或网络进行gdb源码级调试。

如果您在开发或维护 Linux 内核模块时遇到系统崩溃、Oops、死锁或性能异常等问题,通常需要借助内核调试机制快速定位根本原因。以下是几种常用且有效的内核调试与故障定位技巧:
一、启用内核调试配置并编译带调试信息的内核
内核调试能力高度依赖编译时启用的调试选项,未开启相关配置将导致多数调试工具无法获取符号、堆栈或运行时状态。必须确保 CONFIG_DEBUG_KERNEL=y 及其子项(如 CONFIG_DEBUG_INFO、CONFIG_KALLSYMS、CONFIG_FRAME_POINTER)已启用。
1、进入内核源码目录,执行 make menuconfig。
2、依次进入 Kernel hacking → Debugging options,勾选 Enable kernel debugging、Provide GDB scripts for kernel debugging、Include all symbols in kallsyms。
3、在 Kernel hacking → Compile-time checks and compiler options 中启用 Frame pointer support(用于准确回溯栈帧)。
4、保存配置后执行 make -j$(nproc) 编译,生成 vmlinux(含完整调试符号)及 bzImage。
5、安装新内核并确保 vmlinux 文件保留在构建目录中,不可删除,GDB 调试时需显式加载该文件。
二、使用 kdump + crash 工具分析内核崩溃转储
kdump 是基于 kexec 的内核崩溃捕获机制,可在主内核 panic 时启动备用内核保存内存镜像(vmcore),crash 工具则用于离线解析该镜像并检查寄存器、调用栈、内存布局等关键状态。
1、确认系统已安装 kexec-tools 和 crash 软件包。
2、编辑 /etc/default/grub,在 GRUB_CMDLINE_LINUX 行末尾添加 crashkernel=auto。
3、运行 update-grub(Debian/Ubuntu)或 grub2-mkconfig -o /boot/grub2/grub.cfg(RHEL/CentOS)并重启。
4、触发一次测试性 panic(如 echo c > /proc/sysrq-trigger),验证 /var/crash/ 下是否生成 vmcore 文件。
5、执行 crash /usr/lib/debug/lib/modules/$(uname -r)/vmlinux /var/crash/*/vmcore 进入交互式分析环境。
三、利用 ftrace 动态跟踪内核函数执行路径
ftrace 是内核内置的轻量级跟踪框架,无需重新编译即可实时监控函数调用、中断上下文切换、调度事件等,适用于定位延迟毛刺、隐式阻塞或非预期路径跳转。
1、挂载 debugfs:mount -t debugfs none /sys/kernel/debug。
2、查看可用 tracer:cat /sys/kernel/debug/tracing/available_tracers。
3、选择 function_graph tracer:echo function_graph > /sys/kernel/debug/tracing/current_tracer。
4、限定跟踪目标函数(例如 do_sys_open):echo do_sys_open > /sys/kernel/debug/tracing/set_ftrace_filter。
5、启用跟踪:echo 1 > /sys/kernel/debug/tracing/tracing_on;执行待测操作;再执行 echo 0 > /sys/kernel/debug/tracing/tracing_on。
6、读取结果:cat /sys/kernel/debug/tracing/trace_pipe,注意 输出中缩进层级反映函数调用深度,+ 符号标记返回点。
四、通过 dmesg + kernel loglevel 实时捕获内核日志线索
内核日志(ring buffer)是首个故障线索来源,尤其对 Oops、WARNING、stack trace 类错误至关重要。默认 loglevel 可能过滤掉关键信息,需动态调整以捕获更详细输出。
1、查看当前 loglevel:cat /proc/sys/kernel/printk,格式为 “console_loglevel default_message_loglevel minimum_console_level default_console_loglevel”。
2、临时提升控制台日志等级:echo 8 > /proc/sys/kernel/printk,使 KERN_DEBUG 级别消息也输出到终端。
3、复现问题后立即执行 dmesg -T > /tmp/dmesg.log,务必使用 -T 参数显示本地时间戳,便于与用户空间日志对齐。
4、过滤关键模式:dmesg | grep -E "(Oops|BUG|WARNING|Call Trace|invalid opcode)"。
5、若日志被循环覆盖,可提前设置 ring buffer 大小:echo 16777216 > /proc/sys/kernel/log_buf_len(需内核支持 CONFIG_LOG_BUF_SHIFT)。
五、使用 kgdb over serial 或 ethernet 进行源码级交互调试
kgdb 允许通过 GDB 连接正在运行的内核,支持断点、单步、变量查看与内存检查,适用于复现周期长、条件苛刻的竞态或初始化问题。
1、编译内核时启用 CONFIG_KGDB=y、CONFIG_KGDB_SERIAL_CONSOLE=y(串口)或 CONFIG_KGDB_KDB=y(KDB 前端)。
2、启动参数添加 kgdboc=ttyS0,115200(串口)或 kgdboe=@192.168.1.100/24,@192.168.1.200/24(以太网)。
3、在目标机触发断点:echo g > /proc/sysrq-trigger,内核将暂停并等待 GDB 连接。
4、在宿主机执行:gdb vmlinux -ex "target remote /dev/ttyUSB0"(串口)或 -ex "target remote tcp:192.168.1.100:3333"(以太网)。
5、加载符号后,可使用 break do_page_fault、continue、info registers 等命令,注意所有断点均作用于内核地址空间,不可对模块符号直接下断,需先解析其加载基址。







