使用 KGDB 调试 Kernel On Red Hat Linux
1. KGDB 简介
KGDB 提供了一种使用 GDB 调试 Linux 内核的机制。使用 KGDB 可以象调试普通的应用程序那样,在内核中进行设置断点、检查变量值、单步跟踪程序运行等操作。使用 KGDB 调试时需要两台机器,一台作为开发机(Development Machine),另一台作为目标机(Target Machine),两台机器之间通过串口或者以太网口相连。串口连接线是一根RS-232接口的电缆,在其内部两端的第2脚(TXD)与第3脚(RXD)交叉相连,第7脚(接地脚)直接相连。调试过程中,被调试的内核运行在目标机上,GDB 调试器运行在开发机上。
目前, KGDB 发布支持i386、x86_64、32-bit PPC、SPARC等几种体系结构的调试器。
2. KGDB 调试原理
安装 KGDB 调试环境需要为Linux内核应用 KGDB 补丁,补丁实现的gdb远程调试所需要的功能包括命令处理、陷阱处理及串口通讯3个主要的部分。 KGDB 补丁的主要作用是在Linux内核中添加了一个调试Stub。调试Stub是Linux内核中的一小段代码,提供了运行gdb的开发机和所调试内核之间的一个媒介。gdb和调试stub之间通过gdb串行协议进行通讯。gdb串行协议是一种基于消息的ASCII码协议,包含了各种调试命令。当设置断点时, KGDB 负责在设置断点的指令前增加一条trap指令,当执行到断点时控制权就转移到调试stub中去。此时,调试stub的任务就是使用远程串行通信协议将当前环境传送给gdb,然后从gdb处接受命令。gdb命令告诉stub下一步该做什么,当stub收到继续执行的命令时,将恢复程序的运行环境,把对CPU的控制权重新交还给内核。

3. KGDB 安装与配置
下面我们将以Linux 2.6.18 内核为例详细介绍 KGDB 调试环境的建立过程。
3.1 调试环境准备
3.1.1 通过物理串口线进行调试
环境要求:两台运行相同内核版本的Linux系统的设备
3.1.2 通过网络接口进行调试
环境要求:两台运行相同内核版本的Linux系统的设备
KGDB 也支持使用以太网接口作为调试器的连接端口。在对Linux内核应用补丁包时,需应用eth.patch补丁文件。配置内核时在Kernel hacking中选择 KGDB 调试项,配置 KGDB 调试端口为以太网接口,例如:
[*] KGDB : kernel debugging with remote gdbMethod for KGDB communication ( KGDB : On ethernet) --->( ) KGDB : On generic serial port (8250)(X) KGDB : On ethernet
另外使用eth0网口作为调试端口时,grub.list的配置如下:
title 2.6.7 KGDBroot (hd0,0)kernel /boot/vmlinuz-2.6.7- KGDB ro root=/dev/hda1 KGDB wait KGDB oe=@192.168.5.13/,@192.168. 6.13/
其他的过程与使用串口作为连接端口时的设置过程相同。
注意:尽管可以使用以太网口作为 KGDB 的调试端口,使用串口作为连接端口更加简单易行, KGDB 项目组推荐使用串口作为调试端口。
3.1.3 通过 VMware Workstation 搭建调试环境
环境要求:两台运行相同内核版本的Linux系统虚拟机
虚拟机中的串口连接可以采用两种方法:一种是指定虚拟机的串口连接到实际的COM上,例如开发机连接到COM1,目标机连接到COM2,然后把两个串口通过串口线相连接。另一种更为简便的方法是:在较高一些版本的VMware中都支持把串口映射到命名管道,把两个虚拟机的串口映射到同一个命名管道。例如,在两个虚拟机中都选定同一个命名管道 \\.\pipe\com_1,指定Target机的COM口为server端,并选择"The other end is a virtual machine"属性;指定 Development 机的COM口端为client端,同样指定COM口的"The other end is a virtual machine"属性。对于IO mode属性,在Target上选中"Yield CPU on poll"复选择框,Development 机不选。这样,可以无需附加任何硬件,利用虚拟机就可以搭建 KGDB 调试环境。 即降低了使用 KGDB 进行调试的硬件要求,也简化了建立调试环境的过程。
由于通过 VMware Workstation 搭建调试环境最容易实现,而且操作起来简单,所以以该方式来说明 KGDB 安装与配置全过程。
Development 虚拟机串口配置:

Target 虚拟机串口配置:

测试两台机器之间串口连接情况,stty命令可以对串口COM1参数进行设置(注意上面使用的是com_1,串口设备对应的是ttyS1):
Development 虚拟机:
[root@localhost ~]# stty ispeed 115200 ospeed 115200 -F /dev/ttyS1
Target 虚拟机:
[root@BendSha_RHEL5_5_x64 ~]# stty ispeed 115200 ospeed 115200 -F /dev/ttyS1
Development 虚拟机通过串口COM1发送消息:
[root@localhost ~]# echo "this message come from bendsha">/dev/ttyS1[root@localhost ~]# echo "hoho">/dev/ttyS1
Target 虚拟机通过串口COM1接收消息:
[root@BendSha_RHEL5_5_x64 ~]# cat /dev/ttyS1this message come from bendshahoho
串口测试连接成功。
3.2 安装与配置
下面我们需要应用 KGDB 补丁到Linux内核,设置内核选项并编译内核。这方面的资料相对较少,笔者这里给出详细的介绍。下面的工作在开发机(Development )上进行,以上面介绍的试验环境为例,某些具体步骤在实际的环境中可能要做适当的改动。
3.2.1 内核的配置与编译
下载内核源码
[root@localhost ~]# wget ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.32.tar.bz2--2016-11-07 07:18:03-- ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.32.tar.bz2=> `linux-2.6.32.tar.bz2'Resolving ftp.kernel.org... 199.204.44.194, 198.145.20.140, 149.20.4.69Connecting to ftp.kernel.org|199.204.44.194|:21... connected.Logging in as anonymous ... Logged in!==> SYST ... done. ==> PWD ... done.==> TYPE I ... done. ==> CWD /pub/linux/kernel/v2.6 ... done.==> SIZE linux-2.6.32.tar.bz2 ... 64424138==> PASV ... done. ==> RETR linux-2.6.32.tar.bz2 ... done.Length: 64424138 (61M)0% [ ] 172,280 18.6K/s eta 54m 6s
配置内核,File System 的配置(因为后期要调试文件系统,所以勾选了如图所示的几个配置):
[root@localhost linux-2.6.32]# make menuconfigscripts/kconfig/mconf arch/x86/Kconfig

Kernel Hacking 的配置:

保存退出。
编译内核:
[root@localhost linux-2.6.32]# make -j10 bzImage
编译内核模块:
[root@localhost linux-2.6.32]# make modules
将 Development 系统中的 linux-2.6.32 整个目录同步到 Target :
[root@localhost src]# scp -r linux-2.6.32 192.168.117.129:/usr/srcThe authenticity of host '192.168.117.129 (192.168.117.129)' can't be established.RSA key fingerprint is 63:6c:22:cf:a8:55:a9:5a:ea:72:23:1d:56:5f:28:a0.Are you sure you want to continue connecting (yes/no)? yesWarning: Permanently added '192.168.117.129' (RSA) to the list of known hosts.root@192.168.117.129's password:
备注:如果原系统已经是2.6.32的内核,可以只拷贝arch/i386/boot/bzImage,及System.map文件到Target上,然后修改/boot/grub/grub.conf,但由于我是从2.6.18 升级上来,所以需要将整个linux原代码目录拷贝到Target上进行升级。
升级 Target 的内核:
[root@BendSha_RHEL5_5_x64 src]# ln -s linux-2.6.36 linux[root@BendSha_RHEL5_5_x64 src]# cd linux[root@BendSha_RHEL5_5_x64 linux]# make modules_install......INSTALL /lib/firmware/keyspan_pda/xircom_pgs.fwDEPMOD 2.6.32[root@BendSha_RHEL5_5_x64 linux]# make installsh /usr/src/linux-2.6.32/arch/x86/boot/install.sh 2.6.32 arch/x86/boot/bzImage \System.map "/boot"[root@BendSha_RHEL5_5_x64 linux]#
安装完成后,在/boot/目录下会有一个新添加的文件:
[root@BendSha_RHEL5_5_x64 linux]# cd /boot/[root@BendSha_RHEL5_5_x64 boot]# lsconfig-2.6.18-194.el5 lost+found System.map-2.6.32grub message vmlinuzinitrd-2.6.18-194.el5.img symvers-2.6.18-194.el5.gz vmlinuz-2.6.18-194.el5initrd-2.6.18-194.el5kdump.img System.map vmlinuz-2.6.32initrd-2.6.32.img System.map-2.6.18-194.el5
/boot/grub/grub.conf文件也会添加一个新的启动项
[root@BendSha_RHEL5_5_x64 boot]# cat grub/grub.conf# grub.conf generated by anaconda## Note that you do not have to rerun grub after making changes to this file# NOTICE: You have a /boot partition. This means that# all kernel and initrd paths are relative to /boot/, eg.# root (hd0,0)# kernel /vmlinuz-version ro root=/dev/sda2# initrd /initrd-version.img#boot=/dev/sdadefault=1timeout=5splashimage=(hd0,0)/grub/splash.xpm.gzhiddenmenutitle CentOS (2.6.32)root (hd0,0)kernel /vmlinuz-2.6.32 ro root=LABEL=/ crashkernel=128M@16Minitrd /initrd-2.6.32.imgtitle Red Hat Enterprise Linux Server (2.6.18-194.el5)root (hd0,0)kernel /vmlinuz-2.6.18-194.el5 ro root=LABEL=/ crashkernel=128M@16Minitrd /initrd-2.6.18-194.el5.img
注意里面的NOTICE说明,我的系统/boot是一个独立的分区,所以下面配置的文件路径都是相对于/boot/目录的,像 /vmlinuz-2.6.32,实际到它的绝对位置应该是/boot/vmlinuz-2.6.32。在你的系统上请根据实际情况处理。
修改 grub.conf 文件:
[root@BendSha_RHEL5_5_x64 grub]# vi grub.conf# grub.conf generated by anaconda## Note that you do not have to rerun grub after making changes to this file# NOTICE: You have a /boot partition. This means that# all kernel and initrd paths are relative to /boot/, eg.# root (hd0,0)# kernel /vmlinuz-version ro root=/dev/sda2# initrd /initrd-version.img#boot=/dev/sdadefault=1timeout=5splashimage=(hd0,0)/grub/splash.xpm.gzhiddenmenutitle CentOS (2.6.32)root (hd0,0)kernel /vmlinuz-2.6.32 ro root=LABEL=/ crashkernel=128M@16M kgdboc=ttyS1,115200initrd /initrd-2.6.32.imgtitle CentOS (2.6.32 kernel debug)root (hd0,0)kernel /vmlinuz-2.6.32 ro root=LABEL=/ crashkernel=128M@16M kgdboc=ttyS1,115200 kgdbwaitinitrd /initrd-2.6.32.imgtitle Red Hat Enterprise Linux Server (2.6.18-194.el5)root (hd0,0)kernel /vmlinuz-2.6.18-194.el5 ro root=LABEL=/ crashkernel=128M@16Minitrd /initrd-2.6.18-194.el5.img:
说明:
第一个启动项在原来的基础上添加了kgdb的参数kgdboc=ttyS1,115200
kgdboc 的意思是 kgdb over console,这里将kgdb连接的console设置为ttyS1,波特率为115200,如果不在内核启动项中配置该参数,可以在进入系统后执行命令:echo ttyS1 > /sys/module/kgdboc/parameters/kgdboc
第二个启动项同第一个,使用同一个内核,只是添加了kgdbwait参数,kgdbwait 使 kernel 在启动过程中等待 gdb 的连接。
最终启动菜单如下:
CentOS (2.6.32)
CentOS (2.6.32 kernel debug)
Red Hat Enterprise Linux Server (2.6.18-194.el5)
调用内核模块,就选择第一个,如果要调试内核启动过程,选择第二个.
3.2.2 内核调试
重启Target,通过启动菜单第一项CentOS(2.6.32)进入系统:

