内核调试:Linux系统内核的调试(引自www.linuxeden.com,作者ott)


=tf width="98%" align=center border=0>Linux 系统内核调试 
 
调试是软件开发过程中个必不可少环节在 Linux 内核开发过程中也不可避免地会面对如何调试内核问题但是Linux 系统开发者出于保证内核代码正确性考虑不愿意在 Linux 内核源代码树中加入个调试器他们认为内核中调试器会误导开发者从而引入不良修正[1]所以对 Linux 内核进行调试直是个令内核员感到棘手问题调试工作艰苦性是内核级开发区别于用户级开发个显著特点

尽管缺乏种内置调试内核有效方法但是 Linux 系统在内核发展过程中也逐渐形成了些监视内核代码和跟踪技术同时许多补丁应运而生它们为标准内核附加了内核调试支持尽管这些补丁有些并不被 Linux 官方组织认可但他们确实功能完善十分强大调试内核问题时利用这些工具与方法跟踪内核执行情况并查看其内存和数据结构将是非常有用
本文将首先介绍 Linux 内核上些内核代码监视和跟踪技术这些调试和跟踪方法因所要求使用环境和使用方法而各有不同然后重点介绍三种 Linux 内核源代码级调试方法
1. Linux 系统内核级软件调试技术
prk 是调试内核代码时最常用种技术在内核代码中特定位置加入prk 调试可以直接把所关心信息打打印到屏幕上从而可以观察执行路径和所关心变量、指针等信息 Linux 内核调试器(Linux kernel debuggerkdb)是 Linux 内核补丁它提供了种在系统能运行时对内核内存和数据结构进行检查办法Oops、KDB在文章掌握 Linux 调试技术有详细介绍大家可以参考 Kprobes 提供了个强行进入任何内核例程并从中断处理器无干扰地收集信息接口使用 Kprobes 可以轻松地收集处理器寄存器和全局数据结构等调试信息而无需对Linux内核频繁编译和启动具体使用方法请参考使用 Kprobes 调试内核
以上介绍了进行Linux内核调试和跟踪时常用技术和方法当然内核调试与跟踪方法还不止以上提到这些这些调试技术个共同特点在于他们都不能提供源代码级有效内核调试手段有些只能称之为跟踪技术因此这些方法都只能提供有限调试能力下面将介绍三种实用源代码级内核调试方法
2. 使用KGDB构建Linux内核调试环境
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等几种体系结构调试器有关kgdb补丁下载地址见参考资料[4]
2.1 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控制权重新交还给内核



2.2 Kgdb安装与设置
下面我们将以Linux 2.6.7内核为例详细介绍kgdb调试环境建立过程
2.2.1软硬件准备
以下软硬件配置取自笔者进行试验系统配置情况:



kgdb补丁版本遵循如下命名模式:Linux-A-kgdb-B其中A表示Linux内核版本号B为kgdb版本号以试验使用kgdb补丁为例linux内核版本为linux-2.6.7补丁版本为kgdb-2.2
物理连接好串口线后使用以下命令来测试两台机器之间串口连接情况stty命令可以对串口参数进行设置:
在development机上执行:

stty ispeed 115200 ospeed 115200 -F /dev/ttyS0
在target机上执行:

stty ispeed 115200 ospeed 115200 -F /dev/ttyS0
在developement机上执行:

echo hello > /dev/ttyS0
在target机上执行:

cat /dev/ttyS0
如果串口连接没问题话在将在target机屏幕上显示"hello"
2.2.2 安装与配置
下面我们需要应用kgdb补丁到Linux内核设置内核选项并编译内核这方面资料相对较少笔者这里给出详细介绍下面工作在开发机(developement)上进行以上面介绍试验环境为例某些具体步骤在实际环境中可能要做适当改动:
I、内核配置与编译

[root@lisl tmp]# tar -jxvf linux-2.6.7.tar.bz2
[root@lisl tmp]#tar -jxvf linux-2.6.7-kgdb-2.2.tar.tar
[root@lisl tmp]#cd inux-2.6.7
请参照目录补丁包中文件README给出说明执行对应体系结构补丁由于试验在i386体系结构上完成所以只需要安装下补丁:core-lite.patch、i386-lite.patch、 8250.patch、eth.patch、core.patch、i386.patch应用补丁文件时请遵循kgdb软件包内series文件所指定顺序否则可能会带来预想不到问题eth.patch文件是选择以太网口作为调试连接端口时需要运用补丁

应用补丁命令如下所示:

[root@lisl tmp]#patch -p1 <../linux-2.6.7-kgdb-2.2/core-lite.patch 
如果内核正确那么应用补丁时应该不会出现任何问题(不会产生*.rej文件)为Linux内核添加了补丁之后需要进行内核配置内核配置可以按照你习惯选择配置Linux内核任意种方式

[root@lisl tmp]#make menuconfig
在内核配置菜单Kernel hacking选项中选择kgdb调试项例如:

 [*] KGDB: kernel debugging with remote gdb                                                             
       Method for KGDB communication (KGDB: On generic serial port (8250)) ---> 
 [*] KGDB: Thread analysis                                                                            
 [*] KGDB: Console messages through gdb
[root@lisl tmp]#make
 
编译内核之前请注意Linux目录下Makefile中优化选项默认Linux内核编译都以-O2优化级别进行在这个优化级别之下编译器要对内核中某些代码执行顺序进行改动所以在调试时会出现运行与代码顺序不情况可以把Makefile中-O2选项改为-O,但不可去掉- O否则编译会出问题为了使编译后内核带有调试信息注意在编译内核时候需要加上-g选项
不过当选择"Kernel debugging->Compile the kernel with debug info"选项后配置系统将自动打开调试选项另外选择"kernel debugging with remote gdb"后配置系统将自动打开"Compile the kernel with debug info"选项
内核编译完成后使用scp命令进行将相关文件拷贝到target机上(当然也可以使用其它网络工具如rcp)

[root@lisl tmp]#scp arch/i386/boot/bzImage [email protected]:/boot/vmlinuz-2.6.7-kgdb
[root@lisl tmp]#scp .map [email protected]:/boot/.map-2.6.7-kgdb
如果系统启动使所需要某些设备驱动没有编译进内核情况下那么还需要执行如下操作:

[root@lisl tmp]#mkinitrd /boot/initrd-2.6.7-kgdb 2.6.7
[root@lisl tmp]#scp initrd-2.6.7-kgdb [email protected]:/boot/ initrd-2.6.7-kgdb
II、kgdb启动
在将编译出内核拷贝到target机器之后需要配置系统引导加入内核启动选项以下是kgdb内核引导参数说明:



如表中所述在kgdb 2.0版本之后内核引导参数已经与以前版本有所不同使用grub引导直接将kgdb参数作为内核vmlinuz引导参数下面给出引导器配置示例

title 2.6.7 kgdb
root (hd0,0)
kernel /boot/vmlinuz-2.6.7-kgdb ro root=/dev/hda1 kgdbwait kgdb8250=1,115200
在使用lilo作为引导需要把kgdb参放在由append修饰语句中下面给出使用lilo作为引导器时配置示例

image=/boot/vmlinuz-2.6.7-kgdb
label=kgdb
    read-only
    root=/dev/hda3
append="gdb gdbttyS=1 gdbbaud=115200"
保存好以上配置后重新启动计算机选择启动带调试信息内核内核将在短暂运行后在创建init内核线程之前停下来打印出以下信息并等待开发机连接
Waiting for connection from remote gdb...
在开发机上执行:

gdb
file vmlinux
 remotebaud 115200
target remote /dev/ttyS0
其中vmlinux是指向源代码目录下编译出来Linux内核文件链接它是没有经过压缩内核文件gdb从该文件中得到各种符号地址信息
这样就与目标机上kgdb调试接口建立了联系旦建立联接之后对Linux内调试工作与对普通运用调试就没有什么区别了任何时候都可以通过键入ctrl+c打断目标机执行进行具体调试工作
在kgdb 2.0之前版本中编译内核后在arch/i386/kernel目录下还会生成可执行文件gdbstart将该文件拷贝到target机器/boot目录下此时无需更改内核启动配置文件直接使用命令:

[root@lisl boot]#gdbstart -s 115200 -t /dev/ttyS0
可以在KGDB内核引导启动完成后建立开发机与目标机之间调试联系
2.2.3 通过网络接口进行调试
kgdb也支持使用以太网接口作为调试器连接端口在对Linux内核应用补丁包时需应用eth.patch补丁文件配置内核时在Kernel hacking中选择kgdb调试项配置kgdb调试端口为以太网接口例如:

