调  试

一、准备开始

  • 一个bug
  • 一个藏匿bug的内核版本
  • 相关内核代码的知识和运气
  • 知道这个bug最早出现在哪个内核版本中。

1、想要成功进行调试:

  • 让这些错误重现
  • 抽象出问题
  • 从代码中搜索

二、内核中的bug

  从隐藏在源代码中的错误到展现在目击者面前的bug,往往是经历一系列连锁反应的事件才可能触发的。内核确实有一些独特的问题需要考虑,像定时限制和竞争条件等,它们都是允许多个线程在内核中同时运行产生的结果。

三、通过打印来调试
  内核提供的打印函数printk()和C 库提供的printf()函数功能几乎相同。

1、健壮性

  • 健壮性是printk() 函数最容易让人们接受的一个特质..可以在中断上下文和进程上下中被调用,可以在任何持有锁时被调用,可以在多处理器上同时被调用,而且调用者连锁都不必使用。
  • 解决的办单是提供一个printk() 的变体函数一一early_printk(),这个函数在启动过程的初期就具有在终端上打印的能力,它的功能与prink()完全相同,区别仅仅在于名字和能够更早地工作,不过,由于该函数在一些内核支持的硬件体系结构上无法实现,所以这种办法峡少可移植性。

2、日志等级

  • printk()和printf()在使用上最主要的区别就是前者可以指定一个日志级别, 内核根据这个级别来判断是否在终端上打印消息。内核把级别比某个特定值低的所有消息显示在终端上。

3、记录缓冲区

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

4、syslogd 和klogd

  • syslogd 守护进程把它接收到的所有消息添加进一个文件中,该文件默认是/var/log/messages.也可以通过/etc/syslog.conf 配置文件重新指定。在启动klogd 的时候,可以通过指定-c 标志来改变终端的记录级。

四、oops

  • oops 是内核告知用户有不幸发生的最常用的方式。oops 的产生有很多可能原因,其中包括内存访问越界或者非法的指令等。

1、ksymoops
  如果使用的是模块, 还需要一些模块信息。ksymoops 通常会自行解析这些信息,调用后得到解码板的oops

ksymoops saved_ oops. txt

2、kallsyms

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

五、内核调试配置选项
  内核开发( Kernel hacking)菜单项中,它们都依赖于CONFIG_DEBUG_KERNEL。。其中最有用的一个是sleep-inside-spinlock-checking (自旋锁内睡眠选项)。使用锁时睡眠是引发死锁的元凶。所以,包括正使用锁的时候调用,正使用锁的时候以阻塞方式请求分配内存和在引用单CPU 数据时睡眼在内,各种潜在的bug 都能够被探测到。

六、引发bug 并打印信息
  最常用的两个是BUG() 和BUG_ON()。当披调用的时候,它们会引发oops ,导致栓的回溯和错误信息的打印可以把这些调用当做断言使用,想要断言某种情况不该发生:

if (bad_thing)
BUG();

  或者使用更好的形式:

BUG_ON (bad_thing);

  多数内核开发者相信BUG_ON()比BUG()更清晰、更可读, 而且BUG_ON()会将其声明作为一个语句放入unlikely()中。

  可以用panic()引发更严重的错误。调用panic()不但会打印错误消息,而且还会挂起整个系统。显然,只应该在最糟糕的情况下使用它

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

七、内核调试器的传奇
1、gdb

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

gdb vml inux /proc/kcore

  【其中vmlinux 文件是未经压缩的内核映像,不是压缩过的zlmage 或bzlmage,它存放在源代码树的根目录上。】

  • /proc/kcore 作为一个参数选项,是作为core 文件来用的,通过它能够访问到内核驻留的高端内存。只有超级用户才能读取此文件的数据。可以使用gdb 的所有命令来获取信息。如果编译内核的时候使用了-g 参数(在内核的Makefile 文件的CFLAGS 变量中加入-g),gdb 还可以提供更多的信息。

2、kgdb

  • kgdb 是一个补丁,它可以让我们在远端主机上通过串口利用gdb 的所有功能对内核进行调试。这需要两台计算机:第一台运行带有kgdb补丁的内核,第二台通过行线(不通过modem,直接连接两台机器的电缆)使用gdb对第一台进行调试。

八、使用Git 进行二分搜索

一开始,你得告诉Git 你要进行二分搜索:

$ git bisect start

然后再为Git 提供一个出现问题的最早内核版本:

$ git bisect bad <revision>

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

$ git bisect bad

然后,还得为Git 提供一个最新的可正常运行的内核版本:

$ git bisect good v2.6.28

如果这个版本一切正常,可以运行下面的命令:

$ git bisect good

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

$ git bisect bad

如果你已经知道引发bug 的源(比如, x86 机型的启动代码),你可以指定git仅仅在与错模相关的目录列表中去二分搜索提交的补丁。

$ git bisect start - arch/x86

