zoukankan      html  css  js  c++  java
  • 《Linux内核设计与实现》第十八章读书笔记

    1.内核中的bug

    1. 内核中的bug表现得不像用户级程序中那么清晰——因为内核、用户以及硬件之间的交互会很微妙;
    2. 从隐藏在源代码中的错误到展现在目击者面前的bug,往往是经历一系列连锁反应的事件才可能触发的。

    内核调试的难点

    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!
    ");
    printk(KERN_DEBUG "This is a debug notice!
    ");
    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
    ", terrible_thing);

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

    比如:

    if (!debug_check) {
        printk(KERN_DEBUG "provide some information...
    ");
        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 "错误信息....
    ");
    }

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

    static unsigned long limit = 0;
    
    if (limit < 5) { /* 输出5次错误信息后就不再输出 */
        limit++;
        printk(KERN_ERR "错误信息....
    ");
    }

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

    Git 提供的二分搜索机制:

    git bisect start  # 开始二分搜索
    git bisect bad <bad_revision> # 指定一个bug出现的内核版本号
    git bisect good <good_revision> # 指定一个没有bug的内核版本号, 此时git会检测2个版本直接的隐患
    
    # 根据结果再次设置 bad 和 good 的版本号, 缩小Git检索范围, 直至找到可疑之处为止.
  • 相关阅读:
    java 执行 jar 包中的 main 方法
    seven habits of highly effective people 高效能人士的七个习惯
    支付系统对账算法优化方案 转
    iso 培训笔记
    Android日常开发总结的技术经验60条 转
    ANDROID学习之路 转
    Businessworks的设计思想
    JVM内存模型和性能优化 转
    高可用架构设计与实践
    大规模分布式存储实战
  • 原文地址:https://www.cnblogs.com/20135223heweiqin/p/5330398.html
Copyright © 2011-2022 走看看