四。参考资料
一。使用kdb调试内核
kdb全称为Built-in Kernel Debugger,kdb的主要特点是调试器本身是内核的一部分,不需要另外的调试工具如gdb,就可以跟踪和调试内核。
kdb作为sgi的一个open source项目,大家可以访问http://oss.sgi.com/projects/kdb/了解更多信息。
1。kdb的获取
kdb可以从ftp://oss.sgi.com/www/projects/kdb/download/获得。一般kdb由两部分构成:平台相关部分和通用部分。kdb的命名规则是:kdb+版本号+内核版本号+支持平台或通用部分+更新子版本,bz2,选择kdb时需要考虑针对的内核版本、kdb版本和支持的平台,更新子版本一般越高越好。
以针对Linux内核版本2.6.22为例,目前我们需要下载的文件就是kdb-v4.4-2.6.22-i386-2.bz2和kdb-v4.4-2.6.22-common-4.bz2 。
2。kdb的安装
首先选择对应的内核版本,比如linux-2.6.22.tar.gz
tar vzxf linux-2.6.22.tar.gz
bunzip2 kdb-v4.4-2.6.22-i386-2.bz2
bunzip2 kdb-v4.4-2.6.22-common-4.bz2
然后为内核打补丁
cd linux-2.6.22
patch -p1 < ../kdb-v4.4-2.6.22-i386-2
patch -p1 < ../kdb-v4.4-2.6.22-common-4
接下来就是配置内核,主要是设置CONFIG_KDB,如下图所示:
保存配置文件后,就可以编译内核了。
make;make modules_install;make install
然后重启系统,选择刚编译的内核重新进入系统。 如果象下图一样在启动时看到kdb_cmd信息,就可以确认运行的内核是包含了kdb调试器的了。
3。kdb的使用
3.1激活kdb
可以通过cat /proc/sys/kernel/kdb查看kdb是否已经激活,通过echo 0 > /proc/sys/kernel/kdb关闭kdb。
3.2唤出kdb和返回操作系统
通过连按两下Break键唤出kdb命令调试模式,通过命令go回到操作系统。
3.3kdb命令介绍
3.4kdb使用实例
3.4.1查询函数或变量内存地址
查询函数netif_receive_skb的线性地址:
kdb> mds netif_receive_skb
0xc02db540 0001bd55
获得函数netif_receive_skb的线性地址为:0xc02db540
查询变量dev_base_head的线性地址:
kdb> mds dev_base_head
0xc0424a2c c7e6e830
获得变量dev_base_head的线性地址为:0xc0424a2c
3.4.2查询指定内存地址内容
kdb> md 0xc0424a2c
0xc0424a2c c7e6e830
3.4.3设置、清除、禁止和启用断点
设置断点
kdb> bp netif_receive_skb
清除断点
kdb> bc 0
禁止断点
kdb> bd 0
启用断点
kdb> be 0
3.4.4调用跟踪
kdb> bt
3.4.5修改内存数据
kdb> mm cf26ac0c 0x50
0xcf26ac0c 0x50
4。kdb的局限
kdb类似Windows平台上的softice,功能强大,但是基本是汇编级调试,对于一般使用者掌握比较困难,灵活性也比较欠缺。
二。使用kgdb调试内核
kgdb能很方便的在源码级对内核进行调试,使用的语法就是gdb的命令。缺点是kgdb只能进行远程调试,一般它需要一根串口线及两台机器来调试内核,这类似于微软的WinDbg,但目前也可以通过在同一台主机上用vmware软件运行两个操作系统的方式来进行调试。kgdb实质上是通过和gdb进行通讯来完成调试和跟踪的。
kgdb原本也是sgi的open source项目,原先的站点是在http://oss.sgi.com/projects/kgdb/,现在似乎已经独立出去,大家可以访问http://kgdb.linsyssoft.com/了解更多信息。
1。kgdb的获取
kgdb的free版本更新目前看似乎有点滞后(可能主要都发展收费的kgdb pro去了),现在支持Linux 2.6最后版本是2.6.15.5,可以从http://kgdb.linsyssoft.com/downloads/kgdb-2/linux-2.6.15.5-kgdb-2.4.tar.bz2下载获得。
2。kgdb的安装和准备
首先选择对应的内核版本,这里是2.6.15.5。
tar vzxf linux-2.6.15.5.tar.gz
tar vjxf linux-2.6.15.5-kgdb-2.4.tar.bz2
根据linux-2.6.15.5-kgdb-2.4里面README的描述,给内核打上如下补丁:
patch -p1 < ../linux-2.6.15.5-kgdb-2.4/core-lite.patch
patch -p1 < ../linux-2.6.15.5-kgdb-2.4/i386-lite.patch
patch -p1 < ../linux-2.6.15.5-kgdb-2.4/8250.patch
patch -p1 < ../linux-2.6.15.5-kgdb-2.4/eth.patch
patch -p1 < ../linux-2.6.15.5-kgdb-2.4/i386.patch
patch -p1 < ../linux-2.6.15.5-kgdb-2.4/core.patch
然后是配置内核,主要是选择CONFIG_KGDB、CONFIG_KGDB_CONSOLE和CONFIG_KGDB_8250,如下图所示:
这里要注意的是,kgdb可以通过串口和调试系统的gdb通讯,也可以通过以太网发送UDP包和调试系统的gdb通讯。
另外,和kgdb通讯的gdb也必须使用特殊修改过的gdb,在这里使用的是gdbmod-2.4,下载地址为http://kgdb.linsyssoft.com/downloads/gdbmod-2.4.bz2。
3。kgdb的使用
3.1使用步骤
被调试机以kgdb内核启动,内核启动参数要加上kgdbwait,这样被调试机会在启动开始停止,等待调试机和其通讯。
调试机以正常内核启动,使用gdbmod加载被调试机的内核,注意调试机应该是编译kgdb内核的开发机,在内核编译目录下运行
../gdbmod vmlinux
(gdb) tagert remort /dev/ttyS0
这样调试机器就开始和被调试机通讯了。在通讯顺利联系上时,调试机的gdb会打印信息:
breakpoint () at kernel/kgdb.c:1876
1876 atomic_set(&kgdb_setting_breakpoint, 0);
这样就可以开始正常的调试了,调试语法和gdb完全相同,若需要继续运行,可以如下操作:
(gdb) c
Continuing.
3.2 使用VMWare建立kgdb调试环境
其它的步骤和上面相同,只是设置VMWare虚拟机时需要注意以下事项。
3.2.1调试机端
设置服务器串口命名管道,如下图所示:
3.2.2被调试机端
设置客户端串口命名管道,如下图所示:
若需要验证串口是否联通,可以在被调试机端
cat /dev/ttyS0
在调试机端
echo test > /dev/ttyS0
如果被调试机端打印出了test ,则说明串口已经联通了。
3.3用kgdb进行调试
kgdb的调试语法和gdb完全相同,在这里就不举例子了。
4。kgdb的特点
kgdb的优点是调试语法和gdb完全相同,调试起来灵活简便,使用范围广泛,可以调试内核、模块以及设备驱动。问题是需要两个操作系统,需要配置串口或网络,操作起来相对比较麻烦。
三。使用uml调试内核
uml现在已经是Linux 2.6系列中内核发布的一部分,大家可以访问http://user-mode-linux.sourceforge.net/获得更多信息,另外还可以参考Documentation/uml/UserModeLinux-HOWTO.txt,不过此文件看起来已经太旧过时了。
uml是以虚拟机的方式运行的应用层程序,而且一般需要在X-Window环境中运行。由于是应用层程序,就可以很方便地使用gdb来进行调试和跟踪,不像kgdb需要两个操作系统才可以进行调试内核。
1。uml环境的获得
uml的获得比较简单,因为已经被包含到Linux 2.6版本中了,只需要按照如下操作,就可以获得支持uml的内核:
首先在解开的linux-2.6.22目录下配置编译参数,主要是需要选择虚拟块设备、字符设备以及虚拟网络和调试信息的配置。
make menuconfig ARCH=um
如下图所示:
选择虚拟块设备
选择字符设备
选择虚拟网络设备
然后是在kernel hacking中选择调试信息
最后编译系统
make ARCH=um
得到的最终文件是根目录下的文件linux。
完成uml环境还需要安装一些工具,主要是uml_utilities,如uml_switch、uml_mconsole等工具。具体可以从http://user-mode-linux.sourceforge.net/old/dl-sf.html获得。
2。uml的使用
uml的使用和上面两种调试方法不同,由于uml是使用虚拟机的方式运行的应用层程序,所以不但需要有自己的文件系统,另外还需要一些工具软件支持和宿主机的网络通讯。
2.1uml的文件系统
uml的文件系统可以从网络获得,也可以自己制作。参看http://user-mode-linux.sourceforge.net/old/dl-sf.html,可以获得一些文件系统,这里简单介绍自己制作文件系统。
首先制作空的文件系统并格式化,例如要制作一个128M的文件系统。
dd if=/dev/zero of=root_fs count=256k
mkfs.ext3 root_fs
然后将文件系统挂接到本地。
mkdir loop
mount root_fs loop -o loop
最后有选择地将最小化的文件拷贝到挂接目录下,例如一个busybox系统目录,然后umount脱离挂接目录,这样就可以获得一个自己的最小化文件系统了,如果还需要其它文件,可以按照同样形式拷贝到root_fs中。
2.2启动uml虚拟机
启动uml非常简单,如果文件系统命名为root_fs且和运行目录在同一目录下,只要直接运行./linux就可以了。如果文件系统不叫root_fs或不在运行目录下,可以如下运行uml。
./linux ubd0=/path/fsname
当然,还可以挂接多个文件系统,如
./linux ubd0=file1 ubd1=file2
下面是uml运行的图例:
2.3和宿主机进行网络通讯
uml虚拟机可以和宿主机以及网络上的其它主机通讯,但需要宿主机支持uml选用的网络方式,并使用uml_utilities中的uml_switch设置arp_proxy,当然这些都是由程序设定,不需要完全手工执行的,在这里只介绍tuntap方式的网络通讯,其余可以参考http://user-mode-linux.sourceforge.net/old/networking.html。
2.3.1确认宿主机支持tuntap
即确认宿主机支持Universal TUN/TAP device driver support 网络驱动。
2.3.2运行uml_switch
编译安装uml_utilities,运行uml_switch
2.3.3按照以下命令运行uml
./linux ubd0=/path/fsname eth0=tuntap,,,172.31.111.111 eth1=daemon
再这里eth0是可以和宿主机通讯的虚拟网卡,eth1是可以和宿主机上其它uml虚拟机通讯的虚拟网卡。网络地址172.31.111.111是给宿主机的tap0设备设置的ip,然后在uml虚拟机里面设置ip
ifconfig eth0 172.31.1.11
这样就可以和宿主机网络上的其它主机通讯了。
2.4使用uml进行调试
现在终于进入到正题了,利用uml进行内核调试。Linux 2.6中的uml调试和2.4以前的有一些不同,linux命令行中的debug命令已经无效,可以参考http://user-mode-linux.sourceforge.net/hacking.html。下面介绍主要步骤。
首先按照上面介绍的顺序运行uml。
接着获取uml进程的pid,可以有两个方法获得:运行ps查看最小的linux pid;或则ls /用户目录/.uml -al,查看最近生成目录里的pid文件内容。这两个方法可以相互验证。
例如,本实例中使用方法一,获得的最小pid是17460:
[root@FG4DEV linux-2.6.22]# ps ax|grep ./linux-2.6.22/linux
1242 pts/13 S+ 0:00 grep ./linux-2.6.22/linux
17460 pts/16 S+ 0:00 ./linux-2.6.22/linux eth0=tuntap,,,172.31.111.111 eth1-daemon
17465 pts/16 S+ 0:00 ./linux-2.6.22/linux eth0=tuntap,,,172.31.111.111 eth1-daemon
17466 pts/16 S+ 0:00 ./linux-2.6.22/linux eth0=tuntap,,,172.31.111.111 eth1-daemon
17467 pts/16 S+ 0:00 ./linux-2.6.22/linux eth0=tuntap,,,172.31.111.111 eth1-daemon
17473 pts/18 Ss+ 0:00 ./linux-2.6.22/linux eth0=tuntap,,,172.31.111.111 eth1-daemon
17505 pts/21 Ss+ 0:00 ./linux-2.6.22/linux eth0=tuntap,,,172.31.111.111 eth1-daemon
17509 pts/22 Ss+ 0:00 ./linux-2.6.22/linux eth0=tuntap,,,172.31.111.111 eth1-daemon
使用方法二,获得的pid也是17460
[root@FG4DEV linux-2.6.22]# ls /root/.uml/ -al
drwxr-xr-x 7 root root 4096 08-08 10:30 .
drwxr-x--- 27 root root 4096 08-08 10:14 ..
drwxr-xr-x 2 root root 4096 08-08 09:29 3dRcIw
drwxr-xr-x 2 root root 4096 08-08 09:26 6GT1oZ
drwxr-xr-x 2 root root 4096 08-07 15:33 lk9LuY
drwxr-xr-x 2 root root 4096 08-08 09:03 oQCINR
drwxr-xr-x 2 root root 4096 08-08 10:30 yMk81E
[root@FG4DEV linux-2.6.22]# cat /root/.uml/yMk81E/pid 17460
所以可以判定,此时uml的初始进程号就是17460。
到这个时候,就在uml编译目录下运行gdb程序了。
gdb linux 17460
这样gdb已经和uml的内核attach了,可以设置断点进行调试了。
例如此时设置了两个断点
(gdb) b netif_receive_skb
Breakpoint 1 at 0x81d13c9: file net/core/dev.c, line 1835.
(gdb) b dev_queue_xmit
Breakpoint 2 at 0x81d0e29: file net/core/dev.c, line 1482.
(gdb) c
Continuing.
然后我们在uml里ping外面主机,gdb立刻将系统断住,接下来就可以用gdb的语法进行调试了,例如bt
2.5在uml启动时debug内核
可以如下操作,在uml编译目录下,
gdb linux
(gdb) set args "ubd0=/path/fsname eth0=tuntap,,,172.31.111.111 eth1=daemon"
(gdb) b start_kernel
(gdb) r
这样在系统运行到start_kernel时就可以被断住。
3。uml的局限
uml对于非设备相关的内核部分调试是一个很好的选择,调试语法也完全是gdb的命令,但由于是虚拟机形式运行的应用层程序,所以对涉及真实设备以及平台相关部分的调试是无能为力的,同时uml还需要宿主机必须安装上XWindow系统。
四。参考资料
http://oss.sgi.com/projects/kdb/
http://user-mode-linux.sourceforge.net/