《Linux内核设计与实现》 第十八章学习笔记的更多相关文章

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

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

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

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

  3. 《Linux内核设计与实现》Chapter 18 读书笔记

    <Linux内核设计与实现>Chapter 18 读书笔记 一.准备开始 一个bug 一个藏匿bug的内核版本 知道这个bug最早出现在哪个内核版本中. 相关内核代码的知识和运气 想要成功 ...

  4. 《Linux内核设计与实现》Chapter 3 读书笔记

    <Linux内核设计与实现>Chapter 3 读书笔记 进程管理是所有操作系统的心脏所在. 一.进程 1.进程就是处于执行期的程序以及它所包含的资源的总称. 2.线程是在进程中活动的对象 ...

  5. 《Linux内核设计与实现》第四周读书笔记——第五章

    <Linux内核设计与实现>第四周读书笔记--第五章 20135301张忻 估算学习时间:共1.5小时 读书:1.0 代码:0 作业:0 博客:0.5 实际学习时间:共2.0小时 读书:1 ...

  6. 《Linux内核设计与实现》Chapter 1 读书笔记

    <Linux内核设计与实现>Chapter 1 读书笔记 一.Unix的特点 Unix从Multics中产生,是一个强大.健壮和稳定的操作系统. 特点 1.很简洁 2.在Unix系统中,所 ...

  7. 《Linux内核设计与实现》Chapter 2 读书笔记

    <Linux内核设计与实现>Chapter 2 读书笔记 一.获取内核源码 1.使用Git 我们曾经在以前的学习中使用过Git方法 $ git clone git://git.kernel ...

  8. 《Linux内核设计与实现》Chapter 5 读书笔记

    <Linux内核设计与实现>Chapter 5 读书笔记 在现代操作系统中,内核提供了用户进程与内核进行交互的一组接口,这些接口的作用是: 使应用程序受限地访问硬件设备 提供创建新进程与已 ...

  9. LINUX内核设计与实现第三周读书笔记

    LINUX内核设计与实现第三周读书笔记 第一章 LINUX内核简介 1.1 Unix的历史 1969年的夏天,贝尔实验室的程序员们在一台PDR-7型机上实现了Unix这个全新的操作系统. 1973年, ...

  10. 《Linux内核设计与实现》第一二章笔记

    第一章 linux内核简介 每个处理器在任何时间点上的活动必然概括为下列三者: 运行于用户空间,执行用户进程 运行于内核空间,处于进程上下文,代表某个特定的进程执行 运行于内核空间,处于中断上下文,与 ...

随机推荐

  1. Tribonacci UVA - 12470 (简单的斐波拉契数列)(矩阵快速幂)

    题意:a1=0;a2=1;a3=2; a(n)=a(n-1)+a(n-2)+a(n-3);  求a(n) 思路:矩阵快速幂 #include<cstdio> #include<cst ...

  2. 20145203盖泽双 《网络对抗技术》实践八:Web基础

    20145203盖泽双 <网络对抗技术>实践八:Web基础 1.实践目标 (1)编写Web前端--含有表单的HTML代码. (2)编写Web前端--javascipt验证用户名.密码的代码 ...

  3. go标准库的学习-database/sql/driver

    参考:https://studygolang.com/pkgdoc 1>导入方式: import "database/sql/driver" driver包定义了应被数据库驱 ...

  4. rac添加新节点的步骤与方法(官方步骤与自我测试)

    Extending the Oracle Grid Infrastructure Home to the New NodeNow that the new node has been configur ...

  5. 剑指offer从上往下打印二叉树 、leetcode102. Binary Tree Level Order Traversal(即剑指把二叉树打印成多行、层序打印)、107. Binary Tree Level Order Traversal II 、103. Binary Tree Zigzag Level Order Traversal(剑指之字型打印)

    从上往下打印二叉树这个是不分行的,用一个队列就可以实现 class Solution { public: vector<int> PrintFromTopToBottom(TreeNode ...

  6. [转]系统架构演变--集中式架构-垂直拆分-分布式服务-SOA(服务治理)-微服务

    一.系统架构演变 1.1. 集中式架构 当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本.此时,用于简化增删改查工作量的数据访问框架(ORM)是影响项目开发的关键. 存在的 ...

  7. MATLAB——神经网络pureline激活函数

  8. Wi-Fi无线控制器开发例程(基础篇)

    动手来做自己的WIFI远程控制插座吧! 如果感觉视频不容易入门可以看这里 https://www.cnblogs.com/yangfengwu/p/10100152.html WIFI远程控制器系统方 ...

  9. 4-(基础入门篇)学会刷Wi-Fi模块固件(刷AT指令固件)

    http://www.cnblogs.com/yangfengwu/p/8965054.html 基础教程源码链接如果失效,请在淘宝介绍中下载,由于链接很容易失效,如果失效请联系卖家,谢谢 https ...

  10. 8-51单片机ESP8266学习-AT指令(单片机采集温湿度数据通过8266发送给C#TCP客户端显示)

    http://www.cnblogs.com/yangfengwu/p/8785516.html 先写单片机端的程序 先把源码和资料链接放到这里 链接: https://pan.baidu.com/s ...