[*]KGDB: kernel debugging with remote gdb
Method for KGDB communication (KGDB: On ethernet) ---> 
( ) KGDB: On generic serial port (8250)
(X) KGDB: On ethernet
另外使用eth0网口作为调试端口时grub.list配置如下:

title 2.6.7 kgdb
root (hd0,0)
kernel /boot/vmlinuz-2.6.7-kgdb ro root=/dev/hda1 kgdbwait [email protected].
5.13/,@192.168. 6.13/ 
其他过程与使用串口作为连接端口时设置过程相同
注意:尽管可以使用以太网口作为kgdb调试端口使用串口作为连接端口更加简单易行kgdb项目组推荐使用串口作为调试端口
2.2.4 模块调试方法
内核可加载模块调试具有其特殊性由于内核模块中各段地址是在模块加载进内核时候才最终确定所以develop机gdb无法得到各种符号地址信息所以使用kgdb调试模块所需要解决个问题是需要通过某种方法获得可加载模块最终加载地址信息并把这些信息加入到gdb环境中
I、在Linux 2.4内核中内核模块调试方法
在Linux2.4.x内核中可以使用insmod -m命令输出模块加载信息例如:

[root@lisl tmp]# insmod -m hello.ko >modaddr
查看模块加载信息文件modaddr如下:

.this           00000060 c88d8000 2**2
.text           00000035 c88d8060 2**2
.rodata         00000069 c88d80a0 2**5
……
.data           00000000 c88d833c 2**2
.bss            00000000 c88d833c 2**2
……
在这些信息中我们关心只有4个段地址:.text、.rodata、.data、.bss在development机上将以上地址信息加入到gdb中,这样就可以进行模块功能测试了

(gdb) Add-symbol-file hello.o 0xc88d8060 -s .data 0xc88d80a0 -s 
.rodata 0xc88d80a0 -s .bss 0x c88d833c
这种方法也存在不足它不能调试模块代码此时模块化代码已经执行过了而如果不执行模块加载又无法获得模块插入地址更不可能在模块化之前设置断点了对于这种调试要求可以采用以下替代方法
在target 机上用上述方法得到模块加载地址信息然后再用rmmod卸载模块在development机上将得到模块地址信息导入到gdb环境中在内核代码化代码之前设置断点这样在target机上再次插入模块时代码将在执行模块化之前停下来这样就可以使用gdb命令调试模块化代码了
另外种调试模块方法是:当插入内核模块时内核模块机制将sys_init_module(kernel/modle.c)执行对内核模块所插入模块代码片断如下:

…… ……
    (mod->init != NULL)
      ret = mod->init;
…… ……
在该语句上设置断点也能在执行模块化之前停下来
II、在Linux 2.6.x内核中内核模块调试方法
Linux 2.6之后内核中由于module-init-tools工具更改insmod命令不再支持-m参数只有采取其他方法来获取模块加载到内核地址通过分析ELF文件格式我们知道中各段意义如下:
.text(代码段):用来存放可执行文件操作指令也就是说是它是可执行在内存种镜像
.data(数据段):数据段用来存放可执行文件中已化全局变量也就是存放静态分配变量和全局变量
.bss(BSS段):BSS段包含了中未化全局变量在内存中 bss段全部置零
.rodata(只读段):该段保存着只读数据在进程映象中构造不可写
通过在模块中放置下代码我们可以很容易地获得模块加载到内存中地址

……
 bss_var;
  hello_init(void)
{
prk(KERN_ALERT "Text location .text(Code Segment):%p\n",hello_init);

  data_var=0;
prk(KERN_ALERT "Data Location .data(Data Segment):%p\n",&data_var);

prk(KERN_ALERT "BSS Location: .bss(BSS Segment):%p\n",&bss_var);
……
}
Module_init(hello_init);
这里通过在模块中添加段简单使模块在加载时打印出在内核中加载地址.rodata段地址可以通过执行命令readelf -e hello.ko取得.rodata在文件中偏移量并加上段align值得出
为了使读者能够更好地进行模块调试kgdb项目还发布了些脚本能够自动探测模块插入并自动更新gdb中模块符号信息这些脚本工作原理与前面解释工作过程相似更多信息请阅读参考资料[4]
2.2.5 硬件断点
kgdb提供对硬件调试寄存器支持在kgdb中可以设置三种硬件断点:执行断点(Execution Breakpo)、写断点(Write Breakpo)、访问断点(Access Breakpo)但不支持I/O访问断点目前kgdb对硬件断点支持是通过宏来实现最多可以设置4个硬件断点这些宏用法如下:



