1. 内核日志系统概述:printk机制、日志级别、日志缓冲区原理
大家好,我是你们的讲师。今天咱们来聊聊内核日志系统。说实话,搞驱动调试,日志就是你的眼睛。没有它,你就像在黑屋子里找一只黑猫——而且猫还不一定在屋里。
我个人习惯,拿到一个新板子,第一件事就是先把printk调通。为什么?因为后面所有调试手段,都得靠它来验证。你想想看,连日志都打不出来,你还能指望什么?
1.1 printk机制:内核的“printf”
printk,说白了就是内核版的printf。但跟用户态那个printf不一样,它有几个关键特性:
- 能在任何上下文调用——中断、自旋锁、原子操作里都能用
- 不需要用户态进程参与——内核自己就把活干了
- 有日志级别控制——这个后面细说
我记得刚入行那会儿,有个前辈跟我说:“printk是驱动工程师的瑞士军刀。”当时我不信,后来踩了无数坑才明白——这话一点不夸张。
// 最简单的printk用法
printk("Hello, kernel world!\n");
// 带参数的printk
printk("irq = %d, data = 0x%x\n", irq, data);
1.2 日志级别:控制信息的“音量”
日志级别,你可以理解成音量旋钮。有些信息你希望永远能看到(比如系统崩溃),有些信息只在调试时需要(比如每次中断触发)。
内核定义了8个日志级别,从0到7,数字越小越紧急:
| 级别 | 宏定义 | 含义 | 我常用的场景 |
|---|---|---|---|
| 0 | KERN_EMERG | 紧急事件,系统不可用 | 内核panic前最后一条消息 |
| 1 | KERN_ALERT | 必须立即处理 | 硬件严重故障 |
| 2 | KERN_CRIT | 临界条件 | DMA传输失败 |
| 3 | KERN_ERR | 错误条件 | 驱动初始化失败 |
| 4 | KERN_WARNING | 警告 | 寄存器值异常 |
| 5 | KERN_NOTICE | 正常但重要 | 设备热插拔事件 |
| 6 | KERN_INFO | 信息性消息 | 驱动版本信息 |
| 7 | KERN_DEBUG | 调试信息 | 寄存器读写跟踪 |
// 实际使用示例
printk(KERN_ERR "DMA transfer failed: status = 0x%x\n", status);
printk(KERN_INFO "Driver version %s loaded\n", DRV_VERSION);
printk(KERN_DEBUG "reg 0x%x = 0x%x\n", reg_addr, reg_val);
1.3 日志缓冲区原理:环形缓冲区
printk打出来的信息去哪了?答案是内核日志缓冲区。这个缓冲区是个环形结构,说白了就是个固定大小的数组,写满了就从开头覆盖。
为什么会这样设计?你想想看,内核日志可能在任何时刻产生——中断里、调度器里、甚至系统快崩溃的时候。这时候你不可能去分配内存、打开文件。环形缓冲区的好处就是:
- 分配简单——编译时就定好了大小
- 写入快——就是memcpy,没有锁竞争(早期版本)
- 永不阻塞——写满了就覆盖,不会等
我记得有一次调试一个USB驱动,中断频率特别高,每秒几千次。printk打得太勤,结果环形缓冲区被刷得飞快,关键信息全被覆盖了。后来我学乖了——调试信息要控制频率,或者用tracepoint代替。
// 查看日志缓冲区大小(内核配置)
// CONFIG_LOG_BUF_SHIFT 决定大小,通常是17(128KB)
// 可以通过dmesg查看当前缓冲区使用情况
# dmesg | head -1
[ 0.000000] Linux version 5.10.0 ...
- 环形缓冲区大小固定,默认约128KB
- 写满后覆盖最旧的数据
- 可以通过内核参数log_buf_len调整大小
- dmesg命令读取的就是这个缓冲区
1.4 实际调试中的坑与技巧
说了这么多理论,来点实战经验。我这些年踩过的坑,总结成几条:
- 别在中断里打太多日志——printk虽然快,但也不是零开销。高频率中断里打日志,轻则丢数据,重则系统卡死。
- 日志级别要分层——我习惯把驱动分成三层:初始化用KERN_INFO,运行时错误用KERN_ERR,调试用KERN_DEBUG。这样发布时只关掉DEBUG就行。
- 善用dmesg -w——这个命令可以实时查看日志,比cat /var/log/messages方便多了。
- 缓冲区不够怎么办?——加内核参数:log_buf_len=4M。我曾经在调试一个网络驱动时,把缓冲区扩到16MB才够用。
1.5 小结
printk机制,说白了就是内核给你留的一扇窗。通过它,你能看到内核里发生了什么。日志级别帮你控制信息的“音量”,环形缓冲区保证系统再忙也能记录。
下一章,咱们聊聊怎么用这些日志来定位驱动问题。到时候我会分享一个我印象特别深的案例——一个看起来是硬件问题,实际上是printk用错了的bug。
嗯,今天就到这儿。记住:调试驱动,日志先行。