《linux内核设计与实现》第十八章

第十八章 调试git

调试工做艰难是内核级开发区别于用户级开发的一个显著特色。算法

1、准备开始架构

  一、内和调试须要什么函数

  • 一个bug(大部分bug一般都不是行为可靠并且定义明确的)
  • 一个藏匿bug的内核版本(知道bug最先出如今哪一个内核版本中)
  • 相关内核代码的知识和运气(加深理解周围的代码)

  若是错误老是可以重现的话,消除错误的机会会很大。oop

2、内核中的bug性能

  bug发做时可能的症状:学习

  • 明白无误的错误代码(好比,没有把正确的值存放在恰当的位置)
  • 同步时发生的错误(好比共享变量锁定不当)
  • 错误地管理硬件(好比,给错误的控制寄存器发送错误的指令)
  • 从下降全部程序的运行性能到毁坏数据再到使得系统处于死锁状态

3、经过打印来调试测试

  printk()就是内核的格式化打印函数ui

  一、健壮性spa

  健壮性是printk()函数最容易让人们接受的一个特质。任什么时候候,任何地方都能调用它,内核中的printk()比比皆是。

  • 能够在中断上下文和进程上下中被调用
  • 能够在任何持有锁时被调用
  • 能够在多处理器上同时被调用,并且调用者连锁都没必要使用

  负责执行硬件体系结构相关的初始化动做的函数是:setup_arch()

  二、日志等级

  printk()和printf()在使用上最主要的区别就是前者能够指定一个日志级别。

  内核根据这个级别来判断是否在终端上打印消息。

  内核把级别比某个特定值低的全部消息显示在终端上。

  可供使用的记录等级:

 

 

  默认等级:KERN_WARNING

  三、记录缓冲区

  • 内核消息都被保存在一个LOG_BUF_LEN大小的环形队列中。
  • 该缓冲区大小能够在编译时经过设置CONFIG_LOG_BUF_SHIFT进行调整。
  • 在单处理器的系统上其默认值是16KB。(内核在同一时间只能保存16KB的内核消息)
  • 若是消息队列已经达到最大值,那么若是再有printk()调用时,新消息将覆盖队列中的老消息
  • 这个记录缓冲区之因此称为环形是由于它的读写都是按照环形队列方式进行操做的

  环形队列的优缺点:

  优势:

  • 因为同时读写环形缓冲区时,其同步问题很容易解决,因此即便在中断上下文中也能够方便地使用printfk()
  • 它使记录维护起来也更容易。若是有大量的消息同时产生,新消息只需覆盖掉旧消息便可。
  • 在某个问题引起大量消息的时候。记录只会覆盖掉它自己,而不会由于失控而消耗掉大量内存。

  缺点:

  • 可能会丢失消息

  四、syslogd和klogd

  • 用户空间的守护进程klogd从记录缓冲区中获取内核消息,再经过syslogd守护进程将它们保存在系统日志文件中。
  • klogd程序既能够从/proc/kmsg文件中,也能够经过syslog()系统调用读取这些消息
  • 默认状况下,它选择读取/proc方式实现,不论是哪一种方法,klogd都会阻塞,直到有新的内核消息可供读出。在被唤醒以后,它会读取出新的内核消息并进行处理,默认状况下,它就是把消息传给syslogd守护进程
  • syslogd守护进程把它接收到的全部消息添加进一个文件中,该文件默认是/va也r/log/messages。能够经过配置文件从新指定
  • 在启动klogd的时候,能够经过指定-c标志来改变终端的记录等级。

4、oops

  oops是内核告知用户有不行法神最经常使用的方式。

  oops中包含的重要信息对于全部体系结构都是彻底相同的:寄存器上下文和回溯线索

  是一个通过解码的oops,由于内存地址都已经装换成了对应的函数。

  • 回溯线索显示了致使错误发生的函数调用链。

  一、ksymoops

  回溯线索中的地址须要转化成有意义的符号名称才方便使用,这须要调用ksymoops命令。而且还必须提供编译内核时产生的System.map。若是使用的是模块,还须要一些模块信息。

  而后该程序就会吐出解码版的oops。若是ksymoops没法找到默认位置上的信息,或者想提供不一样信息,该程序能够接受许多参数。

  二、kallsyms

  配置选项CONFIG_KALLSYMS_ALL 表示不只存放函数名称,还存放全部的符号名称。

5、内核调试配置选项

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

6、引起bug并打印信息

  一些内核调用能够用来方便标记bug方便标记bug提供断言并输出信息。

  最经常使用的两个是BUG()和些声明BUG_ON()。当被调用的时候,它们会引起oops,致使栈的回溯和错误信息的打印。

7、神奇的系统请求键

  • 该功能能够经过定义CONFIG_MAGIC_SYSRQ配置选项来启用。
  • 当该功能被启用的时候,不管内核处于什么状态,均可以经过特殊的组合键跟内核进行通讯。
  • 除了配置选项之外,还要经过一个sysctl用来标记该特性的开或关。

  启用这个键的功能有2个方法:
  开启内核编译选项 : CONFIG_MAGIC_SYSRQ
  动态启用: echo 1 > /proc/sys/kernel/sysrq

 

  支持sysrq的命令:

 

8、内核调试器的传奇

一、gdb

二、kgdb

  kgdb是一个补丁,它可让咱们在远端主机上经过串口利用gdb的全部功能对内核进行调试。

  这须要两台计算机:第一台运行带有kgdb补丁的内核,第二台经过串行线使用gdb对第一台进行调试。

  经过kgdb的全部功能都能使用:读取或修改变量值,设置断点,设置关注变量,单步执行等。某些版本的gdb甚至容许执行函数。

9、探测系统

若是对内核调试有丰富的经验的话,那么你会掌握一些诀窍来帮助你更进一步地探测系统从而找到想要的答案。内核调试颇有挑战性,即便是一点小的暗示或者技巧都能给你很大的帮助咱们最好把它们联系起来。

一、用UID做为选择条件

  能够利用把用户id做为选择条件来实现这种功能,经过这种选择条件,能够安排到底执行哪一种算法。

  if (current->uid != 7777) {
      /* 老算法 */
  } else {
     /* 新算法 */
  }

二、使用条件变量

  • 若是代码与进程无关,或者但愿有一个针对全部状况都能使用的机制来控制某个特性,可使用条件变量。
  • 只须要建立一个全局变量做为一个条件选择开关(若是该变量为零,就使用一个分支上的代码。若是它不为零,就选择另一个分支)
  • 能够经过某种接口提供对这个变量的操控,也能够直接经过调试器进行操控。

三、使用统计量

  当须要掌握某个特定事件的发生规律而且须要比较多个事件并从中得出规律能够经过建立统计量并提供某种机制访问其统计结果。

四、重复频率限制

10、使用Git进行二分搜索

  若是你使用Git来控制Linux源码树的副本,那么Git将自动运行二分搜索进程。

  Git会在修订版本中进行二分搜索,这样能够找到具体哪次提交的代码引起了bug。

11、小结

  这一章讲的是内核的调试。咱们学习了几种技术:内核内置的调试架构、调试程序、记录日志、git二分法查找等。咱们知道了内和调试须要什么一个bug(大部分bug一般都不是行为可靠并且定义明确的)、一个藏匿bug的内核版本(知道bug最先出如今哪一个内核版本中)、相关内核代码的知识和运气(加深理解周围的代码)。以及内核中的bug可能发生的症状等相关问题。

相关文章
相关标签/搜索