调  试

一、准备开始

  • 一个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. python 从外部获取传入的参数

    有时候我们在执行python程序的时需要接收到外部传入的参数 python的 sys.argv[]就能实现 # test.py import sys #引入模块 str = sys.argv[1]pr ...

  2. ABAP 在被访问的程序中获取访问程序的全局变量

    前些日子接到过一个看起来比较普通的需求: 存在一个系统标准函数组FG01,内含函数模块FM00,FM01……等等.在系统程序中,FM00会调用FM01,通过FM01获取获取某些数据. 需求要求,复制一 ...

  3. 控件_CheckBox(多选按钮)

    import android.os.Bundle; import android.app.Activity; import android.widget.CheckBox; import androi ...

  4. python五十六课——正则表达式(常用函数之match)

    函数:match(regex,string,[flags=0])参数:regex:就是正则表达式(定义了一套验证规则)string:需要被验证的字符串数据flags:模式/标志位,默认情况下(不定义) ...

  5. Android back键及backWebview模式跳转详解

    首先,来看一下关于Android home键和back键区别 back键 Android的程序无需刻意的去退出,当你一按下手机的back键的时候,系统会默认调用程序栈中最上层Activity的Dest ...

  6. windos 系统文件包含

    解决VS2013找不到windows.h文件方法 右键属性管理器添加 包含路径(依据sdk安装目录,我的是64位系统) C:\Program Files (x86)\Microsoft SDKs\Wi ...

  7. ActiveMQ安装配置及使用

    ActiveMQ介绍 ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线.ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,尽管J ...

  8. MP实战系列(四)之DAO讲解

    说到DAO不得不提一个开发名词"三层架构",所谓的三层架构是什么呢?简单的可以概括为数据访问层,业务逻辑层,界面层(又称表现层). 这也是我们Java开发常用的手段,经常有人将三层 ...

  9. 18核心的Intel i9将在2019年夏发布

    受工艺和架构限制,Intel HEDT发烧级桌面平台面对AMD早已经优势不再,但升级仍然在继续. 去年10月份,Intel一方面发布了第二代酷睿i9 X系列,仍然基于14nm Skylake-X架构, ...

  10. Intel CPU 漏洞分析

    Intel CPU漏洞分析报告 预备知识 存储分级 由于计算机存储分级的特性(第一级:寄存器,第二级:高速缓存,第三级:内存,第四级:磁盘),每一级之间的访问速度差距高达数量级.所以处理器会将用到的数 ...