转自http://www.cnblogs.com/wang_yb/p/3504539.html
内核调试的难点在于它不能像用户态程序那样打断点,随时暂停查看各个变量的状态。也不能像用户态程序那样崩溃后迅速的重启,恢复初始状态。
用户态程序和内核交互,用户态程序的各种状态、错误等都可以由内核来捕获并显示。
而内核是直接和硬件交互的,内核出错之后整个系统就无法正常运行了,所以要想熟练地进行内核调试,首先要熟悉内核已经提供给我们的工具,然后实实在在地去做一些内核功能的开发,在开发的过程中不断熟悉内核代码,增加内核调试的经验。
内核调试的难点
- 重现bug困难-如果能够重现一个bug,相当于成功了一半。(特别是有些bug和硬件相关,执行几百万次后才有一次错误)
- 调试风险比较大-稍有不慎,即造成系统崩溃。
- 定位bug的初始版本困难-内核版本更新很快,很难确定bug是在哪个版本开始出现的。
内核调试的工具和方法
输出log
输出log不光是内核调试,即使是在用户态程序的调试中,也是经常使用的一个调试手段。
通过在可以的代码周围加上一些LOG输出,可以准确地了解bug发生前后的一些重要信息。
1.log等级
linux内核中输出LOG的函数是 printk (语法和printf几乎雷同, 唯一的区别是printk可以指定日志级别)
printk之所以好用, 就在与它随时都可以被调用, 没有任何限制条件.
printk的输出日志级别如下:
等级 | 描述 |
---|---|
KERN_EMERG | 一个紧急情况 |
KERN_AKERT | 一个需要被立即注意到的错误 |
KERN_CRIT | 一个临界情况 |
KERN_ERR | 一个错误 |
KERN_WARNING | 一个警告 |
KERN_NOTICE | 一个普通的,不过也有可能需要注意的情况 |
KERN_INFO | 一条非正式的消息 |
KERN_DEBUG | 一条调试信息-一般是冗余信息 |
输出示例:
printk(KERN_WARNING "This is a warning!\n");
printk(KERN_DEBUG "This is a debug notice!\n");
2.log记录
标准linux系统上, printk 输出log之后, 由用户空间的守护进程klogd从缓冲区中读取内核消息, 然后再通过syslogd守护进程将它们保存在系统日志文件中.
syslogd 将接受到的所有内核消息添加到一个文件中, 该文件默认为: /var/log/dmesg (系统Centos6.4 x86_64)
PS. 上篇博客中的内核模块的输出LOG, 都可以在 /var/log/dmesg 中看到
3.主动触发bug
调试中有时将某些情况下标记为bug, 执行到这些情况时, 提供断言并输出信息.
BUG 和 BUG_ON 就是2个可以主动触发oops的内核调用.
在不应该被执行到的地方使用 BUG 或者 BUG_ON 来捕获.
比如:
if (bad_thing)
BUG();
// 或者
BUG_ON(bad_thing);
如果想要触发更为严重的错误, 可以使用 panic() 函数
比如:
if (terrible_thing)
panic("terrible thing is %ld\n", terrible_thing);
此外, 还有dump_stack 函数可以打印寄存器上下文和回溯信息.
比如:
if (!debug_check) {
printk(KERN_DEBUG "provide some information...\n");
dump_statck();
}