- 论坛徽章:
- 0
|
安装调试支持
通过分析文件系统源代码是学习文件系统是如何工作的一种方法。然而,按照这种方法要真正理解对于特定的操作,内核与文件系统是如何交互的却是相当困难的。想必没有比安装并使用内核调试器更好的方法了,通过内核调试器你可以在特定的函数处停下来,查看其栈的回朔、函数参数以及打印一些其它有用的信息。
事实上对于文件系统或内核的其它子系统部分,有3种主要的方法来进行调试。第一种方法在内核中使用printk()函数,它类似于用户态的printf()函数。第二种方法是使用独立调试器如kdb,这样我们就可以通过显式设置断点来停在相应位置或通过按下特殊的组合键进入调试器。第三种方法要使用到两台机器,这两台机器通过串口线相连,通过它就可以使用gdb进行源码级的调试。
后续部分我们将讲述这3种方法,用这3种方法来调试的工作量也是大为不同的,其中printk()是最简单的一种方式,而gdb方式则需要花费更多的时间来建立,同时还需要另外一台机器。对于想进行试验的读者而言,如果上面推荐的资源都有的话,你首先就应该从printk()开始,然后是kdb,最后gdb。
下面部分假设你已经对调试的相关概念比较熟悉。
Printk方式调试
Printf()方式是最原始、也最简单的调试方式之一。通过在我们的程序中插入printf语句,就很容易显示运行程序的信息。这也易于开发或很简单地就可以跟踪程序的运行流程。
Linux提供了printk()函数用于内核/模块编写者来使用。除名称不同以外,它和printf()函数的使用方式相同。在编写uxfs文件系统时,我们可以使用的一种方式是在文件系统的每一个入口点处都可以加上printk()函数。当在命令提示符之后敲入不同的命令时,很容易看到文件系统中哪些函数被调用了。
由于linux支持可加载模块,并且重新编译和加载模块只用几秒钟的时间,因此printk()方式是观察文件系统实际上是如何工作最简单的一种方式。另外,对于刚接触linux内核开发,并且想要理解内核是如何工作的新手来说,这种方法一开始就应该采用。为很好地理解文件系统相关的内核函数是如何工作的,printk()调用可以置于整个内核当中,并且显示不同的结构体。
使用SGI kdb调试器
Kdb是一内置调试器,为使用它必须和内核一块编译。它可以用于设置断点、显示内存、反汇编指令以及显示机器配置信息如寄存器等。它主要是操作内核符号表,因此内核函数和结构体都可以通过名称来进行访问。
Kdb是由SGI((Silicon Graphics Inc)的工程师们开发维护的,其源代码可以从SGI网站上下载。Kdb的主页如下:
http://oss.sgi.com/projects/kdb/
值得注意的是,按照链接到下载处时,显示的目录是针对kdb的版本,而不是linux内核的版本。针对我们开发uxfs的内核(2.4.18),必须使用kdb版本2.1(后续的版本并不支持本内核)。
目录下面的README文件说明了哪些文件需要下载。在下载之前应该认真阅读README文件。注意针对同一个内核可能有多个kdb版本。README文件解释了如何来识别针对不同版本号的补丁。
有两个补丁文件需要下载,第一个文件对所有不同的机器架构都是通用的,另外一个则是具体针对你所运行的机器体系架构的。下载完成后,如下打补丁:
# cd /usr/src/linux-2.4.18
# patch -p1 2.4.18-common-3
patching file
kernel/sysctl.c
patching file
kernel/ksyms.c
patching file
kernel/Makefile
patching file
init/main.c
...
patching file
Documentation/kdb/kdb_env.man
patching file
Documentation/kdb/kdb.mm
336 UNIX Filesystems—Evolution, Design, and Implementation
patching file
Documentation/kdb/kdb_bp.man
patching file
Documentation/kdb/slides
# patch -p2 2.4.18-i386-1
patching file
include/asm-i386/hw_irq.h
patching file
include/asm-i386/keyboard.h
patching file
include/asm-i386/ptrace.h
patching file
arch/i386/vmlinux.lds
...
patching file
arch/i386/kdb/kdbasupport.c
patching file
arch/i386/kdb/ansidecl.h
patching file
arch/i386/kdb/bfd.h
patching file
arch/i386/kdb/ChangeLog
#
一旦补丁成功地打上了,内核的配置也必须作相应的改变以支持kdb。在标识为Kernel
hacking部分,选择Built-in
Kernel Debugger support和KDB modules。之后内核必须再次编译(make dep; make bzImage),并且如前面配置内核部分所描述的那样安装新内核。
与kdb补丁一块还有相关文档,介绍了调试器是如何工作的以及可以使用的命令。通过按下BREAK键就可以进入调试器。Kdb提示符如下所示:
Entering kdb
(current=0xc03b0000,pid 0)on processor 0 due to Keyboard Entry
[0]kdb>
?命令可以用于显示所有kdb命令。下面是经常使用到的一些命令的总括,至于实际上它们又是如何使用的在本章的后续部分有示例讲解。
bp. 设置或显示一个断点
bph. 设置一个硬件断点
bc. 清除一个断点
bl. 列出所有当前断点
bt. 显示当前进程的堆栈跟踪情况
go. 退出调试器并重启内核运行
id. 反汇编指令
md. 显示指定地址内容
mds. 以符号形式显示内存
mm. 修改内存
reboot. 立即重启机器
rd. 显示寄存器内容
ss. 单步执行(一次一条指令)
ssb. 单步执行CPU直到到达一分支
kdb(8)man手册描述了其它命令用法。
使用gdb进行源码级调试
GNU gdb调试器在很多年以前就有了,它主要用于调试用户级程序。然而,通过一个串口线将两台机器以主机/目标机配置方式连接起来,gdb也可以用于调试linux内核。这种方式需要为内核打补丁来包含kgdb驱动,通过它主机上gdb才可以通信。尽管这种方式需要一台额外的机器以及其它的一些构建工作,然而源码级调试将使得内核调试更为简单,所以也是值得的。同时也更容易观察内核的流程,不仅仅是因为我们能够添加断点,而且在运行到断点处的函数参数信息都可以和源代码一起打印出来。
内核级kbd调试有多个补丁,下面网址是kgdb的主页。
http://kgdb.sourceforge.net/
上面包括了所有的补丁以及介绍了gdb的安装。下面部分将描述其中的一些主要知识点,对于具体的细节,请参考kgdb主页。
连接主机与目标机
Gdb调试的第一步就是将两台机器连接起来,并且检验数据是否可以在连接上传输。两台机器必须通过空调制串口线将两台机器的串口连接起来,如图14.2所示。
![]()
图14.2 gdb进行源码级内核调试
串口支持传输率从110波特率到115200波特率,其中缺省值为9600。缺省值对于简单的调试通常就足够了,虽然更高传输波特率对于在连接上要传输大量信息更为有利。当要显示多线程堆栈时就属于传输大量信息的情形。
一旦连接完成,每台机器上的串口速率必须相同。我们在两台机器上可以如下来检验:
# stty
speed 9600 baud; line = 0;
min = 0; time = 10;
-brkint -icrnl
-imaxbel
-opost -onlcr
-isig -icanon
-iexten -echo -echoe -echok -echoctl -echoke
上面波特率是9600,如果两台机器中的传输速率不相同,可以调用stty命令来进行设置:
# stty ispeed 9600 ospeed 9600
假设两台机器上的波特率已经相同,并且连接也建立好了,我们可以简单地通过连线回显一个字符串到另一端,并且另一端进行读,如下所示:
主机 目标机
# cat /dev/ttyS0
# echo hello > /dev/ttyS0
hello
如果还遇到其它问题,请参考kgdb内核网站指南。
下载kgdb补丁
Kgdb内核网站的dowload部分包括有特定linux内核的内核补丁,每一个补丁都是包括一系列diffs的ASCII文件。下载完后,按如下方式给linux内核打上kgdb补丁:
# cd /usr/src/linux
# patch -p1 2.4.18-kgdb-1.5.patch
patching file
Documentation/Configure.help
patching file
Documentation/i386/gdb-serial.txt
patching file
Makefile
patching file
arch/i386/Makefile
patching file
arch/i386/config.in
patching file arch/i386/kernel/Makefile
...
patching file
kernel/ksyms.c
patching file
kernel/sched.c
当补丁打好后,内核配置必须更新以包括kgdb选项。在Kernel
Debugging部分,选择如下行:
KGDB: Remote
(serial) kernel debugging with gdb (NEW)
然后选择kgdb的其它子选项。注意Verbose BUG() reporting不需要选择。
保存好内核配置后,运行如下命令:
# make dep
# make clean
# make bzImage
来构建新内核。正如前面部分所描述的,新内核将位于arch/i386/boot目录下。
安装kgdb修改内核
为安装新内核,lilo.conf中的条目必须修改以指定内核引导时必须等待来自主机上的gdb连接。下面显示的是lilo.conf中针对新内核的条目:
image=/boot/linux.gdb
label=linux.gdb
initrd=/boot/initrd-2.4.18-3.img
read-only
root=/dev/hda2
append="gdb
gdbttyS=0 gdbbaud=9600"
它指定了kgdb stub使用哪一个串口(/dev/ttyS0)以及波特率的设置。
当新内核引导时,将显示如下信息:
Waiting for
connection from remote gdb...
为连接目标机,主机上必须运行gdb,如下命令所示:
# gdb
GNU gdb Red Hat
Linux (5.1.90CVS-5)
Copyright 2002 Free
Software Foundation, Inc.
GDB is free
software, covered by the GNU General Public License, and you
are welcome to
change it and/or distribute copies of it under certain
conditions.
Type "show
copying" to see the conditions.
There is absolutely
no warranty for GDB. Type "show warranty" for
details.
This GDB was
configured as "i386-redhat-linux".
(gdb) target remote /dev/ttyS0
Remote debugging
using /dev/ttyS0
0xc011323d in ?? ()
(gdb) c
Continuing.
PCI: PCI BIOS
revision 2.10 entry at 0xfbfee, last bus=1
PCI: Using
configuration type 1
...
“target remote”命令指定和新内核通信的串口号,C命令则继续运行。
为中断进入调试器并且指定在何处访问符号调试信息,按Control + C
组合键,如下所示:
Program received
signal SIGTRAP, Trace/breakpoint trap.
0xc011323d in ?? ()
(gdb) symbol-file /usr/src/linux/vmlinux
Reading symbols from /usr/src/linux/vmlinux...done.
到这调试器已经有足够的信息来调试内核。
Gdb与模块交互
因为uxfs是一个可加载模块,gdb并不清楚其在内存中的位置或到哪去定位该模块的符号调试信息。
Kgdb网站上的loadmodule脚本可用于加载模块,前提是模块的源代码及二进制码都已经存在于主机上,并且可以从主机rcp到目标机上。
在运行loadmodule之前,必须改变在该脚本的顶部定义的GDBSCRIPTS变量,使其指向一个可安装gdb脚本的目录。例如:
GDBSCRIPTS=/home/spate/uxfs/tools/gdbscripts
然后脚本可以运行如下:
# loadmodule target-machine ../kern/uxfs
Copying ../kern/uxfs
to linux
Loading module
../kern/uxfs
Generating script
/home/spate/uxfs/tools/gdbscripts/loadlinuxuxfs
完成之后,模块将加载到目标机上,并且会显示产生的脚本。该脚本可以从gdb中运行。Ctrl + C可以进入gdb,紧接着我们就可以运行该脚本如下:
Program received
signal SIGTRAP, Trace/breakpoint trap.
breakpoint () at
gdbstub.c:1177
1177 }
(gdb) so
/home/spate/uxfs/tools/gdbscripts/loadlinuxuxfs
add symbol table
from file "/home/spate/uxfs/kern/uxfs" at
.text_addr =
0xd0854060
.rodata_addr = 0xd0855c60
__ksymtab_addr = 0xd085618c
__archdata_addr =
0xd08562b0
__kallsyms_addr =
0xd08562b0
.data_addr = 0xd08568c0
.bss_addr = 0xd0856a60
Gdb的安装到现在已经完成。任何时候需要进入调试器并且添加断点等,Control-C都可以被调用。用gdb进行内核级调试在本章后续部分都将使用到。
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/27708/showart_400505.html |
|