您的位置:首页 > 运维架构 > Linux

《Linux内核设计与实现》 第十八章学习笔记

2016-03-24 11:41 405 查看

调 试

一、准备开始

一个bug

一个藏匿bug的内核版本

相关内核代码的知识和运气

知道这个bug最早出现在哪个内核版本中。

1、想要成功进行调试:

让这些错误重现

抽象出问题

从代码中搜索

二、内核中的bug

  从隐藏在源代码中的错误到展现在目击者面前的bug,往往是经历一系列连锁反应的事件才可能触发的。内核确实有一些独特的问题需要考虑,像定时限制和竞争条件等,它们都是允许多个线程在内核中同时运行产生的结果。

[b]三、通过打印来调试[/b]
  内核提供的打印函数printk()和C 库提供的printf()函数功能几乎相同。

1、健壮性

健壮性是printk() 函数最容易让人们接受的一个特质..可以在中断上下文和进程上下中被调用,可以在任何持有锁时被调用,可以在多处理器上同时被调用,而且调用者连锁都不必使用。

解决的办单是提供一个printk() 的变体函数一一early_printk(),这个函数在启动过程的初期就具有在终端上打印的能力,它的功能与prink()完全相同,区别仅仅在于名字和能够更早地工作,不过,由于该函数在一些内核支持的硬件体系结构上无法实现,所以这种办法峡少可移植性。

2、日志等级

printk()和printf()在使用上最主要的区别就是前者可以指定一个日志级别, 内核根据这个级别来判断是否在终端上打印消息。内核把级别比某个特定值低的所有消息显示在终端上。

3、记录缓冲区

内核消息都被保存在一个LOG_BUF_LEN 大小的环形队列中。该缓冲区大小可以在编译时通过设置CONFIG_LOG_BUF_SHIFf 进行调整。在单处理器的系统上其默认值是16。如果消息队列已经达到最大值,那么如果再有printk()调用,新消息将覆盖队列中的老消息。这个记录缓冲区之所以称为环形,是因为它的读写都是按照环形队列方式进行操作的。

环形缓冲区的唯一缺点一一可能会丢失消息

4、syslogd 和klogd

syslogd 守护进程把它接收到的所有消息添加进一个文件中,该文件默认是/var/log/messages.也可以通过/etc/syslog.conf 配置文件重新指定。在启动klogd 的时候,可以通过指定-c 标志来改变终端的记录级。

[b]四、oops[/b]

oops 是内核告知用户有不幸发生的最常用的方式。oops 的产生有很多可能原因,其中包括内存访问越界或者非法的指令等。

1、ksymoops
  如果使用的是模块, 还需要一些模块信息。ksymoops 通常会自行解析这些信息,调用后得到解码板的oops


ksymoops saved_ oops. txt


2、kallsyms

通过定义CONFIG_KALLS YMS 配置选项启用。该选项存放着内核镜像中相应函数地址的符号名称,所以内核可以打印解码好的跟踪线索

[b]五、内核调试配置选项[/b]
  内核开发( Kernel hacking)菜单项中,它们都依赖于CONFIG_DEBUG_KERNEL。。其中最有用的一个是sleep-inside-spinlock-checking (自旋锁内睡眠选项)。使用锁时睡眠是引发死锁的元凶。所以,包括正使用锁的时候调用,正使用锁的时候以阻塞方式请求分配内存和在引用单CPU 数据时睡眼在内,各种潜在的bug 都能够被探测到。

[b]六、引发bug 并打印信息[/b]
  最常用的两个是BUG() 和BUG_ON()。当披调用的时候,它们会引发oops ,导致栓的回溯和错误信息的打印可以把这些调用当做断言使用,想要断言某种情况不该发生:


if (bad_thing)
BUG();


  或者使用更好的形式:


BUG_ON (bad_thing);


  多数内核开发者相信BUG_ON()比BUG()更清晰、更可读, 而且BUG_ON()会将其声明作为一个语句放入unlikely()中。

  可以用panic()引发更严重的错误。调用panic()不但会打印错误消息,而且还会挂起整个系统。显然,只应该在最糟糕的情况下使用它


if (terrible_thing)
panic ( ” terrible thing is %ld\n ”, terrible_thing);



七、内核调试器的传奇
1、gdb

可以使用标准的GNU 调试器对正在运行的内核进行查看。针对内核启动调试器的方站与针对进程的方栋大致相同:


gdb vml inux /proc/kcore


  【其中vmlinux 文件是未经压缩的内核映像,不是压缩过的zlmage 或bzlmage,它存放在源代码树的根目录上。】

/proc/kcore 作为一个参数选项,是作为core 文件来用的,通过它能够访问到内核驻留的高端内存。只有超级用户才能读取此文件的数据。可以使用gdb 的所有命令来获取信息。如果编译内核的时候使用了-g 参数(在内核的Makefile 文件的CFLAGS 变量中加入-g),gdb 还可以提供更多的信息。

2、kgdb

kgdb 是一个补丁,它可以让我们在远端主机上通过串口利用gdb 的所有功能对内核进行调试。这需要两台计算机:第一台运行带有kgdb补丁的内核,第二台通过行线(不通过modem,直接连接两台机器的电缆)使用gdb对第一台进行调试。

八、使用Git 进行二分搜索

一开始,你得告诉Git 你要进行二分搜索:


$ git bisect start


然后再为Git 提供一个出现问题的最早内核版本:


$ git bisect bad <revision>


如果当前的内核版本就是引发bug的罪魁祸首,那么就不必提供内核版本:


$ git bisect bad


然后,还得为Git 提供一个最新的可正常运行的内核版本:


$ git bisect good v2.6.28


如果这个版本一切正常,可以运行下面的命令:


$ git bisect good


如果这个版本运行有异常一一也就是说,如果证明这个给定的内核版本有bug,可以运行:


$ git bisect bad


如果你已经知道引发bug 的源(比如, x86 机型的启动代码),你可以指定git仅仅在与错模相关的目录列表中去二分搜索提交的补丁。


$ git bisect start - arch/x86

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: