第十八章 调试

调试工作艰难是内核级开发区别于用户级开发的一个显著特点。

一、准备开始

  1、内和调试需要什么

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

  如果错误总是能够重现的话,消除错误的机会会很大。

二、内核中的bug

  bug发作时可能的症状:

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

三、通过打印来调试

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

  1、健壮性

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

  • 可以在中断上下文和进程上下中被调用
  • 可以在任何持有锁时被调用
  • 可以在多处理器上同时被调用,而且调用者连锁都不必使用

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

  2、日志等级

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

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

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

  可供使用的记录等级:

  默认等级:KERN_WARNING

  3、记录缓冲区

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

  环形队列的优缺点:

  优点:

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

  缺点:

  • 可能会丢失消息

  4、syslogd和klogd

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

四、oops

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

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

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

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

  1、ksymoops

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

  然后该程序就会吐出解码版的oops。如果ksymoops无法找到默认位置上的信息,或者想提供不同信息,该程序可以接受许多参数。

  2、kallsyms

  配置选项CONFIG_KALLSYMS_ALL 表示不仅存放函数名称,还存放所有的符号名称。

五、内核调试配置选项

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

六、引发bug并打印信息

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

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

七、神奇的系统请求键

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

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

 

  支持sysrq的命令:

八、内核调试器的传奇

1、gdb

2、kgdb

  kgdb是一个补丁,它可以让我们在远端主机上通过串口利用gdb的所有功能对内核进行调试。

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

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

九、探测系统

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

1、用UID作为选择条件

  可以利用把用户id作为选择条件来实现这种功能,通过这种选择条件,可以安排到底执行哪种算法。

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

2、使用条件变量

  • 如果代码与进程无关,或者希望有一个针对所有情况都能使用的机制来控制某个特性,可以使用条件变量。
  • 只需要创建一个全局变量作为一个条件选择开关(如果该变量为零,就使用一个分支上的代码。如果它不为零,就选择另外一个分支)
  • 可以通过某种接口提供对这个变量的操控,也可以直接通过调试器进行操控。

3、使用统计量

  当需要掌握某个特定事件的发生规律并且需要比较多个事件并从中得出规律可以通过创建统计量并提供某种机制访问其统计结果。

4、重复频率限制

十、使用Git进行二分搜索

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

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

十一、小结

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

  

《linux内核设计与实现》第十八章的更多相关文章

  1. Linux内核设计与实现 第十八章

    1. 内核调试的难点 重现bug困难 调试风险比较大 定位bug的初始版本困难 2. 内核调试的工具和方法 2.1 输出 LOG 输出LOG不光是内核调试, 即使是在用户态程序的调试中, 也是经常使用 ...

  2. Linux内核设计与实现第十周读书笔记

    第十七章 设备与模块 关于设备驱动与设备管理,我们讨论四种内核成分. 设备类型 模块 内核对象 sysfs 17.1设备类型 在Linux以及所有Unix系统中,设备被分为以下三种类型: 块设备,块设 ...

  3. 《Linux内核》读书笔记 第十八章

  4. 《Linux内核设计与实现》课本第十八章自学笔记——20135203齐岳

    <Linux内核设计与实现>课本第十八章自学笔记 By20135203齐岳 通过打印来调试 printk()是内核提供的格式化打印函数,除了和C库提供的printf()函数功能相同外还有一 ...

  5. linux内核设计与实现一书阅读整理 之第十八章

    CHAPTER 18 调试 18.1 准备开始 需要的是准备是: - 一个bug - 一个藏匿bug的内核版本 - 相关内核代码的知识和运气 重点: 想要成功的进行调试,就取决于是否能让这些错误重现. ...

  6. 《Linux内核设计与实现》读书笔记(十九)- 可移植性

    linux内核的移植性非常好, 目前的内核也支持非常多的体系结构(有20多个). 但是刚开始时, linux也只支持 intel i386 架构, 从 v1.2版开始支持 Digital Alpha, ...

  7. 《Linux内核设计与实现》读书笔记(十八)- 内核调试

    内核调试的难点在于它不能像用户态程序调试那样打断点,随时暂停查看各个变量的状态. 也不能像用户态程序那样崩溃后迅速的重启,恢复初始状态. 用户态程序和内核交互,用户态程序的各种状态,错误等可以由内核来 ...

  8. 《Linux内核设计与实现》读书笔记(十二)- 内存管理【转】

    转自:http://www.cnblogs.com/wang_yb/archive/2013/05/23/3095907.html 内核的内存使用不像用户空间那样随意,内核的内存出现错误时也只有靠自己 ...

  9. 读《Linux内核设计与实现》我想到了这些书

          从题目中可以看到,这篇文章是以我读<Linux内核设计与实现>而想到的其他我读过的书,所以,这篇文章的主要支撑点是<Linux内核>.       开始读这本书已经 ...

  10. Linux内核设计期中总结

    Linux内核设计期中总结 ● 知识点 一.计算机是如何工作的 计算机是按照冯·诺依曼存储程序的原理. 在执行程序时须先将要执行的相关程序和数据放入内存储器中,在执行程序时CPU根据当前程序指针寄存器 ...

