《Linux内核设计与实现》——调试

来源:互联网 发布:网络舆情分析师工资 编辑:程序博客网 时间:2024/06/07 23:41

一、内核中的bug

  1、内核中的bug多种多样。它们产生可以有无数的原因,同时它们的表现也变化多端。从隐藏在源代码中的错误到展现在目击者前面的bug,往往是经历一系列连锁反应的时间

        才可能触发的。


  2、内核调试与其他的项目也并没有太大的不同。



二、通过打印来调试

 一)、健壮性

  1、内核提供内核格式化打印函数printk(),其与printf()函数几乎完全相同。


  2、健壮性是printk()函数最容易让人接受的一个特质。

    1)、可以在中断和进程上下文中使用。

    2)、可以在任何持有锁时被调用。

    3)、可以在多处理器上同时被调用。


  3、printk()函数忽地健壮性也难免会有漏洞。在系统功能启动过程中,终端还没有初始化之前,在某些地方不能使用它。



 二)、日志等级

  1、printk(0函数可以指定一个日志级别。内核根据这个日志级别来判断是否在中断上打印信息。内核把级别比某个特定值低的所有信息显示在终端上。


  2、可供使用的记录等级。(P297  表18-1)

    1)、如果没有指定一个记录等级,函数会选用默认的DEFAULT_MESSAGE_LOGLEVEL,现在默认等级是KERN_WARNING。

    2)、内核最重要的记录等级是KERN_EMERG定义为“<0>”,将无关的紧要的记录等级KERN_DEBUG定义为“<7>”。



 三)、记录缓冲区

  1、内核消息都被保存在一个LOG_BUG_LEN大小的环形队列中。该缓冲区大小可以在编译时通过设置CONFIG_LOG_BUG_SHITF。


  2、记录缓冲区使用环形队列的好处。

    1)、同步问题容易解决,在中断上下文也可以使用printk()函数。

    2)、使记录维护起来更容易。


  3、环形缓冲区的惟一缺点是——可能丢失消息。



 四)、syslogd和klogd

  1、在标准的Linux上,用户空间的守护进程klogd从记录缓冲区中获取内核消息,再通过syslogd守护进程将它们保存在系统日志文件中。


  2、klogd既可以从/proc/kmsg文件中,也可以通过syslog(0系统调用读取这些信息。被唤醒之后,它会取出新的内核消息并进行处理。默认情况下,他就是把消息传递给

        syslogd守护进程。


  3、syslogd守护进程把它接收到的所有信息添加进一个文件夹中,该文件默认是/var/messages。也可以通过/etc/sysllogd.conf配置文件重新指定。




三、oops

 一)、相关简介

  1、oops是内核告知用户有不幸发生的最常用方式。


  2、内核中出现的故障很难清理,所以内核往往要经历严峻的考验才能发出oops和靠它完成的一些清理工作。通常,发送完oops之后,内核会处于一种不稳定的状态。


  3、oops的产生有很多原因,其中包括内存访问月结或者非法的指令等。


  4、寄存上下文可能有用,尽管使用起来不那么方便。回溯线程显示了导致错误发生的函数调用链。



 二)、ksymoops

  1、ksymoops命令可以将回溯线索中的地址转化为有意义的符号名称,并且还必须提供编译是产生的System.map。入府哦使用的是模块,还需要一些模块信息。


  2、ksymoops会自行解析这些信息,所以一般这样调用它:

        ksymoops  save_oops.txt


  3、如果ksymoops无法找到默认位置上的信息,或者想提供不同的信息,该程序可以接受许多参数。



 三)、kallsyms

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


  2、CONFIG_KALLSTMS_ALL表示不仅存放函数名称,也存放所有的符号名称。CONFIG_KALLSTMS_EXTRA_PASS选项会引起内核构件过程中再次忽略内核目标代码。




四、内核调试配置选项

 1、在编译的时候,为了方便调试和测试内核代码,内核提供了许多配置选项。这些选项都在内核配置编译器的内核开发菜单项中,它们都依赖CONFIG_DEBUG_KERNEL。


  2、对调试有帮助的配置选项:slab映射调试选项,高端内存调试选项,I/O映射调试选项,自旋锁调试选项,栈溢出检查选项。


  3、内核提供的原子操作计数器可以被配置为一旦在原子操作过程中进程进入睡眠或者做了一些可能引起睡眠的操作,就打印并提供追踪线索。



