zoukankan      html  css  js  c++  java
  • 《Linux内核设计与实现》CHAPTER18阅读梳理

    《Linux内核设计与实现》CHAPTER18阅读梳理

    【学习时间:2hours】

    【学习内容:bug的来源分析;bug调试途径】

    一、bug来源

    1.内核中的bug

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

    bug二、调试方法

    1.通过打印来调试

    1. 优势:
      1. 健壮性是printk()函数最容易让人们接受的一个特质(使用环境和时机很广泛);
      2. printk()函数变体——early-printk()函数,区别仅在于可以更早(甚至在启动初期,终端还没有初始化之前)地工作
    2. 与printf()的区别
      1. printk()函数可以指定一个日志级别;内核更具指定级别与当前终端的等级console_loglevel来决定是否打印(只打印比当前终端等级低的消息);
      2. 终端默认的记录等级是KERN_WARNING
      3. 内核将最重要的记录等级KERNEMERG定为<0>;将无关紧要的记录等级KERNDEBUG定义为<7>
    3. 缓冲区
      1. 内核消息都被记录在环形队列中,以队列方式进行读写;大小可以通过设置CONFIGLOGBUF_SHIFT进行调整
      2. 在单处理器上,该缓冲区大小默认为16KB,也就是说,超过的消息将覆盖旧消息
      3. 优势:
        1. 读写同步问题容易解决
        2. 记录的维护更加方便
    4. 相关进程
      1. 用户空间的守护进程——klogd从记录缓冲区中读取内核信息,再通过syslogd守护进程将它们保存在系统日志文件中
        1. klogd会阻塞知道==直到有新的内核消息可供读出。被唤醒之后,它会读出新的内核消息并进行处理(默认情况下,就是传递给syslogd);
        2. syslogd会将所有接收到的消息添加到(默认情况下是messages)文件中

    2.通过oops判断bug

    1. 概述
      • oops是内核告知用户有不幸发生的最常用方式(因为内核是整个系统的管理者,不能将自己杀死,也很难自行修复);
      • 通常,发送了oops之后,内核会处于不稳定的状态;如果oops在其他进程(除了0号idle和1号init进程)运行的时候发生,内核会杀死这些进程并尝试继续执行
    2. 信息
      • 回溯线索&寄存器上下文
        • 回溯消息展示了导致错误发生的函数调用链
        • 通过寄存器上下文中不正常的值可以判断哪个函数的变量出了问题
    3. 使用
      • 使用ksymoops
      • 将回溯线索中的地址转换成有意义的符号名称:
        • ksymoops saved_oops.txt
      • 使用kallsyms
      • 通过定义CONFIG_KALLSYMS配置选项启用,该选项中存放内核镜像中相应函数地址的符号名称,内核可以打印解码好的跟踪线索

    3.内和调试配置项

    1. 配置项:CONFIGDEBUGKERNEL
    2. 选项 之 sleep-inside-spinlockchecking(自旋锁内睡眠选项)——正在使用自旋锁或者禁止抢占的代码进行的是原子操作,不可更改
    3. 探测范围:
      • 正在使用锁的时候调用schedule();
      • 正使用锁的时候以阻塞方式请求分配内存;
      • 引用单CPU数据时睡眠

    4.引发bug并打印信息

    1. 利用BUG()以及BUG_ON()(因为大多数体系结构都把这两个函数定义成某种非法操作,可以触发oops)
      • 当做断言或者条件语句
    2. 调用panic()函数会在打印错误信息的同时挂起系统
      • panic("terrible thing",terrible_thing);
    3. 调用dump_stack(),只在终端上打印寄存器上下文及函数的跟踪线索

    5.系统请求键

    1. 配置项:CONFIGMAGICSYSR配置选项来启动;此外,通过/proc/sys/kernel/sysrq标记该特性的开关
    2. 优点:无论内核处于什么状态,都可以通过特殊的组合键跟内核进行通信
    3. 常见的命令
      • SysRq-b:重启设备
      • SysRq-o:关闭机器
      • SysRq-u:卸载所有的文件系统
      • SysRq-s:把所有已安装的文件系统都刷新到磁盘

    6.内核调试器

    1. 启动内核调试器
      • gdb vmlinux(未经压缩的内核映像)
    2. 反汇编函数
      • disassemble function(为什么要反汇编?因为有些执行文件无法获取到源代码,所以需要反汇编来获得代码,从而进行调试来查找问题)

    7.探测系统

    1. 使用UID作为选择条件

      if(current->uid != 7777)
      {
          /*老算法*/
      else
      {
          /*新算法*/
      }
      
    2. 使用条件变量
    3. 使用统计量
    4. 重复频率限制
      1. 每隔几秒钟执行几次打印
      2. printk_ratelimit()函数
        • 默认情况下,此函数每隔5秒钟执行产生一次信息
      3. 实现发生次数限制
    5. 使用Git进行二分搜索

    课堂内容总结

    1.操作系统启动之后,开始的init与shell都只是普通程序

    2.为什么系统调用后再调度?

    系统调用咋返回用户态之后需要确定要返回哪个用户进程

    3.Makefile的参数?

    一共有4个:all,rootfs,.c.o,clean

    总结

    本章讲的虽然是“调试”,但并不只是debug;而是从系统的错误出发,探寻如何“解决”这些错误、从哪里寻求线索。我自己调试错误的经常使用的就是printk()以及debug()。

  • 相关阅读:
    Codevs 2602 最短路径问题
    NOIp2015酱油酱油记
    51Nod-1091 线段的重叠
    poj-3264-Balanced Lineup
    51Nod-1212 无向图最小生成树
    51Nod-1279 扔盘子
    51Nod--1010 只包含235的数
    51Nod--1015 水仙花数
    51Nod-1136 欧拉函数
    使用caffe训练自己的CNN
  • 原文地址:https://www.cnblogs.com/lwr-/p/5316605.html
Copyright © 2011-2022 走看看