第十八章 调试

调试工作艰难是内核级开发区别于用户级开发的一个显著特点。

18.1 准备开始

我们需要什么?

一个bug

一个藏匿bug的内核版本

思路:假定能够让bug重现

在用户级程序中,bug直截了当。在内核中,bug不是那么清晰。

18.2 内核中的bug

往往要经历一系列的连锁反应才可能触发

需要考虑的问题:定时限制、竞争条件,它们都是运行多个线程在内核中同时运行产生的结果

18.3 通过打印来调试

Printk():内核的格式化打印函数

18.3.1 健壮性

任何时候,任何地方都可以调用printk(),它是一个弹性极佳的函数

漏洞:在系统启动过程中,终端还没有初始化之前,在某些地方不能使用它

Printf()的变体函数:early_printk(),在启动过程的初期就具有在终端上打印的能力,缺点是缺少可移植性。

18.3.2 日志等级

Printk()与printf()不同的地方:可以指定一个日志级别,内核根据这个级别来判断是否在中断上打印消息,内核把级别比某个特定值低的所有消息显示在终端上。

可以使用的记录等级在<linux/kernel.h>定义,扩展开是形如<n>的字符串,加进要打开信息的开头。内核用这种方式指定记录等级和当前终端的记录等级console_loglevel来决定是不是向终端上打印

如果没有指定记录等级,函数会选用默认的DEFAULT_MESSAGE_LOGLEVEL

内核将最重要的记录等级KERN_EMERG记为<0>,将无关紧要的记录等级KERN_DEBUG记为<7>。

18.3.3 记录缓冲区

内核消息保存在一个LOG_BUF_SHIFT大小的环形队列中,缓冲区大小可以通过CONFIG_LOG_BUF_SHIFT进行调整。在单处理器的系统上其默认值为16KB

如果消息队列已经达到最大值,再用printk()调用,新消息将覆盖队列中的老消息

优点:在中断上下文中也可以方便地使用printk(),记录维护起来也更容易

缺点:可能会丢失消息

18.3.4 syslogd和klogd

用户空间的守护进程klogd从记录空间中获取内核消息,再通过syslogd守护进程将它们保存在系统日志文件中,klogd程序既可以从/proc/kmsg文件中,也可以通过syslog()系统调用读取这些消息。

不管用什么方法,klogd都会阻塞,直到有新的内核消息可供读出。被唤醒后,默认情况下,它把消息传给syslogd守护进程。

Syslogd把它接收到的所有信息添加进一个文件中,该文件默认/var/log/messages,也可以通过/etc/syslog.conf配置文件进行重新指定。

在启动klogd的时候,可以通过指定-c标志来改变终端的记录等级

18.3.5 从printf()到printk()的转换

18.4 oops

Oops是内核告知用户有不幸发生的最常用的方式,这个过程包括向终端上输出错误消息,输出寄存器中保存的信息并输出可供跟踪的回溯线索。

发送完oops后,内核会处于一种不稳定状态,内核必须适当地从当前的上下文环境中退出并尝试恢复对系统的控制(通常结果是失败的),混乱的结果是系统死机

Oops中包含的重要信息对于所有体系结构都是完全相同的:寄存器上下文和回溯线索

回溯线索显示了导致错误发生的函数调用链,可以通过偏移量找到导致错误的语句

寄存器上下文可以重建引发问题的现场

18.4.1 ksymoops

回溯线索中的地址需要转化成有意义的符号名称才方便使用,这需要调用ksymoops命令,而且还必须提供编译内核时产生的System.map

调用方式:

Ksymoops saved_oops.txt

吐出解码版的oops

18.4.2 kallsyms

可以通过定义CONFIG_KALLSYMS配置选项启动,该选项存放着内核镜像中相应函数地址的符号名称,内核可以打印解码好的跟踪线索,但是这样会使内核变大一些,因为从函数的地址到符号名称的映射必须永久驻留在内核所映射的内存地址上。

配置选项CONFIG_KALLSYMS_ALL表示不仅存放函数名称,还存放所有符号的名称

CONFIG_KALLSYMS_EXTRA_PASS选项会引起内核构建过程中再次忽略内核的目标代码

18.5 内核调试配置选项

配置选项在内核配置编辑器的内核开发菜单项里,依赖于CONFIG_DEBUG_KERNEL

为了检查各类由原子操作引发的问题,内核提供了原子操作计数器

原子操作:能够不分割执行的东西;在执行时不能中断否则就是完不成的代码,在执行这类操作时,代码不能睡眠——使用锁时睡眠是引发死锁的元凶。正在使用一个自旋锁或禁止抢占的代码进行的就是原子操作

