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

18.1准备开始

内核调试往往是一个令人挠头不已的漫长过程。幸运的是,在这些费劲的问题中也有不少比较简单而且容易消灭的小bug,运气好你可能面对的是些简单的小bug。

  1. 开始做―些调查之前,不会清楚到底面对的是什么现在,需要的只是:
  2. 一个bug。听起来很可笑,但确实需要一个确定的bug。如果错误总是能够重现的话,那对我们会有很大的帮助(有一部分错误确实如此)。然而不幸的是,大部分bug通常都不是行为可靠而且定义明确的。
  3. 一个藏匿bug的内核版本。如果你知道这个bug最早出现在哪个内核版本中那就再理想不过了。
  4. 相关内核代码的知识和运气。调试内核其实是一个棘手的问题。不过对周围的代码理解得越多调试起来也就越轻松。

18.2 内核中的bug

内核中的bug多种多样,它们的产生可以有无数的原因,同时它们的表象也变化多端

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

18.3通过打印来调试

  1. printk():内核的格式化打印函数

1. 健壮性

弹性极佳的函数:任何时候、任何地方都能调用它

可以在中断上下文和进程上下文中被调用

可以在任何持有锁时被调用

可以在多处理器上同时被调用

除非再启动过程中的初期就要在终端上输出

2. 日志等级

printk()和printf()在使用上最主要的区别就是前者可以指定一个日志级别。内核通过这个级别来判断是否在终端上打印消息。

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

  • 0 KERN_EMERG 最重要
  • 7 KERN_DEBUG 最不重要

调试信息, 有两种赋予记录等级的方法:

  • 保持终端的默认记录等级不变,给所有调试信息KERN_CRIT或更低的等级。
  • 给所有调试信息KERN_DEBUG等级,调整终端的默认记录等级。

3. 记录缓冲区

内核消息保存在一个LOG_BUF_LEN大小的环形队列中,读写都是按照环形队列方式操作的。

大小是可以在编译时通过CONFIG_LOG_BUF_SHIFT进行调整。

在单处理器的系统上默认值是16kb,即内核在同一时间只能保存16kb的内核消息,再多的话新消息就会覆盖老消息。

优点:

  • 同步问题易解决
  • 记录维护容易

    缺点:
  • 可能会丢失消息

18.4、oops

1. 概述

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

内核很难自我修复,也不能将自己杀死,只能发布oops,过程:

  • 向终端上输出错误消息
  • 输出寄存器中保存的信息
  • 输出可供跟踪的回溯线索

    通常发送完oops之后,内核会处于一种不稳定状态。

寄存器上下文信息也很有用,比如帮助冲进引发问题的现场

2. ksymoops

调用ksymoops命令:将回溯线索中的地址需要转化成有意义的符号名称

并且还必须提供编译内核时产生的System.map。如果用的是模块,还需要调用一些模块信息:kysmoop saved_oops.txt

3. kallsyms

现在的版本中不需要使用kysmoops这个工具,因为可能会发生很多问题,新版本中引入了kallsyms,可以通过定义CONFIG_KALLSYMS配置选项启用。

18.5 内核调试配置选项

在编译的时候,为了方便调试和测试内核代码,内核提供了许多配置选项。在内核配置编辑器的内核开发菜单。这些选项中,它们都依赖于CONFIG_DEBUG_KERNEL。当开发内核的时候,作为一种练习,不妨打开所有这些选项。

有些选项确实有用,这些选项确实能完成不少调试工作。

18.6 引发bug并打印信息

一些内核调用可以用来方便标记bug方便标记bug提供断言并输出信息。最常用的两个是BUG()和些声明BUG_ON()。当被调用的时候,它们会引发oops,导致栈的回溯和错误信息的打印。大部分体系结构把BUG()和BUG_ON()定义成某种会导致oops跟硬件的体系结构是相关的非法操作,这样自然会产生需要的oops。可以把这些调用当做断言使用,想要断言某种情况不该发生。

18.7 神奇的系统请求键

神奇的系统请求键是另外一根救命稻草,该功能可以通过定义CONFIG_MAGIC_SYSRQ配置选项来启用。

当该功能被启用的时候,无论内核处于什么状态,都可以通过特殊的组合键跟内核进行通信。这种功能可以让你在面对一台奄奄一息的系统时能完成一些有用的工作。除了配置选项以外,还要通过一个sysctl用来标记该特性的开或关。

需要启用它时使用如下命令:echo 1> /proc/sys/kernel/sysrq

18.8 内核调试器的传奇

很多内核开发者一直以来都希望能拥有一个用于内核的调试器.不幸的是,Linus不愿意在它的内核源代码树中加入一个调试器。他认为调试器会误导开发者,从而致引入不良的修正,没有人能对他的逻辑提出异议从真正理解代码出发,确实更能保证修正的正确性。然而,许多内核开发者们还是希望有一个官方发布的、用于内核的调试器。因为这个要求看起来不会马上被满足,所以许多补丁应运而生了,它们为标准内核附加上了内核调试的支持,虽然这都是―些不被官方认可的附加补丁,但它们确实功能完善,十分强大。在我们深入这些解决方案之前,先看看标准的调试器gdb能够给我们一些什么帮助是―个不错的选择。

18.9 探测系统

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

18.9.1 用UID作为选择条件

