📄 1161.html
字号:
<input type=radio checked value=title name=type>标题
<input type=radio value=content name=type>内容
<input type=image src="images/button_go.gif" tppabs="http://www.linuxhero.com/docs/images/button_go.gif" border=0 name=image2>
</font></div>
</td>
</tr>
<tr>
<td noWrap>
<div align="center">
<input maxlength=100 size=30 name=keyword2>
</div>
</td>
</tr></tbody>
</table>
</form>
</TD>
<TD rowSpan=2><IMG src="images/header_r1_c7.gif" tppabs="http://www.linuxhero.com/docs/images/header_r1_c7.gif" width=26 border=0 name=header_r1_c7></TD>
<TD><IMG height=83 src="images/spacer.gif" tppabs="http://www.linuxhero.com/docs/images/spacer.gif" width=1 border=0></TD></TR>
<TR>
<TD background="images/bgline.gif" tppabs="http://www.linuxhero.com/docs/images/bgline.gif"><IMG height=22
src="images/header_r2_c1.gif" tppabs="http://www.linuxhero.com/docs/images/header_r2_c1.gif" width=296 border=0
name=header_r2_c1></TD>
<TD background="images/bgline.gif" tppabs="http://www.linuxhero.com/docs/images/bgline.gif" colSpan=5>
<DIV align=right><FONT class=normalfont>当前位置:
<A href="index.html" tppabs="http://www.linuxhero.com/docs/index.html">本站首页</A>
<font color="#FF6699">>></font>
<A href="type16.html" tppabs="http://www.linuxhero.com/docs/type16.html">内核技术</A> | <A href="copyright.html" tppabs="http://www.linuxhero.com/docs/copyright.html">版权说明</A></font></DIV>
</TD>
<TD><IMG height=22 src="images/spacer.gif" tppabs="http://www.linuxhero.com/docs/images/spacer.gif" width=1
border=0></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=10 cellPadding=0 width="100%" bgColor=#ffffff
border=0>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=3 width="100%" border=0>
<TR>
<TD vAlign=top align=middle width="60%">
<TABLE cellSpacing=0 cellPadding=0 width="100%"
background="images/back.gif" tppabs="http://www.linuxhero.com/docs/images/back.gif" border=0>
<TBODY>
<TR>
<TD vAlign=top width="80%">
<DIV align=center>
<FORM action="search.html" tppabs="http://www.linuxhero.com/docs/search.html" method=get>
</FORM>
<TABLE cellSpacing=0 cellPadding=0 width="95%"
border=0><TBODY>
<TR>
<TD background="images/bgi.gif" tppabs="http://www.linuxhero.com/docs/images/bgi.gif"
height=30></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=3 width="95%"
align=center border=0>
<TBODY>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=3 width="100%"
border=0>
<TBODY>
<TR>
<TD vAlign=top>
<p><FONT class=normalfont><B><font color=blue>Linux内核调试器内幕</font></B></FONT><BR><FONT class=smallfont color=#ff9900>2004-04-23 15:18 pm</FONT><BR><FONT class=normalfont>作者:Hariprasad Nellitheertha(nharipra@in.ibm.com)<br>来自:Linux知识宝库<br>联系方式:无名<br><br>原文地址:[url]http://www-900.ibm.com/developerWorks/cn/linux/l-kdbug/index.shtml[/url]<br>
内容:<br>
[size=18:6ddc15f4ad]入门<br>
初始化并设置环境变量<br>
激活 KDB<br>
KDB 命令<br>
技巧和诀窍<br>
结束语<br>
参考资料[/size:6ddc15f4ad]<br>
<br>
[size=18:6ddc15f4ad]KDB 入门指南[/size:6ddc15f4ad]<br>
Hariprasad Nellitheertha(nharipra@in.ibm.com)<br>
软件工程师,IBM<br>
2003 年 9 月<br>
调试内核问题时,能够跟踪内核执行情况并查看其内存和数据结构是非常有用的。Linux 中的内置内核调试器 KDB 提供了这种功能。在本文中您将了解如何使用 KDB 所提供的功能,以及如何在 Linux 机器上安装和设置 KDB。您还将熟悉 KDB 中可以使用的命令以及设置和显示选项。<br>
Linux 内核调试器(KDB)允许您调试 Linux 内核。这个恰如其名的工具实质上是内核代码的补丁,它允许高手访问内核内存和数据结构。KDB 的主要优点之一就是它不需要用另一台机器进行调试:您可以调试正在运行的内核。<br>
设置一台用于 KDB 的机器需要花费一些工作,因为需要给内核打补丁并进行重新编译。KDB 的用户应当熟悉 Linux 内核的编译(在一定程度上还要熟悉内核内部机理),但是如果您需要编译内核方面的帮助,请参阅本文结尾处的参考资料一节。<br>
在本文中,我们将从有关下载 KDB 补丁、打补丁、(重新)编译内核以及启动 KDB 方面的信息着手。然后我们将了解 KDB 命令并研究一些较常用的命令。最后,我们将研究一下有关设置和显示选项方面的一些详细信息。<br>
入门<br>
KDB 项目是由 Silicon Graphics 维护的(请参阅参考资料以获取链接),您需要从它的 FTP 站点下载与内核版本有关的补丁。(在编写本文时)可用的最新 KDB 版本是 4.2。您将需要下载并应用两个补丁。一个是“公共的”补丁,包含了对通用内核代码的更改,另一个是特定于体系结构的补丁。补丁可作为 bz2 文件获取。例如,在运行 2.4.20 内核的 x86 机器上,您会需要 kdb-v4.2-2.4.20- common-1.bz2 和 kdb-v4.2-2.4.20-i386-1.bz2。<br>
这里所提供的所有示例都是针对 i386 体系结构和 2.4.20 内核的。您将需要根据您的机器和内核版本进行适当的更改。您还需要拥有 root 许可权以执行这些操作。<br>
将文件复制到 /usr/src/linux 目录中并从用 bzip2 压缩的文件解压缩补丁文件:<br>
[code:1:6ddc15f4ad]#bzip2 -d kdb-v4.2-2.4.20-common-1.bz2<br>
<br>
#bzip2 -d kdb-v4.2-2.4.20-i386-1.bz2 [/code:1:6ddc15f4ad]<br>
您将获得 kdb-v4.2-2.4.20-common-1 和 kdb-v4.2-2.4-i386-1 文件。<br>
现在,应用这些补丁:<br>
[code:1:6ddc15f4ad]#patch -p1 <kdb-v4.2-2.4.20-common-1<br>
<br>
#patch -p1 <kdb-v4.2-2.4.20-i386-1 [/code:1:6ddc15f4ad]<br>
这些补丁应该干净利落地加以应用。查找任何以 .rej 结尾的文件。这个扩展名表明这些是失败的补丁。如果内核树没问题,那么补丁的应用就不会有任何问题。<br>
接下来,需要构建内核以支持 KDB。第一步是设置 CONFIG_KDB 选项。使用您喜欢的配置机制(xconfig 和 menuconfig 等)来完成这一步。转到结尾处的“Kernel hacking”部分并选择“Built- in Kernel Debugger support”选项。<br>
您还可以根据自己的偏好选择其它两个选项。选择“Compile the kernel with frame pointers”选项(如果有的话)则设置 CONFIG_FRAME_POINTER 标志。这将产生更好的堆栈回溯,因为帧指针寄存器被用作帧指针而不是通用寄存器。您还可以选择“KDB off by default”选项。这将设置 CONFIG_KDB_OFF 标志,并且在缺省情况下将关闭 KDB。我们将在后面一节中对此进行详细介绍。<br>
保存配置,然后退出。重新编译内核。建议在构建内核之前执行“make clean”。用常用方式安装内核并引导它。<br>
[size=18:6ddc15f4ad]初始化并设置环境变量[/size:6ddc15f4ad]<br>
您可以定义将在 KDB 初始化期间执行的 KDB 命令。需要在纯文本文件 kdb_cmds 中定义这些命令,该文件位于 Linux 源代码树(当然是在打了补丁之后)的 KDB 目录中。该文件还可以用来定义设置显示和打印选项的环境变量。文件开头的注释提供了编辑文件方面的帮助。使用这个文件的缺点是,在您更改了文件之后需要重新构建并重新安装内核。<br>
[size=18:6ddc15f4ad]激活 KDB[/size:6ddc15f4ad]<br>
如果编译期间没有选中 CONFIG_KDB_OFF,那么在缺省情况下 KDB 是活动的。否则,您需要显式地激活它 - 通过在引导期间将 kdb=on 标志传递给内核或者通过在挂装了 /proc 之后执行该工作:<br>
[code:1:6ddc15f4ad]#echo "1" >/proc/sys/kernel/kdb [/code:1:6ddc15f4ad]<br>
倒过来执行上述步骤则会取消激活 KDB。也就是说,如果缺省情况下 KDB 是打开的,那么将 kdb=off 标志传递给内核或者执行下面这个操作将会取消激活 KDB:<br>
[code:1:6ddc15f4ad]#echo "0" >/proc/sys/kernel/kdb[/code:1:6ddc15f4ad] <br>
在引导期间还可以将另一个标志传递给内核。kdb=early 标志将导致在引导过程的初始阶段就把控制权传递给 KDB。如果您需要在引导过程初始阶段进行调试,那么这将有所帮助。<br>
调用 KDB 的方式有很多。如果 KDB 处于打开状态,那么只要内核中有紧急情况就自动调用它。按下键盘上的 PAUSE 键将手工调用 KDB。调用 KDB 的另一种方式是通过串行控制台。当然,要做到这一点,需要设置串行控制台(请参阅参考资料以获取这方面的帮助)并且需要一个从串行控制台进行读取的程序。按键序列 Ctrl-A 将从串行控制台调用 KDB。<br>
[size=18:6ddc15f4ad]KDB 命令[/size:6ddc15f4ad]<br>
KDB 是一个功能非常强大的工具,它允许进行几个操作,比如内存和寄存器修改、应用断点和堆栈跟踪。根据这些,可以将 KDB 命令分成几个类别。下面是有关每一类中最常用命令的详细信息。<br>
[size=18:6ddc15f4ad]内存显示和修改[/size:6ddc15f4ad]<br>
这一类别中最常用的命令是 md、mdr、mm 和 mmW。<br>
[size=18:6ddc15f4ad]md[/size:6ddc15f4ad] 命令以一个地址/符号和行计数为参数,显示从该地址开始的 line-count 行的内存。如果没有指定 line-count,那么就使用环境变量所指定的缺省值。如果没有指定地址,那么 md 就从上一次打印的地址继续。地址打印在开头,字符转换打印在结尾。<br>
[size=18:6ddc15f4ad]mdr[/size:6ddc15f4ad] 命令带有地址/符号以及字节计数,显示从指定的地址开始的 byte-count 字节数的初始内存内容。它本质上和 md 一样,但是它不显示起始地址并且不在结尾显示字符转换。mdr 命令较少使用。<br>
[size=18:6ddc15f4ad]mm[/size:6ddc15f4ad] 命令修改内存内容。它以地址/符号和新内容作为参数,用 new-contents 替换地址处的内容。<br>
[size=18:6ddc15f4ad]mmW[/size:6ddc15f4ad] 命令更改从地址开始的 W 个字节。请注意,mm 更改一个机器字。<br>
[size=18:6ddc15f4ad]示例[/size:6ddc15f4ad]<br>
[code:1:6ddc15f4ad]显示从 0xc000000 开始的 15 行内存:<br>
[0]kdb> md 0xc000000 15 <br>
将内存位置为 0xc000000 上的内容更改为 0x10:<br>
[0]kdb> mm 0xc000000 0x10 [/code:1:6ddc15f4ad]<br>
[size=18:6ddc15f4ad]寄存器显示和修改[/size:6ddc15f4ad]<br>
这一类别中的命令有 rd、rm 和 ef。<br>
[size=18:6ddc15f4ad]rd[/size:6ddc15f4ad] 命令(不带任何参数)显示处理器寄存器的内容。它可以有选择地带三个参数。如果传递了 c 参数,则 rd 显示处理器的控制寄存器;如果带有 d 参数,那么它就显示调试寄存器;如果带有 u 参数,则显示上一次进入内核的当前任务的寄存器组。<br>
[size=18:6ddc15f4ad]rm[/size:6ddc15f4ad] 命令修改寄存器的内容。它以寄存器名称和 new- contents 作为参数,用 new-contents 修改寄存器。寄存器名称与特定的体系结构有关。目前,不能修改控制寄存器。<br>
[size=18:6ddc15f4ad]ef[/size:6ddc15f4ad] 命令以一个地址作为参数,它显示指定地址处的异常帧。<br>
[size=18:6ddc15f4ad]示例[/size:6ddc15f4ad]<br>
显示通用寄存器组:<br>
[code:1:6ddc15f4ad][0]kdb> rd <br>
将寄存器 ebx 的内容设置成 0x25:<br>
[0]kdb> rm %ebx 0x25 [/code:1:6ddc15f4ad]<br>
[size=18:6ddc15f4ad]断点[/size:6ddc15f4ad]<br>
常用的断点命令有 bp、bc、bd、be 和 bl。<br>
[size=18:6ddc15f4ad]bp[/size:6ddc15f4ad] 命令以一个地址/符号作为参数,它在地址处应用断点。当遇到该断点时则停止执行并将控制权交予 KDB。该命令有几个有用的变体。[size=18:6ddc15f4ad]bpa[/size: 6ddc15f4ad] 命令对 SMP 系统中的所有处理器应用断点。bph 命令强制在支持硬件寄存器的系统上使用它。bpha 命令类似于 bpa 命令,差别在于它强制使用硬件寄存器。<br>
[size=18:6ddc15f4ad]bd[/size:6ddc15f4ad] 命令禁用特殊断点。它接收断点号作为参数。该命令不是从断点表中除去断点,而只是禁用它。断点号从 0 开始,根据可用性顺序分配给断点。<br>
[size=18:6ddc15f4ad]be[/size:6ddc15f4ad] 命令启用断点。该命令的参数也是断点号。<br>
[size=18:6ddc15f4ad]bl [/size:6ddc15f4ad]命令列出当前的断点集。它包含了启用的和禁用的断点。<br>
[size=18:6ddc15f4ad]bc[/size:6ddc15f4ad] 命令从断点表中除去断点。它以具体的断点号或 * 作为参数,在后一种情况下它将除去所有断点。<br>
[size=18:6ddc15f4ad]示例[/size:6ddc15f4ad]<br>
[code:1:6ddc15f4ad]对函数 sys_write() 设置断点:<br>
[0]kdb> bp sys_write <br>
列出断点表中的所有断点:<br>
[0]kdb> bl <br>
清除断点号 1:<br>
[0]kdb> bc 1[/code:1:6ddc15f4ad]<br>
[size=18:6ddc15f4ad]堆栈跟踪[/size:6ddc15f4ad]<br>
主要的堆栈跟踪命令有 bt、btp、btc 和 bta。<br>
[size=18:6ddc15f4ad]bt [/size:6ddc15f4ad]命令设法提供有关当前线程的堆栈的信息。它可以有选择地将堆栈帧地址作为参数。如果没有提供地址,那么它采用当前寄存器来回溯堆栈。否则,它假定所提供的地址是有效的堆栈帧起始地址并设法进行回溯。如果内核编译期间设置了 CONFIG_FRAME_POINTER 选项,那么就用帧指针寄存器来维护堆栈,从而就可以正确地执行堆栈回溯。如果没有设置 CONFIG_FRAME_POINTER,那么 bt 命令可能会产生错误的结果。<br>
[size=18:6ddc15f4ad]btp[/size:6ddc15f4ad] 命令将进程标识作为参数,并对这个特定进程进行堆栈回溯。<br>
[size=18:6ddc15f4ad]btc[/size:6ddc15f4ad] 命令对每个活动 CPU 上正在运行的进程执行堆栈回溯。它从第一个活动 CPU 开始执行 bt,然后切换到下一个活动 CPU,以此类推。<br>
[size=18:6ddc15f4ad]bta[/size:6ddc15f4ad] 命令对处于某种特定状态的所有进程执行回溯。若不带任何参数,它就对所有进程执行回溯。可以有选择地将各种参数传递给该命令。将根据参数处理处于特定状态的进程。选项以及相应的状态如下:<br>
?D:不可中断状态 <br>
?R:正运行 <br>
?S:可中断休眠 <br>
?T:已跟踪或已停止 <br>
?Z:僵死 <br>
?U:不可运行<br>
这类命令中的每一个都会打印出一大堆信息。请查阅下面的参考资料以获取这些字段的详细文档。<br>
[size=18:6ddc15f4ad]示例[/size:6ddc15f4ad]<br>
[code:1:6ddc15f4ad]跟踪当前活动线程的堆栈:<br>
[0]kdb> bt <br>
跟踪标识为 575 的进程的堆栈:<br>
[0]kdb> btp 575 [/code:1:6ddc15f4ad]<br>
[size=18:6ddc15f4ad]其它命令[/size:6ddc15f4ad]<br>
下面是在内核调试过程中非常有用的其它几个 KDB 命令。<br>
[size=18:6ddc15f4ad]id [/size:6ddc15f4ad]命令以一个地址/符号作为参数,它对从该地址开始的指令进行反汇编。环境变量 IDCOUNT 确定要显示多少行输出。<br>
[size=18:6ddc15f4ad]ss [/size:6ddc15f4ad]命令单步执行指令然后将控制返回给 KDB。该指令的一个变体是 ssb,它执行从当前指令指针地址开始的指令(在屏幕上打印指令),直到它遇到将引起分支转移的指令为止。分支转移指令的典型示例有 call、 return 和 jump。<br>
[size=18:6ddc15f4ad]go[/size:6ddc15f4ad] 命令让系统继续正常执行。一直执行到遇到断点为止(如果已应用了一个断点的话)。<br>
[size=18:6ddc15f4ad]reboot [/size:6ddc15f4ad]命令立刻重新引导系统。它并没有彻底关闭系统,因此结果是不可预测的。<br>
[size=18:6ddc15f4ad]ll[/size:6ddc15f4ad] 命令以地址、偏移量和另一个 KDB 命令作为参数。它对链表中的每个元素反复执行作为参数的这个命令。所执行的命令以列表中当前元素的地址作为参数。<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -