= 0xc013db4c (sys_read)
同样,
同样,表明 sys_write 位于地址 0xc013dcc8:
[0]kdb> sys_write
sys_write = 0xc013dcc8 (sys_write)
这些有助于在分析堆栈时找到全局数据和函数地址。
技巧 #2
在编译带 KDB 的内核时,只要 CONFIG_FRAME_POINTER 选项出现就使用该选项。为此,需要在配置内核时选择“Kernel hacking”部分下面的“Compile the kernel with frame pointers”选项。这确保了帧指针寄存器将被用作帧指针,从而产生正确的回溯。实际上,您可以手工转储帧指针寄存器的内容并跟踪整个堆栈。例如,在 i386 机器上,%ebp 寄存器可以用来回溯整个堆栈。
例如,在函数 rmqueue() 上执行第一个指令后,堆栈看上去类似于下面这样:
[0]kdb> md %ebp
0xc74c9f38 c74c9f60 c0136c40 000001f0 00000000
0xc74c9f48 08053328 c0425238 c04253a8 00000000
0xc74c9f58 000001f0 00000246 c74c9f6c c0136a25
0xc74c9f68 c74c8000 c74c9f74 c0136d6d c74c9fbc
0xc74c9f78 c014fe45 c74c8000 00000000 08053328
[0]kdb> 0xc0136c40
0xc0136c40 = 0xc0136c40 (__alloc_pages +0x44)
[0]kdb> 0xc0136a25
0xc0136a25 = 0xc0136a25 (_alloc_pages +0x19)
[0]kdb> 0xc0136d6d
0xc0136d6d = 0xc0136d6d (__get_free_pages +0xd)
我们可以看到 rmqueue() 被 __alloc_pages 调用,后者接下来又被 _alloc_pages 调用,以此类推。
每一帧的第一个双字(double word)指向下一帧,这后面紧跟着调用函数的地址。因此,跟踪堆栈就变成一件轻松的工作了。
技巧 #3
go 命令可以有选择地以一个地址作为参数。如果您想在某个特定地址处继续执行,则可以提供该地址作为参数。另一个办法是使用 rm 命令修改指令指针寄存器,然后只要输入 go。如果您想跳过似乎会引起问题的某个特定指令或一组指令,这就会很有用。但是,请注意,该指令使用不慎会造成严重的问题,系统可能会严重崩溃。
技巧 #4
您可以利用一个名为 defcmd 的有用命令来定义自己的命令集。例如,每当遇到断点时,您可能希望能同时检查某个特殊变量、检查某些寄存器的内容并转储堆栈。通常,您必须要输入一系列命令,以便能同时执行所有这些工作。defcmd 允许您定义自己的命令,该命令可以包含一个或多个预定义的 KDB 命令。然后只需要用一个命令就可以完成所有这三项工作。其语法如下:
[0]kdb> defcmd name "usage" "help"
[0]kdb> [defcmd] type the commands here
[0]kdb> [defcmd] endefcmd
例如,可以定义一个(简单的)新命令 hari,它显示从地址 0xc000000 开始的一行内存、显示寄存器的内容并转储堆栈:
[0]kdb> defcmd hari "" "no arguments needed"
[0]kdb> [defcmd] md 0xc000000 1
[0]kdb> [defcmd] rd
[0]kdb> [defcmd] md %ebp 1
[0]kdb> [defcmd] endefcmd
该命令的输出会是:
[0]kdb> hari
[hari]kdb> md 0xc000000 1
0xc000000 00000001 f000e816 f000e2c3 f000e816
[hari]kdb> rd
eax = 0x00000000 ebx = 0xc0105330 ecx = 0xc0466000 edx = 0xc0466000
....
...
[hari]kdb> md %ebp 1
0xc0467fbc c0467fd0 c01053d2 00000002 000a0200
[0]kdb>
技巧 #5
可以使用 bph 和 bpha 命令(假如体系结构支持使用硬件寄存器)来应用读写断点。这意味着每当从某个特定地址读取数据或将数据写入该地址时,我们都可以对此进行控制。当调试数据/内存毁坏问题时这可能会极其方便,在这种情况中您可以用它来识别毁坏的代码/进程。
示例
每当将四个字节写入地址 0xc0204060 时就进入内核调试器:
[0]kdb> bph 0xc0204060 dataw 4
在读取从 0xc000000 开始的至少两个字节的数据时进入内核调试器:
[0]kdb> bph 0xc000000 datar 2
结束语
对于执行内核调试,KDB 是一个方便的且功能强大的工具。它提供了各种选项,并且使我们能够分析内存内容和数据结构。最妙的是,它不需要用另一台机器来执行调试。
参考资料
* 请在 Documentation/kdb 目录中查找 KDB 手册页。
* 有关设置串行控制台的信息,请查找 Documentation 目录中的 serial-console.txt。
* 请在 SGI 的内核调试器项目网站上下载 KDB。
* 有关几个基于方案的 Linux 调试技术的概述,请阅读“掌握 Linux 调试技术”(developerWorks,2002 年 8 月)。
* 教程“编译 Linux 内核”(developerWorks,2000 年 8 月)让您完整地了解配置、编译和安装内核的过程。
* IBM AIX 用户可以在 KDB Kernel Debugger and Command 页面上获取有关用于 AIX 的 KDB 的使用帮助。
* 那些寻求有关调试 OS/2 信息的读者应该阅读 IBM 红皮书 The OS/2 Debugging Handbook(共四卷)的第 II 卷。
* 在 developerWorks Linux 专区中查找更多针对 Linux 开发人员的参考资料。
关于作者
Hariprasad Nellitheertha 在印度班加罗尔(Bangalore)的 IBM Linux 技术中心工作。他目前正在 Linux Change Team 从事修正内核和其它 Linux 错误的工作。Hari 研究过 OS/2 内核和文件系统。他的兴趣包括 Linux 内核内部机理、文件系统和自主计算。可以通过 nharipra@in.ibm.com 与 Hari 联系。








