一、内核日志快速排错:printk+dmesg 零基础上手,实时追踪运行状态
最基础、最常用的调试手段,用于打印内核/驱动运行状态、变量值、执行流程。
1. printk(内核态打印)
内核代码中替代 printf,带日志级别(控制是否输出到控制台):
C // 内核代码中使用 #include// 日志级别(优先级从高到低) printk(KERN_EMERG"紧急:系统崩溃\n"); printk(KERN_ALERT"警报:需要立即处理\n"); printk(KERN_CRIT"严重:硬件/软件错误\n"); printk(KERN_ERR"错误:常规错误\n"); printk(KERN_WARNING "警告:潜在问题\n"); printk(KERN_NOTICE"通知:正常但重要信息\n"); printk(KERN_INFO"信息:常规调试信息\n"); printk(KERN_DEBUG"调试:仅调试用\n"); // 简化写法(推荐) pr_info("驱动初始化成功,版本:%d\n", 10); pr_err("寄存器写入失败!\n"); pr_debug("变量 val = %#x\n", val); // 调试级别,默认不打印 |
2. dmesg(查看内核日志)
用户态命令,读取内核环形缓冲区的 printk 日志:
Bash # 基础用法 dmesg# 查看所有日志 dmesg -w# 实时跟踪日志(类似 tail -f) dmesg -c# 查看并清空日志 dmesg -n 8# 开启所有级别日志(包含DEBUG) dmesg | grep 驱动名# 过滤自己的驱动日志 # 查看带时间戳的日志 dmesg -T |
二、免代码查状态:procfs+sysfs 一键洞悉系统与设备底层信息
无需修改代码,实时查看内核/硬件/进程状态,是排查系统、驱动问题的首选。
1. procfs(/proc):进程+内核运行时信息
经典虚拟文件系统,只读为主,用于查看系统状态:
Bash # 核心调试命令 cat /proc/cpuinfo# 查看CPU信息 cat /proc/meminfo# 查看内存状态 cat /proc/modules# 查看已加载内核模块(驱动) cat /proc/cmdline# 查看内核启动参数 cat /proc/interrupts # 查看中断分配(排查驱动中断问题) cat /proc/kmsg# 实时内核日志(同dmesg) cat /proc/[PID]/maps # 查看进程内存映射 |
2. sysfs(/sys):设备+驱动+总线信息
Linux 统一设备模型,可读写,用于调试驱动、硬件寄存器、设备状态:
Bash # 驱动/设备调试常用路径 ls /sys/class/# 设备类(gpio、i2c、tty等) ls /sys/devices/# 所有硬件设备 ls /sys/module/# 内核模块信息 # 实战示例:调试GPIO驱动 cat /sys/class/gpio/gpio10/value# 读取GPIO电平 echo 1 > /sys/class/gpio/gpio10/value # 写入GPIO电平 # 查看驱动绑定状态 cat /sys/bus/platform/drivers/xxx_driver/xxx_device |
三、硬件寄存器直调:devmem 硬核读写物理地址,跳过驱动速排硬件故障
嵌入式/驱动调试神器,直接读写物理地址,无需驱动即可调试硬件寄存器。
用法(busybox 自带工具)
Bash # 格式:devmem 物理地址 [位宽] [值] devmem 0x12340000# 读取 0x12340000 物理地址(默认32位) devmem 0x12340000 8# 8位读取 devmem 0x12340000 32 0x55# 写入 0x55 到该地址 # 场景:排查硬件寄存器配置错误、外设未响应问题 |
⚠️ 警告:随意读写物理地址会导致系统崩溃!仅用于硬件调试。
四、内核崩溃精准定位:Oops信息拆解,快速锁定崩溃代码行
内核/驱动崩溃时自动打印Oops(空指针、内存越界、非法指令),是定位崩溃代码的核心。
1. 关键信息解读
Plain Text BUG: Unable to handle kernel NULL pointer dereference at virtual address 00000000 PC : [] test_driver+0x50/0x100# PC指针:崩溃的代码地址 LR : [] 0xc0654321# 链接寄存器 Process bash (pid: 1234, stack limit: 0xcf000000) Stack: 0xcf001000: ...# 栈信息 |
•PC:崩溃的函数+偏移,直接定位代码行
•NULL pointer dereference:空指针访问(最常见)
•invalid address:非法内存访问
2. 定位代码行
Bash # 用addr2line解析PC地址 arm-linux-gnueabihf-addr2line -e vmlinux c0123456 # 输出:/driver/test/test.c:56→ 直接定位崩溃行 |
五、内存bug克星:KASAN 内核内存检测,越界/泄漏/野指针无所遁形
内核级内存越界、内存泄漏、野指针检测工具,比Oops更精准,是驱动稳定性调试必备。
1. 开启方法(内核配置)
Plain Text CONFIG_KASAN=y# 开启KASAN CONFIG_KASAN_GENERIC=y# 通用模式(推荐) CONFIG_DEBUG_KERNEL=y |
2. 调试效果
KASAN 会直接打印越界代码行、内存类型(栈/堆/全局),无需手动分析:
Plain Text BUG: KASAN: stack-out-of-bounds in test_func+0x20/0x50 Read of size 4 at addr 0xffffffc000001234 task bash |
直接告诉你:栈内存越界,在test_func函数第XX行。
六、内核用户态交互:ioctl 调试利器,高效测试驱动命令与参数传递
用户态与内核驱动交互的核心接口,用于调试驱动命令、参数传递。
1. 内核态(驱动实现)
驱动中实现 unlocked_ioctl 回调,处理用户态命令:
C long test_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { switch(cmd) { case TEST_CMD_READ:// 自定义命令 copy_to_user((void __user *)arg, &val, sizeof(val)); break; case TEST_CMD_WRITE: copy_from_user(&val, (void __user *)arg, sizeof(val)); break; default: return -ENOTTY; } return 0; } |
2. 用户态调试工具
编写简单C程序/使用 ioctl 工具测试驱动交互:
C // 用户态测试代码 int fd = open("/dev/test_dev", O_RDWR); ioctl(fd, TEST_CMD_WRITE, &write_val); // 发送命令给驱动 close(fd); |
✅ 用途:调试驱动命令是否正常响应、参数传递是否正确。
七、调试流程标准化:一套实战流程,搞定内核驱动全场景调试
给你一套驱动开发标准调试流程,直接套用:
1.加载驱动:insmod xxx.ko
2.看日志:dmesg -w 查看printk输出,排查初始化错误
3.查状态:cat /proc/modules、ls /sys/class/xxx 确认设备注册成功
4.硬件调试:devmem 0xXX 读写寄存器,验证硬件
5.功能测试:用户态程序调用ioctl,测试驱动功能
6.崩溃排查:Oops + addr2line 定位崩溃代码
7.稳定性测试:开启KASAN,检测内存问题
调试技巧核心总结
1.基础调试:printk 打印 + dmesg 查看日志
2.系统/设备状态:/proc 看系统信息,/sys 调试驱动设备
3.硬件寄存器:devmem 直接读写物理地址
4.崩溃定位:Oops信息 + addr2line 快速找bug
5.内存安全:KASAN 检测越界/泄漏(必用)
6.驱动交互:ioctl 完成用户态与内核态通信
这些是 Linux 内核/驱动调试最核心、最高频的技能,覆盖90%以上调试场景