随机推荐

  1. 【PAT】B1080 MOOC期终成绩(25 分)

    还是c++好用,三部分输入直接用相同的方法, 用map映射保存学生在结构体数组中的下标. 结构体保存学生信息,其中期末成绩直接初始化为-1, 注意四舍五入 此题还算简单 #include<ios ...

  2. Linux之shell脚本for、while、case语句的高级用法

    1.case语句的用法: [root@ELK-chaofeng test]# cat test3.sh #!/bin/bash while true ;do read -p "please ...

  3. linux运行apache出现403错误

    1.文档权限问题,这是linux操作系统下经常会遇到的问题,需要使用chmod的指令把网站所在目录的权限提升到755.2.SElinux,开启它也会导致403错误的产生. 查看SELinux状态:1. ...

  4. JS学习小结(上)

    学而时习之,不亦说乎,开启JS学习新乐章~ JS是干啥的?网页特效,它主要是实现控制结构和样式,是一种行为,有多重要,不言而喻吧,页面炫酷的资本. 1. JS输出: alert("hello ...

  5. [TJOI2018]教科书般的亵渎

    嘟嘟嘟 题面挺迷的,拿第一个样例说一下: 放第一次亵渎,对答案产生了\(\sum_{i = 1} ^ {10} i ^ {m + 1} - 5 ^ {m + 1}\)的贡献,第二次亵渎产生了\(\su ...

  6. SSM框架之整合(Maven实例)

    有不少朋友在maven中因为pom文件依赖的事导致报错 今天我这个快速搭建ssm框架,确保在jdk7或者jdk8的环境,tomcat没什么要求.但如果要用jdk8的话,最好用run as中的serve ...

  7. 如何在Ubuntu18.04(Bionic Beaver)系统安装Teamviewer远程控制软件

    首先,打开TeamViewer的下载页面,下载Debian/Ubuntu的Deb安装包. 这里有64位和32位安装包选项.可以在Terminal(终端)中输入uname -a 查看自己系统版本是64位 ...

  8. 静态分析Android程序

    快速定位Android程序的关键代码 1.通过apktool反编译apk文件,得到AndroidManifest.xml文件,可以得到程序用到的组建.配置.以及主Activity 2.信息反馈法(特殊 ...

  9. 扫盲记-第六篇--Normalization

    深度学习模型中的Normalization 数据经过归一化和标准化后可以加快梯度下降的求解速度,这就是Batch Normalization等技术非常流行的原因,Batch Normalization ...

  10. QT pri 文件的作用

    i 是什么东西?包含(include)的首字母.类似于C.C++中的头文件吧,我们可以把 *.pro 文件内的一部分内容单独放到一个 *.pri 文件内,然后包含进来. 接前面的例子,我们将源文件的设 ...