在有些情况下硬件断点使用对于内核调试是非常方便有关硬件断点定义和具体使用说明见参考资料[4]

2.3.在VMware中搭建调试环境
kgdb调试环境需要使用两台微机分别充当development机和target机使用VMware后我们只使用台计算机就可以顺利完成kgdb调试环境搭建以windows下环境为例创建两台虚拟机台作为开发机台作为目标机
2.3.1虚拟机之间串口连接
虚拟机中串口连接可以采用两种方法种是指定虚拟机串口连接到实际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进行调试硬件要求也简化了建立调试环境过程



2.3.2 VMware使用技巧
VMware 虚拟机是比较占用资源尤其是象上面那样在Windows中使用两台虚拟机因此最好为系统配备512M以上内存每台虚拟机至少分配128M内存这样硬件要求对目前主流配置PC而言并不是过高要求出于系统性能考虑在VMware中尽量使用界面进行调试工作同时Linux 系统默认情况下开启了sshd服务建议使用SecureCRT登陆到Linux进行操作这样可以有较好用户使用界面
2.3.3 在Linux下虚拟机中使用kgdb
对于在Linux下面使用VMware虚拟机情况笔者没有做过实际探索从原理上而言只需要在Linux下只要创建台虚拟机作为target机开发机工作可以在实际Linux环境中进行搭建调试环境过程与上面所述过程类似由于只需要创建台虚拟机所以使用Linux下虚拟机搭建 kgdb调试环境对系统性能要求较低(vmware已经推出了Linux下版本)还可以在development机上配合使用些其他调试工具例如功能更强大cgdb、图形界面DDD调试器等以方便内核调试工作



2.4 kgdb些特点和不足
使用kgdb作为内核调试环境最大不足在于对kgdb硬件环境要求较高必须使用两台计算机分别作为target和development机尽管使用虚拟机方法可以只用台PC即能搭建调试环境但是对系统其他方面性能也提出了要求同时也增加了搭建调试环境时复杂程度另外kgdb内核编译、配置也比较复杂需要技巧笔者当时做时候也是费了很多周折当调试过程结束后时还需要重新制作所要发布内核使用kgdb并不能进行全程调试也就是说kgdb并不能用于调试系统开始化引导过程
不过kgdb是个不错内核调试工具使用它可以进行对内核全面调试甚至可以调试内核中断处理如果在些图形化开发工具帮助下对内核调试将更方便
3. 使用SkyEye构建Linux内核调试环境
SkyEye 是个开源软件项目(OPenSource Software),SkyEye项目目标是在通用Linux和Windows平台上模拟常见嵌入式计算机系统SkyEye实现了个指令级硬件模拟平台可以模拟多种嵌入式开发板支持多种CPU指令集SkyEye 核心是 GNU  gdb 项目它把gdb和 ARM Simulator很好地结合在了加入ARMulator 功能之后它就可以来仿真嵌入式开发板在它上面不仅可以调试硬件驱动还可以调试操作系统Skyeye项目目前已经在嵌入式系统开发领域得到了很大推广
3.1 SkyEye安装和μcLinux内核编译
3.1.1 SkyEye安装
SkyEye 安装不是本文要介绍重点目前已经有大量资料对此进行了介绍有关SkyEye安装与使用内容请查阅参考资料[11]由于skyeye面目主要用于嵌入式系统领域所以在skyeye上经常使用是μcLinux系统当然使用Linux作为skyeye上运行系统也是可以由于介绍 μcLinux 2.6在skyeye上编译相关资料并不多所以下面进行详细介绍
3.1.2 μcLinux 2.6.x编译
要在SkyEye中调试操作系统内核首先必须使被调试内核能在SkyEye所模拟开发板上正确运行因此正确编译待调试操作系统内核并配置 SkyEye是进行内核调试下面我们以SkyEye模拟基于Atmel AT91X40开发板并运行μcLinux 2.6为例介绍SkyEye具体调试方法
I、安装交叉编译环境
先安装交叉编译器尽管在些资料中说明使用工具链arm -elf-tools-20040427.sh ,但是由于arm-elf-xxx与arm-linux-xxx对宏及链接处理不同经验证明使用arm-elf-xxx工具链在链接vmlinux最后阶段将会出错所以这里我们使用交叉编译工具链是:arm-uclinux-tools-base-gcc3.4.0-20040713.sh关于该交叉编译工具链下载地址请参见[6]注意以下步骤最好用root用户来执行

