1. 内核调试的难点

  1. 重现bug困难
  2. 调试风险比较大
  3. 定位bug的初始版本困难

2. 内核调试的工具和方法

2.1 输出 LOG

输出LOG不光是内核调试, 即使是在用户态程序的调试中, 也是经常使用的一个调试手段.

通过在可疑的代码周围加上一些LOG输出, 可以准确的了解bug发生前后的一些重要信息.

2.1.1 LOG等级

linux内核中输出LOG的函数是 printk (语法和printf几乎雷同, 唯一的区别是printk可以指定日志级别)

printk之所以好用, 就在与它随时都可以被调用, 没有任何限制条件.

printk的输出日志级别如下:

等级

描述

KERN_EMERG 一个紧急情况
KERN_ALERT 一个需要立即被注意到的错误
KERN_CRIT 一个临界情况
KERN_ERR 一个错误
KERN_WARNING 一个警告
KERN_NOTICE 一个普通的, 不过也有可能需要注意的情况
KERN_INFO 一条非正式的消息
KERN_DEBUG 一条调试信息--一般是冗余信息

输出示例:

printk(KERN_WARNING "This is a warning!\n");
printk(KERN_DEBUG "This is a debug notice!\n");
2.1.2 LOG记录

标准linux系统上, printk 输出log之后, 由用户空间的守护进程klogd从缓冲区中读取内核消息, 然后再通过syslogd守护进程将它们保存在系统日志文件中.

syslogd 将接受到的所有内核消息添加到一个文件中, 该文件默认为: /var/log/dmesg (系统Centos6.4 x86_64)

2.2 oops

oopss是个拟声词, 类似 "哎哟" 的意思. 它是内核通知用户有不幸发生的最常用方式.

触发一个oops很简单, 其实只要在上篇博客中的那些内核模块示例中随便找一个, 里面加上一段给未初始化的指针赋值的代码, 就能触发一个oops

oops中包含错误发生时的一些重要信息(比如, 寄存器上下文和回溯线索), 对调试bug很有帮助!

调试内核时, 还可以开启内核编译参数中的各种和内核调试相关的选项, 那样还可以给我们提供内核崩溃时的一些额外信息.

2.3 主动触发bug

调试中有时将某些情况下标记为bug, 执行到这些情况时, 提供断言并输出信息.

BUG 和 BUG_ON 就是2个可以主动触发oops的内核调用.

在不应该被执行到的地方使用 BUG 或者 BUG_ON 来捕获.

比如:

if (bad_thing)
BUG();
// 或者
BUG_ON(bad_thing);

如果想要触发更为严重的错误, 可以使用 panic() 函数

比如:

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

此外, 还有dump_stack 函数可以打印寄存器上下文和回溯信息.

比如:

if (!debug_check) {
printk(KERN_DEBUG "provide some information...\n");
dump_statck();
}

2.4 系统请求键

这个系统请求键可以在一个快挂了的系统上输出一些有用的信息.

这个按键一般就是标准键盘上的 [SysRq] 键 (就在 F12 键右边, 其实就是windows中截整个屏幕的按键)

单独按那个键相当于截屏, 按住 ALT + [SysRq] = [SysRq]的功能

启用这个键的功能有2个方法:

  • 开启内核编译选项 : CONFIG_MAGIC_SYSRQ
  • 动态启用: echo 1 > /proc/sys/kernel/sysrq

支持 SysRq 的命令如下:

主要命令

描述

SysRq-b 重新启动机器
SysRq-e 向init以外的所有进程发送SIGTERM信号
SysRq-h 在控制台显示SysRq的帮助信息
SysRq-i 向init以外的所有进程发送SIGKILL信号
SysRq-k 安全访问键:杀死这个控制台上的所有程序
SysRq-l 向包括init的所有进程发送SIGKILL信号
SysRq-m 把内存信息输出到控制台
SysRq-o 关闭机器
SysRq-p 把寄存器信息输出到控制台
SysRq-r 关闭键盘原始模式
SysRq-s 把所有已安装文件系统都刷新到磁盘
SysRq-t 把任务信息输出到控制台
SysRq-u 卸载所有已加载文件系统

2.5 内核调试器 gdb和kgdb

linux内核的调试器可以使用 gdb或者kgdb, 配置比较麻烦, 准备实际用调试的时候再去试试效果如何..

2.6 探测系统

下面一些方法是在修改内核后, 用来试探内核反应的小技巧.

2.6.1 用UID控制内核执行

比如在内核中加入了新的特性, 为了测试特性, 可以用UID来控制内核是否执行新特性.