五、引发bug并打印信息。

  1、一些内核调用可以用来方便标记bug,提供断言并输出信息。最常用的两个是BUG(0和BUG_ON()。当被调用的时候,它们会引发oops,导致栈的回溯和错误信息的打

        印。


  2、可以用panic()引发更严重的错误。调用panic()不但会打印错误信息,而且还会挂起整个系统。


  3、dump_stack()可以在终端上打印一下栈的回溯信息来帮助调试。



六、神奇的系统请求键

  1、系统请求键——该功能可以通过定义CONFIG_MAGIC_SYSRQ配置选项来启用。SysRq(系统请求)在大多数键盘都是标准键。当功能被被启用的时候,无论内核处于什

        么状态,都可以通过特殊的组合键跟内核进行通信。


  2、除了配置选项以外,还要通过一个sysctl用来标记该特性的开或关。需要启用它时使用如下命令:

       echo  1  >  /proc/sys/kernel/sysrq


  3、从中断上,可以输入Sysrq  h获取一份可用的选项列表。SysRq-s将“脏”缓冲区跟硬盘交换分区同步,SysRq-u卸载所有的文件系统,SysRq-b重启设备。


  4、神奇系统请求键是调试和挽救垂危系统所必须的一种工具。由于该功能对终端上的任何用户都提供服务,所以在重要的机器上启用它需要三思而后行。


  5、支持SysRq的命令。(P303  表18-2)



七、内核调试器传奇

 一)、gdb

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

        gdb  vmlinx  /proc/kcore

    1)、其中vmlinux文件是未经压缩的内核映像,怒视压缩过的zImage或bzImage,它存放在源代码树的根目录上。

    2)、/proc/kcore作为一个参数选项,是作为core文件来用的,通过它能够访问到内核驻留的高端内存。


  2、可以使用gdb的所有命令来获取信息。


  3、gdb的缺点

    1)、没有任何办法修改内核数据。

    2)、也不能单步执行内核代码,不能加断点。

   


 二)、kgdb

  1、kgdb是一个补丁,它可以让我们在远端主机上通过串口利用gdb的所有功能对内核进行调试。该补丁在Documemtation/目录下安装下很多说明文件。


  2、通过kgdb、gdb的所有功能:读取或修改变量值,设置断点,设置关注变量,单步执行等。某些版本的gdb甚至允许执行函数。




八、探测系统

 一)、用UID作为选择条件

  1、一般情况下,只要保留原有的算法而把新算法加入到其他位置上,基本就能保证安全。可以利用把用户id(UID)作为选择条件来实现这种功能,通过这种选择条件,可以

        安排到底执行那种算法:

        if(current-> != 7777)  {

            /*老算法*/

        }else{

           /*新算法*/

        };

    1)、除了7777以外,其他所有的用户都用的是老算法。


  2、可以创造一个UID为7777的用户,专门来测试新算法。对于要求很严格的进程相关部分的代码来说,这种方法使得测试变得容易许多。



 二)、使用条件变量

  1、如果代码与进程无关,或者希望有一个针对所有情况都能使用的机制来控制某个特性,可以使用条件变量。

    1)、只需要创建一个全局变量作为一个条件选择开关。如果该变量为零,就是用一个分支上的代码。如果它不为零,就选择另外一个分支。


  2、可以通过某种接口对这个变量的操控,也可以直接通过调试器进行操控。



 三)、使用统计量

  1、有些时候需要掌握某个特定事件的发生规律,有些时候需要比较多个事件并从中得出规律。可以通过创建统计量并提供某种机制访问其统计结果,很容易就能满足这种需

        求。


  2、注意,统计量并非是SMP安全的。理想的办法是通过原子操作进行实现。但是仅仅对于一个简单的每次加一的调试统计量,一般无需这么麻烦。



 四)、重复频率限制

  1、有两种技巧可以解决printk()引起的问题,第一种是重复频率限制,如果某种事件发生的非常频繁,而又需要观察它的整体进展情况,就可以让这种技巧执行。


  2、为了避免调试信息发生井喷,可以每隔几秒执行一次打印。


  3、另一个棘手的问题是你如何确认在特定的情况下某段代码确实被执行好了。


  4、不管重复频率限制用到的变量都应该是静态的(static),并且应该限制在函数的局部范围内,这样才能保证变量的值在经历多次函数调用后仍然能够保留下来。




九、用二分查找法找出引发罪恶的变更

 


十、使用GIT进行二分搜索

  1、GIT源码管理工具提供了一个有用的二分搜索机制。


  2、使用步骤

    1)、告诉GIT你需要的进行二分搜索树:

              git  bisect  start

    2)、然后在为GIT提供一个出现问题的最早内核版本:

              git  bisect  bad  <revision>

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

              git  bisect  bad

    3)、然后,为GIT提供一个最新的可正常运行的内核版本:

              git  bisect  good  v2.6.28

    4)、接下来,Linux将会利用二分搜索法在Linux源码树中,自动检测正常的内核版本和有bug的内核版本之间那个版本有隐患。接着在编译,运行以及测试正被检测的版

              本。如果此版本一切正常,可以运行下面命令:

              git  bisect  good

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

              igt  bisect  bad

    5)、可以指定git仅仅在与错误相关的目录列表中去二分搜索提交补丁:

              git  bisect  start - arch/x86

0 0
原创粉丝点击