[root@lisl tmp]#chmod +x arm-uclinux-tools-base-gcc3.4.0-20040713.sh
[root@lisl tmp]#./arm-uclinux-tools-base-gcc3.4.0-20040713.sh
安装交叉编译工具链之后请确保工具链安装路径存在于系统PATH变量中
II、制作μcLinux内核
得到μcLinux发布包个最容易方法是直接访问uClinux.org站点[7]该站点发布内核版本可能不是最新但你能找到个最新 μcLinux补丁以及找个对应Linux内核版本来制作个最新μcLinux内核这里将使用这种方法来制作最新μcLinux内核目前(笔者记录编写此文章时)所能得到发布包最新版本是uClinux-dist.20041215.tar.gz
下载uClinux-dist.20041215.tar.gz文件下载地址请参见[7]
下载linux-2.6.9-hsc0.patch.gz文件下载地址请参见[8]
下载linux-2.6.9.tar.bz2文件下载地址请参见[9]
现在我们得到了整个linux-2.6.9源代码以及所需内核补丁请准备个有2GB空间目录里来完成以下制作μcLinux内核过程

[root@lisl tmp]# tar -jxvf uClinux-dist-20041215.tar.bz2
[root@lisl uClinux-dist]# tar -jxvf linux-2.6.9.tar.bz2
[root@lisl uClinux-dist]# gzip -dc linux-2.6.9-hsc0.patch.gz | patch -p0 
或者使用:

[root@lisl uClinux-dist]# gunzip linux-2.6.9-hsc0.patch.gz 
[root@lisl uClinux-dist]patch -p0 < linux-2.6.9-hsc0.patch
执行以上过程后将在linux-2.6.9/arch目录下生成个补丁目录-armnommu删除原来μcLinux目录里linux-2.6.x(即那个linux-2.6.9-uc0)并将我们打好补丁Linux内核目录更名为linux-2.6.x

[root@lisl uClinux-dist]# rm -rf linux-2.6.x/
[root@lisl uClinux-dist]# mv linux-2.6.9 linux-2.6.x
III、配置和编译μcLinux内核
只是出于调试μcLinux内核这里没有生成uClibc库文件及romfs.img文件在发布μcLinux时已经预置了某些常用嵌入式开发板配置文件因此这里直接使用这些配置文件过程如下:

[root@lisl uClinux-dist]# cd linux-2.6.x
[root@lisl linux-2.6.x]#make ARCH=armnommu CROSS_COMPILE=arm-uclinux- atmel_
deconfig
atmel_deconfig文件是μcLinux发布时提供个配置文件存放于目录linux-2.6.x /arch/armnommu/configs/中

[root@lisl linux-2.6.x]#make ARCH=armnommu CROSS_COMPILE=arm-uclinux-
oldconfig
下面编译配置好内核:

[root@lisl linux-2.6.x]# make ARCH=armnommu CROSS_COMPILE=arm-uclinux- v=1
般情况下编译将顺利结束并在Linux-2.6.x/目录下生成未经压缩μcLinux内核文件vmlinux需要注意是为了调试μcLinux内核需要打开内核编译调试选项-g使编译后内核带有调试信息打开编译选项方法可以选择:
"Kernel debugging->Compile the kernel with debug info"后将自动打开调试选项也可以直接修改linux-2.6.x目录下Makefile文件为其打开调试开关方法如下:

CFLAGS  -g 
最容易出现问题是找不到arm-uclinux-gcc命令主要原因是PATH变量中没有包含arm-uclinux-gcc命令所在目录在arm-linux-gcc缺省安装情况下安装目录是/root/bin/arm-linux-tool/使用以下命令将路径加到PATH环境变量中

Export PATH=$PATH:/root/bin/arm-linux-tool/bin
IV、根文件系统制作
Linux内核在启动最后操作之是加载根文件系统根文件系统中存放了嵌入式系统使用所有应用、库文件及其他些需要用到服务出于文章篇幅考虑这里不打算介绍根文件系统制作方法读者可以查阅些其他相关资料值得注意由配置文件skyeye.conf指定了装载到内核中根文件系统
3.2 使用SkyEye调试
编译完μcLinux内核后就可以在SkyEye中调试该ELF执行文件格式内核了前面已经说过利用SkyEye调试内核与使用gdb调试运用方法相同
需要提醒读者SkyEye配置文件-skyeye.conf记录了模拟硬件配置和模拟执行行为该配置文件是SkyEye系统中个及其重要文件很多和异常情况发生都和该文件有关在安装配置SkyEye出错时请首先检查该配置文件然后再进行其他工作此时所有准备工作已经完成就可以进行内核调试工作了
3.3使用SkyEye调试内核特点和不足
在SkyEye 中可以进行对Linux系统内核全程调试由于SkyEye目前主要支持基于ARM内核CPU因此般而言需要使用交叉编译工具编译待调试 Linux系统内核另外制作SkyEye中使用内核编译、配置过程比较复杂、繁琐不过当调试过程结束后无需重新制作所要发布内核
SkyEye只是对系统硬件进行了定程度上模拟所以在SkyEye与真实硬件环境相比较而言还是有差距这对些与硬件紧密相关调试可能会有影响例如驱动调试不过对于大部分软件调试SkyEye已经提供了精度足够模拟了
SkyEye个目标是和eclipse结合有了图形界面能为调试和查看源码提供些方便
4. 使用UML调试Linux内核
User-mode Linux(UML)简单说来就是在Linux内运行Linux该项目是使Linux内核成为个运行在 Linux 系统之上单独、用户空间进程UML并不是运行在某种新硬件体系结构之上而是运行在基于 Linux 系统接口所实现虚拟机正是由于UML是个将Linux作为用户空间进程运行特性可以使用UML来进行操作系统内核调试有关UML介绍请查阅参考资料[10]、[12]
4.1 UML安装与调试
UML 安装需要台运行Linux 2.2.15以上或者2.3.22以上I386机器对于2.6.8及其以前版本UML采用两种形式发布:种是以RPM包形式发布种是以源代码形式提供UML安装按照UML说明以RPM形式提供安装包比较陈旧且会有许多问题以二进制形式发布UML包并不包含所需要调试信息这些代码在发布时已经做了程度不同优化所以要想利用UML调试Linux系统内核需要使用最新UML patch代码和对应版本Linux内核编译、安装UML完成UML补丁之后会在arch目录下产生个um目录主要UML代码都放在该目录下
从2.6.9版本之后(包含2.6.9版本Linux)User-Mode Linux已经随Linux内核源代码树起发布它存放于arch/um目录下
编译好UML内核之后直接使用gdb运行已经编译好内核即可进行调试
4.2使用UML调试系统内核特点和不足
目前用户模式 Linux 虚拟机也存在局限性由于UML虚拟机是基于Linux系统接口方式实现虚拟机所以用户模式内核不能访问主机系统上硬件设备因此 UML并不适合于调试那些处理实际硬件驱动不过如果所编写内核不是硬件驱动例如Linux文件系统、协议栈等情况使用UML作为调试工具还是个不错选择
5. 内核调试配置选项
为了方便调试和测试代码内核提供了许多与内核调试相关配置选项这些选项大部分都在内核配置编辑器内核开发(kernel hacking)菜单项中在内核配置目录树菜单其他地方也还有些可配置调试选项下面将对他们作介绍
Page alloc debugging :CONFIG_DEBUG_PAGEALLOC: 
不使用该选项时释放内存页将从内核地址空间中移出使用该选项后内核推迟移出内存页过程因此能够发现内存泄漏
Debug memory allocations :CONFIG_DEBUG_SLAB: 
该打开该选项时在内核执行内存分配之前将执行多种类型检查通过这些类型检查可以发现诸如内核过量分配或者未化等内核将会在每次分配内存前后时设置些警戒值如果这些值发生了变化那么内核就会知道内存已经被操作过并给出明确提示从而使各种隐晦变得容易被跟踪
Spinlock debugging :CONFIG_DEBUG_SPINLOCK:
打开此选项时内核将能够发现spinlock未化及各种其他,能用于排除些死锁引起
Sleep-inside-spinlock checking:CONFIG_DEBUG_SPINLOCK_SLEEP: 
打开该选项时当spinlock持有者要睡眠时会执行相应检查实际上即使者目前没有睡眠而只是存在睡眠可能性时也会给出提示
Compile the kernel with debug info :CONFIG_DEBUG_INFO:
打开该选项时编译出内核将会包含全部调试信息使用gdb时需要这些调试信息
Stack utilization instrumentation :CONFIG_DEBUG_STACK_USAGE:
该选项用于跟踪内核栈溢出个内核栈溢出明显现象是产生oops却没有列出系统栈信息该选项将使内核进行栈溢出检查并使内核进行栈使用统计
Driver Core verbose debug messages:CONFIG_DEBUG_DRIVER:
该选项位于"Device drivers-> Generic Driver Options"下打开该选项使得内核驱动核心产生大量调试信息并将他们记录到系统日志中
Verbose SCSI error reporting (kernel size 12K) :CONFIG_SCSI_CONSTANTS:
该选项位于"Device drivers/SCSI device support"下当SCSI设备出错时内核将给出详细出错信息
Event debugging:CONFIG_INPUT_EVBUG: 
打开该选项时会将输入子系统及所有事件都输出到系统日志中该选项在产生了详细输入报告同时也会导致安全问题
以上内核编译选项需要读者根据自己所进行内核编程实际情况灵活选取在使用以上介绍三种源代码级内核调试工具时般需要选取CONFIG_DEBUG_INFO选项以使编译内核包含调试信息
6. 总结
上面介绍了些调试Linux内核方法特别是详细介绍了三种源代码级内核调试工具以及搭建这些内核调试环境方法读者可以根据自己情况从中作出选择
调试工具(例如gdb)运行都需要操作系统支持而此时内核由于代码而不能正确执行对系统管理功能所以对内核调试必须采取些特殊方法进行以上介绍三种源代码级调试方法可以归纳为以下两种策略:
I、为内核增加调试Stub利用调试Stub进行远程调试这种调试策略需要target及development机器才能完成调试任务
II、将虚拟机技术与调试工具相结合使Linux内核在虚拟机中运行从而利用调试器对内核进行调试这种策略需要制作适合在虚拟机中运行系统内核
由不同调试策略决定了进行调试时不同工作原理同时也形成了各种调试方法不同软硬件需求和各自特点
另外需要说明是内核调试能力掌握很大程度上取决于经验和对整个操作系统深入理解对系统内核全面深入理解将能在很大程度上加快对Linux系统内核开发和调试
对系统内核调试技术和方法绝不止上面介绍所涉及内容这里只是介绍了些经常看到和听到方法在Linux内核向前发展同时内核调试技术也在不断进步希望以上介绍些方法能对读者开发和学习Linux有所帮助
参考资料
[1] http://oss.sgi.com/projects/kdb/
[2] http://www-128.ibm.com/developerworks/cn/linux/sdk/l-debug/index.html
[3] http://www-128.ibm.com/developerworks/cn/linux/l-kdbug/
[4] http://www-128.ibm.com/developerworks/cn/linux/l-kprobes.html
[5] http://kgdb.linsyssoft.com/downloads.htm
[6]&nbsftp://166.111.68.183
[7] http://adam.kaist.ac.kr/~hschoe/dow...ols-20040427.sh
[8] http://www.uclinux.org/pub/uClinux/dist/
[9] http://adam.kaist.ac.kr/~hschoe/dow...5-hsc2.patch.gz
[10] http:// www.kernel.org
[11] http://user-mode-linux.sourceforge.net/
[12] http://www-128.ibm.com/developerworks/cn/linux/l-skyeye/part1/
[13] http://www-128.ibm.com/developerworks/cn/views/linux/tutorials.jsp?cv_doc_id=84978
参考文献
[1]Robert Love Linux kernel development机械工业出版社
[2]陈渝源代码开发嵌入式系统软件分析与实践北京航空航天大学出版社
[3]Alessandro Rubini Linux device driver 2se Edition O'Reilly
[4]Jonathan Corbet Linux device driver 3rd Edition O'Reilly
[5]李善平 Linux内核源代码分析大全机械工业出版社 

Tags:  linuxeden linux内核调试 内核调试器 内核调试

延伸阅读

最新评论

发表评论