进入Development 系统,开始调试:
[root@localhost src]# cd linux-2.6.32[root@localhost linux-2.6.32]# gdb vmlinuxGNU gdb (GDB) Red Hat Enterprise Linux (7.0.1-23.el5)Copyright (C) 2009 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law. Type "show copying"and "show warranty" for details.This GDB was configured as "x86_64-redhat-linux-gnu".For bug reporting instructions, please see:<http://www.gnu.org/software/gdb/bugs/>...Reading symbols from /usr/src/linux-2.6.32/vmlinux...(no debugging symbols found)...done.(gdb) set remotebaud 115200(gdb) target remote /dev/ttyS1Remote debugging using /dev/ttyS1kgdb_breakpoint () at kernel/kgdb.c:17211721 wmb(); /* Sync point after breakpoint */(gdb)
看到上面的内容说明已经连接成功,但 Target 系统依然是假死状态,这时你可以像使用本地gdb一样设置断点(break),单步执行(step,,或其它命令。
输入 cont,继续执行,Target 就继续下面的系统初始化了。
噢噢,出了点小问题,回头解决吧。
系统启动完成后的内核调试:
进入Target 后,执行命令:
[root@BendSha_RHEL5_5_x64 grub]# echo g > /proc/sysrq-trigger
系统同样会中断,进入假死状态,等待远程gdb的连接.KGDB可能会输出如下信息:
上面的命令(echo g > /proc/sysrq-trigger)可以有一个快捷键(ALT-SysRq-G)代替,当然前提是你编译内核时需要选中相关选项,并且需要修改配置文件:/etc/sysctl.conf ,我用了一下,不太好用,因为有的桌面系统中PrintScreen/SysRq键是用于截屏的,所以还是直接用命令来的好!。
可以在~/.bashrc中添加了一句(添加完保存后,要执行source ~/.bashrc应用该配置):
alias debug='echo g > /proc/sysrq-trigger'
之后就可以直接输入debug 来使内核进入调试状态.。
注意:有的技术文档反馈,因为vmware的named piped不能被gdb直接使用,需要使用 socat -d -d /tmp/com_1 /dev/ttyS1转换,然后使用转换后的设备。socat需要自己下载安装,但在我的环境中直接使用并没有出错.。
浙公网安备 33010602011771号