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

CHAPTER 18 调试

18.1 准备开始

需要的是准备是: - 一个bug - 一个藏匿bug的内核版本 - 相关内核代码的知识和运气

重点: 想要成功的进行调试,就取决于是否能让这些错误重现。如若不能,消灭bug就只能通过抽象出问题,再从代码中寻找蛛丝马迹来进行了。

18.2 内核中的bug

  1. bug出现时可能的症状:

    • 错误代码。(如没把正确的值存放在恰当的位置)
    • 同步时发生的错误。(如共享变量锁定不当)
    • 错误的管理硬件。(如给错误的控制寄存器发送错误的指令)
    • ......
  2. 内核bug发作时可能的症状:
    • 降低所有程序的运行性能
    • 毁坏数据
    • 使得系统处于死锁状态

    - .......

    概述:

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

18.3 通过打印来调试

18.3.1 健壮性

  1. 健壮性是printk()函数最容易让人们接受的一个特质,在任何时候,任何地方都能调用它。

    • 在中断上下文和进程上下文中被调用
    • 在任何持有锁时被调用
    • 在多处理器上同时被调用,并且不必使用锁。
  2. printk()函数变体——early-printk()函数,区别仅在于可以更早(甚至在启动初期,终端还没有初始化之前)地工作
  3. 弹性极佳。

18.3.2 日志等级

  • printk() 与printf()主要的的区别:前者可以指定一个日志级别,内核根据这个级别来判断是否打印消息。内核把级别比某个特定值低的所有消息显示在终端上。
  • 终端默认的记录等级是KERN_WARNING
  • 内核将最重要的记录等级KERNEMERG定为<0>;将无关紧要的记录等级KERNDEBUG定义为<7>
  • 0 KERNEMERG 最重要 …… 7 KERNDEBUG 最不重要
  • 对于调试信息, 有两种赋予记录等级的方法:
    • 保持终端的默认记录等级不变,给所有调试信息KERN_CRIT或更低的等级。
    • 给所有调试信息KERN_DEBUG等级,调整终端的默认记录等级。

18.3.3 记录缓冲区

  1. 内核消息都被记录在环形队列中,以队列方式进行读写;大小可以通过设置CONFIGLOGBUF_SHIFT进行调整
  2. 在单处理器上,该缓冲区大小默认为16KB,也就是说,超过的消息将覆盖旧消息
  3. 优势:
    • 读写同步问题容易解决
    • 记录的维护更加方便
  4. 缺点:可能会丢失信息。

18.3.4 syslogd和klogd ###

这是两个用户空间的守护进程,klogd从记录缓冲区中获取内核消息,再通过syslogd守护进程将他们保存在系统日志文件中。 - klogd

- 既可以从/proc/kmsg文件中,也可以通过syslog()系统调用读取这些消息。
- 默认是/proc方式。
- 两种情况klogd都会阻塞,知道有新的内核消息可供读出,唤醒之后默认处理是将消息传给syslogd。
- 可以通过-c标志来改变终端的记录等级
  • syslogd

    • 将它接收到的所有消息添加到一个文件中,默认是/var/log/messages。

18.4 oops

  • • oops是内核告知用户有不幸发生的最常用方式(因为内核是整个系统的管理者,不能将自己杀死,也很难自行修复)
  • • 通常,发送了oops之后,内核会处于不稳定的状态;如果oops在其他进程(除了0号idle和1号init进程)运行的时候发生,内核会杀死这些进程并尝试继续执行
  • 过程包括:
    • 向终端上输出错误消息
    • 输出寄存器中保存的信息
    • 输出可供跟踪的回溯线索
  • 关于oops发生的时机:

1.发生在中断上下文:内核无法继续,会陷入混乱,导致系统死机

2.发生在idle进程或init进程(0号进程和1号进程),同上

3.发生在其他进程运行时,内核会杀死该进程并尝试着继续执行

  • oops发生的可能原因:

    内存访问越界

    非法的指令

    ……

  • oops中包含的重要信息:寄存器上下文和回溯线索回溯线索:显示了导致错误发生的函数调用链。

    寄存器上下文信息也很有用,比如帮助冲进引发问题的现场

18.4.1 ksymoops

• 将回溯线索中的地址转换成有意义的符号名称:

           •    ksymoops saved_oops.txt

18.4.2 kallsyms

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

18.5 内核调试配置选项

  • 位于内核配置编辑器的内核开发菜单项中,都依赖于CONFIGDEBUGKERNEL。

    slab layer debugging slab层调试选项 high-memory debugging 高端内存调试选项 I/O mapping debugging I/O映射调试选项 spin-lock debugging 自旋锁调试选项 stack-overflow debugging 栈溢出检查选项 sleep-inside-spinlock checking 自旋锁内睡眠选项 ……