if (current->uid != 7777) {
/* 原先的代码 */
} else {
/* 新的特性 */
}
2.6.2 用条件变量控制内核执行

也可以设置一些条件变量来控制内核是否执行某段代码.

条件变量可以像上篇博客中那样, 设置在 sys 文件系统的某个文件中. 当文件中的值变化时, 通知内核执行相应的代码.

2.6.3 使用统计量观察内核执行某段代码的频率

实现思路就是在内核中的设置一个全局变量, 比如 my_count, 当内核执行到某段代码时, 给 my_count + 1 就行.

同时还要将 my_count 打印出来(可以用printk), 便于随时查看它的值.

2.6.4 控制内核执行某段代码的频率

有时侯, 我们需要在内核发生错误时打印错误相关的信息, 如果这个错误不会导致内核崩溃, 并且这个错误每秒会发生几百次甚至更多.

那么, 用printk输出的信息会非常多, 给系统造成额外的负担.

这时, 我们就需要想办法控制错误输出的频率, 有2种方法:

方法1: 隔一段时间才输出一次错误

static unsigned long prev_jiffy = jiffies;  /* 频率限制 */

if (time_after(jiffies, prev_jiffy + 2*HZ)) {  /* 输出间隔至少 2HZ */
prev_jiffy = jiffies;
printk(KERN_ERR "错误信息....\n");
}

方法2: 输出 N 次之后, 不再输出(N是正整数)

static unsigned long limit = 0;

if (limit < 5) { /* 输出5次错误信息后就不再输出 */
limit++;
printk(KERN_ERR "错误信息....\n");
}

2.7 二分法查找bug发生的最初内核版本

Git 提供的二分搜索机制:

git bisect start  # 开始二分搜索
git bisect bad <bad_revision> # 指定一个bug出现的内核版本号
git bisect good <good_revision> # 指定一个没有bug的内核版本号, 此时git会检测2个版本直接的隐患 # 根据结果再次设置 bad 和 good 的版本号, 缩小Git检索范围, 直至找到可疑之处为止.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  9. Linux内核设计期中总结

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

随机推荐

  1. 作为一名GIS从业人员,这些网站你应该关注

    前言:今年工作的第二年,端午节后,入职新公司.总算是回归本行,从事GIS相关工作.这个系列算是对在公司工作和学习成长的记录吧. 数据篇:       作为遥感的商业应用,首先考虑的是遥感数据的产品化. ...

  2. 加载驱动三种execute

    executeQuery executeUpdate executeQueryBatch

  3. vue-cli静态资源处理

    vue-cli是利用webpack进行打包部署,其中静态资源的路径问题是一个比较麻烦的部分. 项目中共有两个存放静态文件的地方. /static 根目录下的static文件夹 assets src目录 ...

  4. BZOJ 1085 骑士精神 迭代加深搜索+A*

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1085 题目大意: 在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个 ...

  5. BZOJ4894:天赋(矩阵树定理)

    Description 小明有许多潜在的天赋,他希望学习这些天赋来变得更强.正如许多游戏中一样,小明也有n种潜在的天赋,但有一些天赋必须是要有前置天赋才能够学习得到的. 也就是说,有一些天赋必须是要在 ...

  6. Vue表单输入绑定(文本框和复选框)

    文本框 <!DOCTYPE html><html>    <head>        <meta charset="utf-8">  ...

  7. 关于OpenCV2.4.9在VS2012上的配置

    今天写着篇文章是由于自从上次电脑换硬盘今天再次安装OpenCV又遇到了一些问题,最后终于搞定,,,,用的版本是2.4.9,,,因为第一次配置用3.0的没有配置成功,而2.4.9的配置成功. 首先当然是 ...

  8. STM32 & FreeRTOS & KFIFO (巧夺天工)

    巧夺天工 的 KFIFO ,用STM32实现. 实现源文件如下: /********************************************************** * * 文件名 ...

  9. DM9161 和 STM32F107 和 FreeRTOS 和 LWIP

    1.首先移植 FreeRTOS到  107上 可以正常运行. 可以到群下载移植好的,文件名称:STM32F107VC+FreeRTOS V8.2.3+kfifo(巧夺天工)! 2.第二步 :ST 官方 ...

  10. Hadoop中ssh+IP、ssh+别名免秘钥登录配置

    1.为什么要进行 SSH 无密码验证配置? Hadoop运行过程中需要管理远端Hadoop守护进程,在Hadoop启动以后,NameNode是通过SSH(Secure Shell)来启动和停止各个Da ...