news 2026/4/30 16:56:30

内核调试技术全攻略:从printk到KGDB

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
内核调试技术全攻略:从printk到KGDB

一、顶级架构一句话总结

printk(日志) → 动态调试 → ftrace(跟踪) → kprobes(探针) → KGDB(调试器)

内核调试是驱动开发中定位问题、分析性能的核心技能。


二、printk调试

日志级别

#include<linux/printk.h>// 日志级别定义#defineKERN_EMERG"<0>"// 系统不可用#defineKERN_ALERT"<1>"// 必须立即处理#defineKERN_CRIT"<2>"// 严重错误#defineKERN_ERR"<3>"// 错误#defineKERN_WARNING"<4>"// 警告#defineKERN_NOTICE"<5>"// 正常但重要#defineKERN_INFO"<6>"// 信息#defineKERN_DEBUG"<7>"// 调试信息// 使用方式printk(KERN_INFO"Hello, kernel\n");printk(KERN_ERR"Error occurred: %d\n",error);// 便捷宏pr_info("Information message\n");pr_err("Error message\n");pr_warn("Warning message\n");pr_debug("Debug message\n");// 需要定义DEBUGdev_info(dev,"Device info\n");dev_err(dev,"Device error\n");

查看日志

# 查看内核日志dmesgdmesg|tail-50dmesg-w# 实时查看# 查看日志级别cat/proc/sys/kernel/printk# 设置日志级别echo"8 4 1 7">/proc/sys/kernel/printk# 清空日志dmesg-c

三、动态调试(Dynamic Debug)

启用动态调试

# 内核配置CONFIG_DYNAMIC_DEBUG=y# 启用所有调试信息echo"8">/proc/sys/kernel/printk

动态调试命令

# 查看所有动态调试点cat/sys/kernel/debug/dynamic_debug/control# 启用模块的所有调试echo"module mydriver +p">/sys/kernel/debug/dynamic_debug/control# 启用文件的所有调试echo"file mydriver.c +p">/sys/kernel/debug/dynamic_debug/control# 启用函数的调试echo"func my_probe +p">/sys/kernel/debug/dynamic_debug/control# 启用行号范围echo"file mydriver.c:100-200 +p">/sys/kernel/debug/dynamic_debug/control# 禁用调试echo"module mydriver -p">/sys/kernel/debug/dynamic_debug/control# 启用所有调试echo"+p">/sys/kernel/debug/dynamic_debug/control

调试标志

标志说明
p打印到日志
f打印函数名
l打印行号
m打印模块名
t打印线程ID

代码中使用

// 动态调试宏pr_debug("Debug message: %d\n",value);dev_dbg(dev,"Device debug: %d\n",value);// 条件调试pr_debug("Value is %d\n",value);

四、ftrace跟踪

启用ftrace

# 挂载debugfsmount-tdebugfs none /sys/kernel/debug# 进入ftrace目录cd/sys/kernel/debug/tracing

函数跟踪

# 查看可用跟踪器catavailable_tracers# 启用函数跟踪echofunction>current_tracer# 跟踪特定函数echomy_probe>set_ftrace_filter# 跟踪特定模块echo":mod:mydriver">set_ftrace_filter# 查看结果cattrace# 清空缓冲区echo>trace

函数图跟踪

# 启用函数图echofunction_graph>current_tracer# 设置跟踪函数echomy_probe>set_graph_function# 查看结果cattrace

事件跟踪

# 查看可用事件lsevents/# 启用事件echo1>events/sched/sched_switch/enable# 查看结果cattrace

跟踪特定进程

# 设置跟踪进程PIDecho1234>set_ftrace_pid

五、kprobes探针

kprobes类型

┌─────────────────────────────────────────────────────────┐ │ kprobes类型 │ ├─────────────────────────────────────────────────────────┤ │ kprobe - 在任意指令处插入探针 │ │ jprobe - 函数入口探针(已废弃) │ │ kretprobe - 函数返回探针 │ └─────────────────────────────────────────────────────────┘

使用kprobes

# 添加kprobeecho'p:myprobe my_probe'>/sys/kernel/debug/kprobes/add_probe# 添加kretprobeecho'r:myretprobe my_probe'>/sys/kernel/debug/kprobes/add_probe# 启用探针echo1>/sys/kernel/debug/kprobes/enable# 查看结果cat/sys/kernel/debug/kprobes/listcat/sys/kernel/debug/tracing/trace# 禁用探针echo0>/sys/kernel/debug/kprobes/enable# 删除探针echo'-:myprobe'>/sys/kernel/debug/kprobes/del_probe

内核模块中使用

#include<linux/kprobes.h>staticstructkprobekp={.symbol_name="my_probe",};staticinthandler_pre(structkprobe*p,structpt_regs*regs){pr_info("kprobe: %s called\n",p->symbol_name);return0;}staticvoidhandler_post(structkprobe*p,structpt_regs*regs,unsignedlongflags){pr_info("kprobe: %s returned\n",p->symbol_name);}staticint__initmy_init(void){kp.pre_handler=handler_pre;kp.post_handler=handler_post;if(register_kprobe(&kp)<0){pr_err("Failed to register kprobe\n");return-1;}return0;}staticvoid__exitmy_exit(void){unregister_kprobe(&kp);}

六、KGDB远程调试

配置KGDB

# 内核配置CONFIG_KGDB=yCONFIG_KGDB_SERIAL_CONSOLE=yCONFIG_KGDB_KDB=y# 内核启动参数kgdboc=ttyS0,115200 kgdbwait

进入调试模式

# 在目标机上触发echog>/proc/sysrq-trigger# 或使用魔术键SysRq + g

GDB调试

# 在开发机上启动GDBgdb vmlinux# 连接目标机target remote /dev/ttyS0# GDB命令breakmy_probecontinuestep next print variable backtrace

KDB命令

# 在KGDB提示符下kdb>bp my_probe# 设置断点kdb>go# 继续kdb>bt# 调用栈kdb>ps# 进程列表kdb>rd# 寄存器kdb>md<addr># 内存dump

七、内核oops分析

oops信息解读

[12345.678901] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [12345.678902] pgd = c0004000 [12345.678903] [00000000] *pgd=00000000 [12345.678904] [12345.678905] PC is at my_probe+0x20/0x100 [mydriver] [12345.678906] LR is at my_probe+0x18/0x100 [mydriver] [12345.678907] pc : [<bf012345>] lr : [<bf01233d>] psr: 60000013 [12345.678908] sp : c0123456 ip : c0123458 fp : c012345a [12345.678909] r10: 00000000 r9 : c012345c r8 : c012345e [12345.678910] r7 : 00000001 r6 : 00000002 r5 : 00000003 r4 : 00000004 [12345.678911] r3 : 00000000 r2 : 00000005 r1 : 00000006 r0 : 00000007 [12345.678912] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM [12345.678913] Control: 10c5387d Table: 4000404a DAC: 00000015 [12345.678914] Process myprocess (pid: 1234, stack limit = 0xc0123456) [12345.678915] Stack: (0xc0123456 to 0xc0123456) ... [12345.678920] Backtrace: [12345.678921] [<bf012345>] (my_probe [mydriver]) from [<bf023456>] (driver_probe_device+0x56/0x100)

分析步骤

# 1. 定位出错的函数和偏移# PC is at my_probe+0x20/0x100# 2. 使用addr2line定位源码行addr2line-emydriver.ko 0x20# 3. 使用gdb反汇编gdb mydriver.ko(gdb)disassemble my_probe# 4. 分析调用栈# Backtrace中的函数调用链

八、内存调试

SLAB调试

# 内核配置CONFIG_DEBUG_SLAB=yCONFIG_DEBUG_KMEMLEAK=y# 查看内存泄漏cat/sys/kernel/debug/kmemleak

KASAN(地址消毒器)

# 内核配置CONFIG_KASAN=y# 运行时检测内存错误# 自动报告越界访问、use-after-free等

lockdep(锁依赖检测)

# 内核配置CONFIG_LOCKDEP=y# 查看锁依赖cat/proc/lockdepcat/proc/lockdep_stats

九、调试工具对比

工具用途开销适用场景
printk简单日志快速定位
动态调试可控日志生产环境
ftrace函数跟踪性能分析
kprobes动态探针深度调试
KGDB源码调试复杂问题
KASAN内存检测内存问题

十、终极总结

内核调试 = 定位问题的关键能力

  • printk:最简单、最常用的调试手段
  • 动态调试:生产环境可控的调试输出
  • ftrace:函数调用跟踪、性能分析
  • kprobes:动态插入探针、深度调试
  • KGDB:源码级调试、复杂问题定位

掌握多种调试技术,才能高效解决内核问题!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/13 19:58:50

Arduino无线传感器网络自适应传输周期库

1. 项目概述AdaptiveTXWSN 是一款专为无线传感器网络&#xff08;Wireless Sensor Network, WSN&#xff09;节点设计的轻量级 Arduino 库&#xff0c;其核心目标是实现基于电池电压状态的自适应传输周期调控。在典型的 WSN 部署场景中&#xff0c;节点往往由不可更换的纽扣电池…

作者头像 李华
网站建设 2026/4/17 7:25:25

这个效率技巧,能找回你复制过的内容

很多人不知道&#xff0c;复制内容其实可以看历史记录。 也就是说&#xff0c;你复制过的内容&#xff0c;不一定只能保留最后一条。 Windows&#xff1a;系统自带 如果你用的是 Windows 10 / 11&#xff0c;系统已经内置了这个功能。 直接按&#xff1a;Win V 第一次使用…

作者头像 李华
网站建设 2026/4/17 7:56:54

OpenCV人脸识别三大经典算法:LBPH、EigenFace、FisherFace详解与代码实战

从原理到代码&#xff0c;一篇弄懂传统人脸识别的三驾马车 前言 如果你是OpenCV初学者&#xff0c;在接触到人脸识别模块时&#xff0c;一定会遇到三个名字&#xff1a;LBPH、EigenFace、FisherFace。它们都位于cv2.face子模块中&#xff0c;用法高度相似——创建识别器、训练…

作者头像 李华
网站建设 2026/4/17 13:33:19

前端与后端分离架构:从理论到实践

前端与后端分离架构&#xff1a;从理论到实践 1. 背景介绍 随着Web应用的复杂度不断提高&#xff0c;传统的前后端混合开发模式已经难以满足现代Web应用的需求。前端与后端分离架构作为一种新型的开发模式&#xff0c;正在被越来越多的企业和开发者采用。这种架构将前端和后端视…

作者头像 李华