原子操作计数器:一旦在原子操作过程中进程进入睡眠,就打印报告信息并提供追踪线索

18.6 引发bug并打印信息

一些系统调用可以用来方便标记bug,提供断言并输出信息。

最常用的两个是BUG()和BUG_ON()。

当被调用时,它们会引发oops,导致栈的回溯和错误信息的打印。

可以把这些调用当做断言使用

If (bad_thing)

BUG();

BUG_ON(bad_thing);

Panic():调用panic()不但会打印错误消息,还会挂起整个系统

If(terrible_thing)

Panic(“terrible thing is %ld\n,terrible_thing);

Dump_stack():在终端上打印栈的回溯信息来帮助调试,只在终端上打印寄存器和函数的跟踪线索

If(!debug_check){

Printk(KERN_DEBUG “provide some information...\n”);

Dump_stack();

}

18.7 神奇的系统求救键

通过定义CONFIG_MAGIC_SYSRQ配置选项来启动

通过一个sysctl来标记该特性的开或关

Echo 1 > /proc/sys/kernel/sysrq

Sysrq-h:获取一份可用的选项列表

Sysrq-s:将”脏“缓冲区跟硬盘交换分区同步

Sysrq-u:卸载所有的文件系统

Sysrq-b:重启设备

在一行内发送这三个键的组合可以重新启动濒临死亡的系统

实际的实现在drivers/char/sysrq.c中

18.8 内核调试器的传奇

18.8.1 gdb

gdb vmlinux /proc/kcore

Vmlinux文件:未经压缩的内核映像,存放在源代码树的根目录上

/proc/kcore:作为core文件来用的,通过它能访问到内核驻留的高端内存

局限性:没有办法修改内核数据,也不能单步执行内核代码,不能加断点

18.8.2 kgdb

Kgdb是一个补丁,可以在远端主机上通过串口利用gdb的所有功能对内核进行调试

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

18.9 探测系统

18.9.1 用UID作为选择条件

只要保留原有的算法而把新算法加入到其他位置上,基本都能保证安全

可以利用把用户id(UID)作为选择条件来实现这种功能,通过这种选择条件,可以安排到底执行那种算法

18.9.2 使用条件变量

创建一个全局变量作为一个条件选择开关。如果该变量为零,就使用一个分支上的代码。如果不为零,就选择另外一个分支

18.9.3 使用统计量

定义两个全局变量,每当事件发生的时候,就让相应的变量加1

18.9.4 重复频率限制

如果只使用printk(),可以用一个特殊的函数去限制printk()的调用频率

If(error && printk_ratelimit())

Printk(KERN_DEBUG “error=%d\n”,error);

如果频率限制生效,那么printk_ratelimit()返回0,否则返回非0.

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

1.需要一个可靠的可复制的错误

2.需要一个能确保被问题的内核

3.需要一个肯定有问题的内核,从已知最早出现该问题的内核开始

4.在问题内核和良好内核之间使用二分法

18.11 使用Git进行二分搜索

Git源码管理中心提供了一个有用的二分搜索机制,使用Git来控制Linux源码树的副本,那么Git将自动运行二分搜索进程,并在修订版本中进行二分搜索

告诉git要进行二分搜索

$ git bisect start

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

$ git bisect bad <revision>

如果抓到罪魁祸首

$ git bisect bad

为Git提供一个最新的可正常运行的内核版本

$ git bisect good v2.6.28

如果当前版本一切正常

$ git bisect good

如果当前版本有异常

$ git bisect bad

反复执行直到不能进行二分搜索,Git将最终打印出有问题的版本号

如果已经知道了引发bug的源,可以指定git仅仅在与错误相关的目录列表中去二分搜索提交的补丁

$ git bisect start - arch/x86

18.12 当所有的努力都失败时:社区

如果bug是在Linux内核的主流部分中,可以在内核开发社区中寻求其他开发者的帮助,向内核邮件列表发送一份电子邮件

Linux内核分析第十八章读书笔记的更多相关文章

  1. Linux内核分析第四章 读书笔记

    Linux内核分析第四章 读书笔记 第一部分--进程调度 进程调度:操作系统规定下的进程选取模式 面临问题:多任务选择问题 多任务操作系统就是能同时并发地交互执行多个进程的操作系统,在单处理器机器上这 ...

  2. Linux内核分析——第十八章 调试

    第十八章    调试 18.1 准备开始 1.在用户级的程序里,bug表现比较直接:在内核中却不清晰. 2.内核级开发的调试工作远比用户级开发艰难的多. 3.准备工作需要的是: (1)一个bug (2 ...

  3. Linux内核分析第一二章读书笔记

    linux读书笔记(1,2章) 标签(空格分隔): 20135328陈都 第一章 Linux内核简介 Unix的历史 Unix 虽然已经使用了40年,但计算机科学家仍然认为它是现存操作系统中最强大和最 ...

  4. linux内核分析 第18章读书笔记

    十八章 调试 一.内核调试概述 1.需要面对的 一个确定的bug 一个藏匿bug的内核版本 相关的内核代码的知识和运气 2.艰难的调试工作 重现bug很困难:大部分bug通常都不是行为可靠而且定义明确 ...

  5. linux内核分析 第六周读书笔记

    第三章 进程管理 3.1 进程 进程:处于执行期的程序 线程是在进程活动中的对象:内核调度的对象是线程而不是进程,在Linux系统中,并不区分线程和进程 在现代操作系统中, 进程提供两种虚拟机制:虚拟 ...

  6. linux内核分析 第八周读书笔记

    第四章 进程调度 4.1 多任务 1.多任务操作系统就是能同时并发的交互执行多个进程的操作系统. 2.多任务操作系统使多个进程处于堵塞或者睡眠状态,实际不被投入执行,这些任务尽管位于内存,但是并不处于 ...

  7. linux内核分析 第4章读书笔记

    第四章 进程调度 一.抢占与非抢占 1.非抢占式进程调度 进程会一直执行直到自己主动停止运行 2.抢占式进程调度 Linux/Unix使用的是抢占式的方式,强制的挂起进程的动作就叫做抢占. 二.进程优 ...

  8. Linux内核分析第四章读书笔记

    第四章 进程调度 进程调度程序:确保进程能有效工作的一个内核子程序 决定将哪个进程投入运行,何时运行已经运行多长时间 进程调度程序可看做在可运行态进程之间分配有限的处理器时间资源的内核子系统 原则:只 ...

  9. Linux内核分析第三章读书笔记

    第三章 进程管理 3.1 进程 进程就是处于执行期的程序 进程就是正在执行的程序代码的实时结果 线程:在进程中活动的对象.每个线程都拥有一个独立的程序计数器.进程栈和一组进程寄存器. 内核调度的对象是 ...

随机推荐

  1. 学习H5C3

    不一样的老师,不一样风格,刚开始我们都是非常热情,知道这是非常重要的,我需要坚持,加油!!!

  2. 一些安卓模拟器的IP问题和getOutputStream();关于connect();的函数异常的问题

    ip问题 1.不能使用local host 和127.0.0.1作为本地服务器的地址,而网上无论10.0.0.2还是10.0.2.2的8080端口都无法访问.真正的地址应该在CMD 键入ipconfi ...

  3. Alpha冲刺报告(6/12)(麻瓜制造者)

    今日已完成 邓弘立: 看github上的开源库 确定了几个对UI改进有帮助的第三方库 符天愉: 部署了用户修改信息,修改头像的接口,并且完成两个接口的api文档,复习了PHP的无限分类来实现商品的发布 ...

  4. 基于Java反射的map自动装配JavaBean工具类设计

    我们平时在用Myabtis时不是常常需要用map来传递参数,大体是如下的步骤: public List<Role> findRoles(Map<String,Object> p ...

  5. js面对对象编程(二):属性和闭包

    上篇博客中解说了一些js对象的基本概念和使用方法.这篇博客解说一下js属性方面的:公有属性.私有属性,特权方法. 假设学过java.公有属性.私有属性,特权方法(即能够訪问和设置私有属性的方法)一定非 ...

  6. pandas中的series数据类型

    import pandas as pd import numpy as np import names ''' 写在前面的话: 1.series与array类型的不同之处为series有索引,而另一个 ...

  7. .NET批量操作窗口样式

    1. 背景 我们在开发过程中,可能会遇到需要批量控制程序中窗体的大小或其它一些操作, 这些窗体有可能是属于程序本身的,也许是其它程序的窗口.本文就是基于此的一篇关于如何批量操作窗口样式的,我们主要是通 ...

  8. css 字体、文本、padding的样式

    一.字体的样式: 1)字体倾斜:font-style:italic 2)字体大小:font-size 一般为偶数. 3)行高:line-height   当行高为奇数的时候,是文字上面比文字下面的少一 ...

  9. MySQL(六)创建用户与授权

    转载自:MySQL创建用户与授权 目录 一.创建用户 二.授权 三.设置和更改用户密码 四.撤销用户权限 五.删除用户 一.创建用户 命令: CREATE USER 'username'@'host' ...

  10. day39

    今日内容: 1.对于表,库,记录的基本操作 2.数据库引擎的了解 3.表的详细 4.数据类型的掌握 1.回顾昨日对于表,库,记录的基本操作 库 增: create database mydb2; 删: ...