- 原子操作:指那些能够不分隔执行的东西;在执行时不能中断否则就是完不成的代码。

 例如;正在使用一个自旋锁或禁止抢占的代码。
      使用锁时睡眠是引发死锁的元凶。

18.6 引发bug并打印信息

  1. 利用BUG()以及BUG_ON()(因为大多数体系结构都把这两个函数定义成某种非法操作,可以触发oops)

    • 当做断言或者条件语句
  2. 调用panic()函数会在打印错误信息的同时挂起系统
    • panic("terrible thing",terrible_thing)
  3. 调用dump_stack(),只在终端上打印寄存器上下文及函数的跟踪线索

18.7 神奇的系统请求键

  • 这个功能可以通过定义CONFIGMAGICSYSRQ配置选项来启用。SysRq(系统请求)键在大多数键盘上都是标准键。
  • 该功能被启用时,无论内核出于什么状态,都可以通过特殊的组合键和内核进行通信。
  • 除了配置选项以外,还要通过一个sysctl用来标记该特性的开或关,启动命令如下:
     echo 1 > /proc/sys/kernel/sysrq
    
  • Sysrq的几个命令:
     SysRq-s:将“脏”缓冲区跟硬盘交换分区同步
     SysRq-u:卸载所有的文件系统
     SysRq-b:重启设备

18.8 内和调试的传奇

18.8.1 gdb

- 启动内核调试器

    gdb vmlinux(未经压缩的内核映像)-
  • 可以使用gdb的所有命令来获取信息。例如:

         打印一个变量的值:
             p global_variable
    
         反汇编一个函数:
             disassemble function
    
            -g参数还可以提供更多的信息。
    
  • 局限性:

    没有办法修改内核数据

    不能单步执行内核代码

18.8.2 kgdb

  • 是一个补丁 ,可以让我们在远程主机上通过串口利用gdb的所有功能对内核进行调试。
  • 需要两台计算机:仪态运行带有kgdb补丁的内核,第二胎通过串行线使用gdb对第一台进行调试。

- 通过kgdb,gdb的所有功能都能使用:

     - 读取和修改变量值
     - 设置断点
     - 设置关注变量
     -  单步执行

18.9 探测系统