假设为了加入一个激动人心的新特性,你重写了fork()系统调用。除非第一次的尝试就完美无缺,否则系统调试就是―场噩梦。如fork()系统调用不正常的话,压根就不用指望整个系统还能正常工作。当然,和任何时候一样,希望总是存在的,一般情况下,只要保留原有的算法而把你的新算法加入到其他位置上,基本就能保证安全:可以利用把用户id作为选择条件来实现这种功能,通过这种选择条件,可以安排到底执行哪种算法。

18.9.2 使用条件变量

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

18.9.3 使用统计量

有些时候你需要掌握某个特定事件的发生规律。有些时候需要比较多个事件并从中得出规律。通过创建统计量并提供某种机制访问其统计结果,很容易就能满足这种需求。举个例子,假设我们希望得到foo和bar的发生频率,那么在某个文件中,当然最好是在定义该事件的那个文件里定义两个全局变量。

18.9.4 重复频率限制

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

18.11 使用Git进行二分搜索

  1. Git源码管理工具提供了一个有用的二分搜索机制。如果你使用Git来控制Linux源码树的副本,那么Git将自动运行二分搜索进程。此外,Git会在修订版本中进行二分搜索,这样可以找到具体哪次提交的代码引发了bug。很多Git相关的任务比较繁杂,但使用Git进行二分搜索并不那么的困难。

20135320赵瀚青LINUX第十八章读书笔记的更多相关文章

  1. 20135320赵瀚青LINUX第四章读书笔记

    概述 什么是进程调度 进程调度:在可运行态进程之间分配有限处理器时间资源的内核子系统. 一.调度策略 4.1进程类型 I/O消耗型进程:大部分时间用来提交I/O请求或是等待I/O请求,经常处于可运行状 ...

  2. 20135320赵瀚青LINUX第三章读书笔记

    第三章 进程管理 3.1 进程 进程的定义: 是处于执行期的程序以及它所包含的资源的总称. 线程的定义: 是在进程中活动的对象. 每个线程都拥有一个独立的程序计数器.进程栈和一组进程寄存器. 内核调度 ...

  3. 20135320赵瀚青LINUX第五章读书笔记

    第五章--系统调用 5.1 与内核通信 作用 1.为用户空间提供一种硬件的抽象接口 2.保证系统稳定和安全 3.除异常和陷入,是内核唯一的合法入口. API.POSIX和C库 关于Unix接口设计:提 ...

  4. 20135320赵瀚青LINUX第八周学习笔记

    赵瀚青原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 概述 本周学习的是linux ...

  5. 20135320赵瀚青LINUX第七周学习笔记

    赵瀚青原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 概述 本周学习的内容主要是讨 ...

  6. 20135320赵瀚青LINUX第五周学习笔记

    赵瀚青原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 概述 按照刘老师的周从三个角 ...

  7. 20135320赵瀚青LINUX第六周学习笔记

    赵瀚青原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 概述 这周主要讲解的是进程. ...

  8. 期末总结20135320赵瀚青LINUX内核分析与设计期末总结

    赵瀚青原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 对LINUX内核分析与设计这 ...

  9. 20135320赵瀚青LINUX内核分析第一周学习笔记

    赵瀚青原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.概述 第一周的学习内容主 ...

随机推荐

  1. Linux安装vsftpd

    卸载vsftpd sudo yum remove vsftpd 安装vsftpd sudo yum -y install vsftpd 创建一个文件夹用来当作ftp得仓库 cd / sudo mkdi ...

  2. HDFS 常用Shell命令

    HDFS Shell命令 概述 HDFS Shell命令允许使用命令行在HDFS存储中进行文件夹和文件操作. 如文件夹的增删改查.文件的增删改查等. 开始练习hadoop时,打开Linux之后要用 s ...

  3. HDU 5877 2016大连网络赛 Weak Pair(树状数组,线段树,动态开点,启发式合并,可持久化线段树)

    Weak Pair Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Tota ...

  4. easyui datagrid行内编辑

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/ ...

  5. 小程序 Page is not constructed because it is not found.

    如下错误一般发生在点击事件切换页面的时候 解决方式: 在需要切换到的那个页面的js文件中添加Page({ })方法即可解决此问题. Tis:在js文件中输入Page回车,可自动添加Page方法,包括里 ...

  6. 自定义HTTP头时的注意事项(转)

    原文:https://blog.gnuers.org/?p=462 HTTP头是可以包含英文字母([A-Za-z]).数字([0-9]).连接号(-)hyphens, 也可义是下划线(_).在使用ng ...

  7. (2.5)Mysql之SQL基础——数据类型

    (2.5)Mysql之SQL基础——数据类型 关键词:mysql数据类型 目录: 一.整数型 二.小数型(以下均不能使用无符号) 三.日期时间型 四.字符串型 一.整数型 额外参数示例: int [( ...

  8. sqlserver表结构查询语句

    SELECT syscolumns.name,systypes.name,syscolumns.isnullable,syscolumns.length FROM syscolumns, systyp ...

  9. 主成分分析PCA学习一条龙

    转自:https://yoyoyohamapi.gitbooks.io/mit-ml/content/%E7%89%B9%E5%BE%81%E9%99%8D%E7%BB%B4/articles/PCA ...

  10. R中的一些基础1106

    1.R中NA,NaN,Inf代表什么? NA:缺失数据 NaN:无意义的数,比如sqrt(-2) Inf:正无穷大 -Inf:负无穷大 2.确定一个数值型vector的第一个最值(最大/最小)的下标: ...