18.9.1 使用uid作为选择条件

  1. if(current->uid != 7777)
  2. {
  3. /老算法/
  4. else
  5. {
  6. /新算法/
  7. }

18.9.2 使用条件变量

  • 如果代码与进程无关,或者希望有一个针对所有情况都能使用的机制来控制某个特性,可以使用条件变量。
  • 这种方式比使用UID更简单,只需要创建一个全局变量作为一个条件选择开关:
      如果该变量为0,就使用某一个分支上的代码;
      否则,选择另外一个分支。
    
  • 操控方式:某种接口,或者调试器。

18.9.3 使用统计量

  • 这种方法常用于使用者需要掌握某个特定事件的发生规律的时候。
  • 方法是创建统计量,并提供某种机制访问其统计结果。

定义全局变量

        在/proc目录中创建一个文件
       or新建一个系统调用
       or通过调试器直接访问(最直接)

18.9.4.重复频率限制

(1)重复频率限制 (2)发生次数限制

18.10 用二分查找法找出引发罪恶的变更

18.11 使用Git进行二分搜索

总结

本章主要讲内核的调试,调试过程就是一种寻求实现与目标偏差的行为,通过学习各种调试技巧,,发现困难重重,但相信有一天努力会有成果的。

参考资料

《linux内核设计与实现》原书第三版

时间: 2024-10-11 17:47:22

linux内核设计与实现一书阅读整理 之第十八章的相关文章

linux内核设计与实现一书阅读整理 之第三章

chapter 3 进程管理 3.1 进程 进程就是处于执行期的程序. 进程就是正在执行的程序代码的实时结果. 内核调度的对象是线程而并非进程. 在现代操作系统中,进程提供两种虚拟机制: 虚拟处理器 虚拟内存 进程是处于执行期的程序以及相关的资源的总称. 进程包括代码段和其他资源. 几个函数: fork():创建新进程 exec():创建新的地址空间并把新的程序载入其中 clone():fork实际由clone实现 exit():退出执行 wait4():父进程查询子进程是否终结 wait().

linux内核设计与实现一书阅读整理 之第一二章整合

第一章:Linux内核简介 一.Unix和linux Unix是一个强大.健壮和稳定的操作系统. 1.Unix内核特点 十分简洁:仅提供几百个系统调用并且有明确的目的: 在Unix中,大部分东西都被(或者正致力于)被当做文件对待: Unix内核即相关系统工具软件都是用C语言编写的,这使得系统有着强大的可移植性: Unix进程创建非常迅速,目标在于一次执行保质保量地完成一个任务 2.Linux与Unix异同 Linux是基于Unix的类系统,比如它也实现了Unix的API: 但它不同于Unix,没

Linux内核设计与实现高清版pdf免费下载

下载地址:网盘下载 备用地址:网盘下载 内容简介编辑<Linux内核设计与实现(原书第3版)>基于Linux 2.6.34内核详细介绍了Linux内核系统,覆盖了从核心内核系统的应用到内核设计与实现等各方面的内容.<Linux内核设计与实现(原书第3版)>主要内容包括:进程管理.进程调度.时间管理和定时器.系统调用接口.内存寻址.内存管理和页缓存.VFS.内核同步以及调试技术等.同时<Linux内核设计与实现(原书第3版)>也涵盖了Linux 2.6内核中颇具特色的内容

Linux内核设计与实现下载 &#143719;

下载地址: http://www.gqylpy.com/di/15 <Linux内核设计与实现>PDF高清完整版-下载 内容简介 编辑 <Linux内核设计与实现(原书第3版)>基于Linux 2.6.34内核详细介绍了Linux内核系统,覆盖了从核心内核系统的应用到内核设计与实现等各方面的内容.<Linux内核设计与实现(原书第3版)>主要内容包括:进程管理.进程调度.时间管理和定时器.系统调用接口.内存寻址.内存管理和页缓存.VFS.内核同步以及调试技术等.同时&l

读书笔记2013-2 Linux内核设计与实现A

<Linux内核设计与实现> 简介 这本书不是想Linux源码剖析那样一行行分析Linux源代码的书,而是从Linux历史,Linux哲学,Linux设计原理和原则,计算机硬件相关知识,编译安装内核实战等多方面多角度讲述和Linux相关的方方面面.从中学到的更多的不单内核的源代码,而是Linux的哲学.建议所有从事Linux相关工作的猿都要读一下,读完之后,很多东西都变得容易理解,知其然,知其所以然.如果没有读过<深入理解计算机>类似的图书,建议和<深入理解计算机>一起

《LINUX内核设计的艺术》第一章从开机家电到执行main函数之前的过程 学习笔记之一

从开机加电到实行main函数之前的过程 分为三步,目的是实现从启动盘加载操作系统程序,完成实现main函数的准备工作 启动BLOS,准备是模式下的中断向量表和中断服务程序 从启动盘加载操作系统程序到内存.加载操作系统程序就是靠第一步实现的 为实现32位的main函数做过度工作 1.1启动blos,准备实模式下的中断向量表和中断服务程序 由blos来加载软件操作系统的任务 1.1.1         BLOS的启动原理 0XFFFF0 由硬件来启动,CPU硬件设计逻辑设计为加电瞬间就强行将CS的值

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

摘自http://www.cnblogs.com/wang_yb/p/3512095.html <Linux内核设计与实现>读书笔记(十九)- 可移植性 linux内核的移植性非常好, 目前的内核也支持非常多的体系结构(有20多个). 但是刚开始时, linux也只支持 intel i386 架构, 从 v1.2版开始支持 Digital Alpha, Intel x86, MIPS和SPARC(虽然支持的还不是很完善). 从 v2.0版本开始加入了对 Motorala 68K和PowerPC

《Linux内核设计与实现》笔记——内核同步简介

相关概念 竞争条件 多个执行线程(进程/线程/中断处理程序)并发(并行)访问共享资源,因为执行顺序不一样造成结果不一样的情况,称为竞争条件(race condition) 举例说明 #include<thread> using namespace std; int i = 0; void thread1(){ //for(int x=0;x<100000;x++) i++; } void thread2(){ //for(int x=0;x<100000;x++) i++; } i

【读书笔记】《Linux内核设计与实现》内核同步介绍&内核同步方法

简要做个笔记,以备忘. 需同步的原因是,我们并发访问了共享资源.我们将访问或操作共享资源的代码段称"临界区",如果两个执行线程处于同一临界区中同时执行,称"竞争条件".这里术语执行线程指任何正在执行的代码实例,如一个在内核执行的进程.一个中断处理程序或一个内核线程. 举个简单例子,i++操作.该操作可以转换为下面的机器指令序列: 1.得到当前变量i的值,并保存到一个寄存器. 2.将寄存器的值加1. 3.将i的新值写回到内存中. 当两个线程同时进入这个临界区,若i初值