首页 »
嵌入式开发 » 内核调试:Linux系统内核的调试(引自www.linuxeden.com,作者ott)
Rss订阅
内核调试:Linux系统内核的调试(引自www.linuxeden.com,作者ott)
=tf width="98%" align=center border=0>Linux 系统内核
调试
调试是软件开发过程中
个必不可少
环节
在 Linux 内核开发
过程中也不可避免地会面对如何调试内核
问题
但是
Linux 系统
开发者出于保证内核代码正确性
考虑
不愿意在 Linux 内核源代码树中加入
个调试器
他们认为内核中
调试器会误导开发者
从而引入不良
修正[1]
所以对 Linux 内核进行调试
直是个令内核
员感到棘手
问题
调试工作
艰苦性是内核级
开发区别于用户级开发![](/icons/30922de.gif)
个显著特点![](/icons/30922dou2.gif)
尽管缺乏
种内置
调试内核
有效方法
但是 Linux 系统在内核发展
过程中也逐渐形成了
些监视内核代码和
跟踪
技术
同时
许多
补丁
应运而生
它们为标准内核附加了内核调试
支持
尽管这些补丁有些并不被 Linux 官方组织认可
但他们确实功能完善
十分强大
调试内核问题时
利用这些工具与方法跟踪内核执行情况
并查看其内存和数据结构将是非常有用![](/icons/30922de.gif)
![](/icons/30922dou2.gif)
本文将首先介绍 Linux 内核上![](/icons/30922de.gif)
些内核代码监视和
跟踪技术
这些调试和跟踪方法因所要求
使用环境和使用方法而各有不同
然后重点介绍三种 Linux 内核
源代码级
调试方法![](/icons/30922dou2.gif)
1. Linux 系统内核级软件
调试技术
pr
k
是调试内核代码时最常用![](/icons/30922de.gif)
种技术
在内核代码中
特定位置加入pr
k
调试![](/icons/30922diaoyong.gif)
可以直接把所关心
信息打打印到屏幕上
从而可以观察![](/icons/30922chengxu.gif)
执行路径和所关心
变量、指针等信息
Linux 内核调试器(Linux kernel debugger
kdb)是 Linux 内核
补丁
它提供了
种在系统能运行时对内核内存和数据结构进行检查
办法
Oops、KDB在文章掌握 Linux 调试技术有详细介绍
大家可以参考
Kprobes 提供了
个强行进入任何内核例程
并从中断处理器无干扰地收集信息
接口
使用 Kprobes 可以轻松地收集处理器寄存器和全局数据结构等调试信息
而无需对Linux内核频繁编译和启动
具体使用方法
请参考使用 Kprobes 调试内核![](/icons/30922dou2.gif)
以上介绍了进行Linux内核调试和跟踪时
常用技术和方法
当然
内核调试与跟踪
方法还不止以上提到
这些
这些调试技术![](/icons/30922de.gif)
个共同
特点在于
他们都不能提供源代码级
有效
内核调试手段
有些只能称之为
跟踪技术
因此这些方法都只能提供有限
调试能力
下面将介绍三种实用
源代码级
内核调试方法![](/icons/30922dou2.gif)
2. 使用KGDB构建Linux内核调试环境
kgdb 提供了
种使用 gdb调试 Linux 内核
机制
使用KGDB可以象调试普通
应用
那样
在内核中进行设置断点、检查变量值、单步跟踪
运行等操作
使用KGDB调试时需要两台机器![](/icons/30922dou.gif)
台作为开发机(Development Machine),另
台作为目标机(Target Machine)
两台机器之间通过串口或者以太网口相连
串口连接线是
根RS-232接口
电缆
在其内部两端
第2脚(TXD)与第3脚(RXD)交叉相连
第7脚(接地脚)直接相连
调试过程中
被调试
内核运行在目标机上
gdb调试器运行在开发机上![](/icons/30922dou2.gif)
目前
kgdb发布支持i386、x86_64、32-bit PPC、SPARC等几种体系结构
调试器
有关kgdb补丁
下载地址见参考资料[4]![](/icons/30922dou2.gif)
2.1 kgdb
调试原理
安装kgdb调试环境需要为Linux内核应用kgdb补丁
补丁实现
gdb远程调试所需要
功能包括命令处理、陷阱处理及串口通讯3个主要
部分
kgdb补丁
主要作用是在Linux内核中添加了
个调试Stub
调试Stub是Linux内核中![](/icons/30922de.gif)
小段代码
提供了运行gdb
开发机和所调试内核之间![](/icons/30922de.gif)
个媒介
gdb和调试stub之间通过gdb串行协议进行通讯
gdb串行协议是
种基于消息
ASCII码协议
包含了各种调试命令
当设置断点时
kgdb负责在设置断点
指令前增加
条trap指令
当执行到断点时控制权就转移到调试stub中去
此时
调试stub
任务就是使用远程串行通信协议将当前环境传送给gdb
然后从gdb处接受命令
gdb命令告诉stub下
步该做什么
当stub收到继续执行
命令时
将恢复![](/icons/30922chengxu.gif)
运行环境
把对CPU
控制权重新交还给内核![](/icons/30922dou2.gif)
2.2 Kgdb
安装与设置
下面我们将以Linux 2.6.7内核为例详细介绍kgdb调试环境
建立过程![](/icons/30922dou2.gif)
2.2.1软硬件准备
以下软硬件配置取自笔者进行试验
系统配置情况:
kgdb补丁
版本遵循如下命名模式:Linux-A-kgdb-B
其中A表示Linux
内核版本号
B为kgdb
版本号
以试验使用
kgdb补丁为例
linux内核
版本为linux-2.6.7
补丁版本为kgdb-2.2![](/icons/30922dou2.gif)
物理连接好串口线后
使用以下命令来测试两台机器之间串口连接情况
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"![](/icons/30922dou2.gif)
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给出
说明
执行对应体系结构
补丁![](/icons/30922chengxu.gif)
由于试验在i386体系结构上完成
所以只需要安装
下补丁:core-lite.patch、i386-lite.patch、 8250.patch、eth.patch、core.patch、i386.patch
应用补丁文件时
请遵循kgdb软件包内series文件所指定
顺序
否则可能会带来预想不到
问题
eth.patch文件是选择以太网口作为调试
连接端口时需要运用
补丁
![](/icons/30922dou2.gif)
应用补丁
命令如下所示:
[root@lisl tmp]#patch -p1 <../linux-2.6.7-kgdb-2.2/core-lite.patch
如果内核正确
那么应用补丁时应该不会出现任何问题(不会产生*.rej文件)
为Linux内核添加了补丁之后
需要进行内核
配置
内核
配置可以按照你
习惯选择配置Linux内核
任意
种方式![](/icons/30922dou2.gif)
[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选项![](/icons/30922dou2.gif)
不过
当选择"Kernel debugging->Compile the kernel with debug info"选项后配置系统将自动打开调试选项
另外
选择"kernel debugging with remote gdb"后
配置系统将自动打开"Compile the kernel with debug info"选项![](/icons/30922dou2.gif)
内核编译完成后
使用scp命令进行将相关文件拷贝到target机上(当然也可以使用其它
网络工具
如rcp)![](/icons/30922dou2.gif)
[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机器之后
需要配置系统引导![](/icons/30922chengxu.gif)
加入内核
启动选项
以下是kgdb内核引导参数
说明:
如表中所述
在kgdb 2.0版本之后内核
引导参数已经与以前
版本有所不同
使用grub引导
时
直接将kgdb参数作为内核vmlinuz
引导参数
下面给出引导器
配置示例![](/icons/30922dou2.gif)
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作为引导器时
配置示例![](/icons/30922dou2.gif)
image=/boot/vmlinuz-2.6.7-kgdb
label=kgdb
read-only
root=/dev/hda3
append="gdb gdbttyS=1 gdbbaud=115200"
保存好以上配置后重新启动计算机
选择启动带调试信息
内核
内核将在短暂
运行后在创建init内核线程之前停下来
打印出以下信息
并等待开发机
连接![](/icons/30922dou2.gif)
Waiting for connection from remote gdb...
在开发机上执行:
gdb
file vmlinux
remotebaud 115200
target remote /dev/ttyS0
其中vmlinux是指向源代码目录下编译出来
Linux内核文件
链接
它是没有经过压缩
内核文件
gdb
从该文件中得到各种符号地址信息![](/icons/30922dou2.gif)
这样
就与目标机上
kgdb调试接口建立了联系![](/icons/30922dou2.gif)
旦建立联接之后
对Linux内
调试工作与对普通
运用![](/icons/30922chengxu.gif)
调试就没有什么区别了
任何时候都可以通过键入ctrl+c打断目标机
执行
进行具体
调试工作![](/icons/30922dou2.gif)
在kgdb 2.0之前
版本中
编译内核后在arch/i386/kernel目录下还会生成可执行文件gdbstart
将该文件拷贝到target机器
/boot目录下
此时无需更改内核
启动配置文件
直接使用命令:
[root@lisl boot]#gdbstart -s 115200 -t /dev/ttyS0
可以在KGDB内核引导启动完成后建立开发机与目标机之间
调试联系![](/icons/30922dou2.gif)
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/
其他
过程与使用串口作为连接端口时
设置过程相同![](/icons/30922dou2.gif)
注意:尽管可以使用以太网口作为kgdb
调试端口
使用串口作为连接端口更加简单易行
kgdb项目组推荐使用串口作为调试端口![](/icons/30922dou2.gif)
2.2.4 模块
调试方法
内核可加载模块
调试具有其特殊性
由于内核模块中各段
地址是在模块加载进内核
时候才最终确定![](/icons/30922de.gif)
所以develop机
gdb无法得到各种符号地址信息
所以
使用kgdb调试模块所需要解决![](/icons/30922de.gif)
个问题是
需要通过某种方法获得可加载模块
最终加载地址信息
并把这些信息加入到gdb环境中![](/icons/30922dou2.gif)
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中,这样就可以进行模块功能
测试了![](/icons/30922dou2.gif)
(gdb) Add-symbol-file hello.o 0xc88d8060 -s .data 0xc88d80a0 -s
.rodata 0xc88d80a0 -s .bss 0x c88d833c
这种方法也存在
定
不足
它不能调试模块
化
代码![](/icons/30922dou.gif)
此时模块
化代码已经执行过了
而如果不执行模块
加载又无法获得模块插入地址
更不可能在模块
化之前设置断点了
对于这种调试要求可以采用以下替代方法![](/icons/30922dou2.gif)
在target 机上用上述方法得到模块加载
地址信息
然后再用rmmod卸载模块
在development机上将得到
模块地址信息导入到gdb环境中
在内核代码![](/icons/30922de.gif)
![](/icons/30922diaoyong.gif)
化代码之前设置断点
这样
在target机上再次插入模块时
代码将在执行模块
化之前停下来
这样就可以使用gdb命令调试模块
化代码了![](/icons/30922dou2.gif)
另外
种调试模块
化![](/icons/30922hanshu.gif)
方法是:当插入内核模块时
内核模块机制将![](/icons/30922diaoyong.gif)
sys_init_module(kernel/modle.c)执行对内核模块![](/icons/30922de.gif)
化
该
将
所插入模块![](/icons/30922de.gif)
化![](/icons/30922hanshu.gif)
![](/icons/30922dou2.gif)
代码片断如下:
…… ……
(mod->init != NULL)
ret = mod->init
;
…… ……
在该语句上设置断点
也能在执行模块
化之前停下来![](/icons/30922dou2.gif)
II、在Linux 2.6.x内核中
内核模块调试方法
Linux 2.6之后
内核中
由于module-init-tools工具
更改
insmod命令不再支持-m参数
只有采取其他
方法来获取模块加载到内核
地址
通过分析ELF文件格式
我们知道
中各段
意义如下:
.text(代码段):用来存放可执行文件
操作指令
也就是说是它是可执行
在内存种
镜像![](/icons/30922dou2.gif)
.data(数据段):数据段用来存放可执行文件中已
化全局变量
也就是存放
静态分配
变量和全局变量![](/icons/30922dou2.gif)
.bss(BSS段):BSS段包含了
中未
化全局变量
在内存中 bss段全部置零![](/icons/30922dou2.gif)
.rodata(只读段):该段保存着只读数据
在进程映象中构造不可写
段![](/icons/30922dou2.gif)
通过在模块
化
中放置
下代码
我们可以很容易地获得模块加载到内存中
地址![](/icons/30922dou2.gif)
……
bss_var;
hello_init(void)
{
pr
k(KERN_ALERT "Text location .text(Code Segment):%p\n",hello_init);
data_var=0;
pr
k(KERN_ALERT "Data Location .data(Data Segment):%p\n",&data_var);
pr
k(KERN_ALERT "BSS Location: .bss(BSS Segment):%p\n",&bss_var);
……
}
Module_init(hello_init);
这里
通过在模块![](/icons/30922de.gif)
化
中添加
段简单![](/icons/30922de.gif)
![](/icons/30922chengxu.gif)
使模块在加载时打印出在内核中
加载地址
.rodata段
地址可以通过执行命令readelf -e hello.ko
取得.rodata在文件中
偏移量并加上段
align值得出![](/icons/30922dou2.gif)
为了使读者能够更好地进行模块
调试
kgdb项目还发布了
些脚本
能够自动探测模块
插入并自动更新gdb中模块
符号信息
这些脚本![](/icons/30922chengxu.gif)
工作原理与前面解释
工作过程相似
更多
信息请阅读参考资料[4]![](/icons/30922dou2.gif)
2.2.5 硬件断点
kgdb提供对硬件调试寄存器
支持
在kgdb中可以设置三种硬件断点:执行断点(Execution Breakpo
)、写断点(Write Breakpo
)、访问断点(Access Breakpo
)但不支持I/O访问
断点
目前
kgdb对硬件断点
支持是通过宏来实现![](/icons/30922de.gif)
最多可以设置4个硬件断点
这些宏
用法如下:
在有些情况下
硬件断点
使用对于内核
调试是非常方便![](/icons/30922de.gif)
有关硬件断点
定义和具体
使用说明见参考资料[4]
![](/icons/30922dou2.gif)
2.3.在VMware中搭建调试环境
kgdb调试环境需要使用两台微机分别充当development机和target机
使用VMware后我们只使用
台计算机就可以顺利完成kgdb调试环境
搭建
以windows下
环境为例
创建两台虚拟机![](/icons/30922dou.gif)
台作为开发机![](/icons/30922dou.gif)
台作为目标机![](/icons/30922dou2.gif)
2.3.1虚拟机之间
串口连接
虚拟机中
串口连接可以采用两种方法![](/icons/30922dou2.gif)
种是指定虚拟机
串口连接到实际
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进行调试
硬件要求
也简化了建立调试环境
过程![](/icons/30922dou2.gif)
2.3.2 VMware
使用技巧
VMware 虚拟机是比较占用资源![](/icons/30922de.gif)
尤其是象上面那样在Windows中使用两台虚拟机
因此
最好为系统配备512M以上
内存
每台虚拟机至少分配128M
内存
这样
硬件要求
对目前主流配置
PC而言并不是过高
要求
出于系统性能
考虑
在VMware中尽量使用
界面进行调试工作
同时
Linux 系统默认情况下开启了sshd服务
建议使用SecureCRT登陆到Linux进行操作
这样可以有较好
用户使用界面![](/icons/30922dou2.gif)
2.3.3 在Linux下
虚拟机中使用kgdb
对于在Linux下面使用VMware虚拟机
情况
笔者没有做过实际
探索
从原理上而言
只需要在Linux下只要创建
台虚拟机作为target机
开发机
工作可以在实际
Linux环境中进行
搭建调试环境
过程与上面所述
过程类似
由于只需要创建
台虚拟机
所以使用Linux下
虚拟机搭建 kgdb调试环境对系统性能
要求较低
(vmware已经推出了Linux下
版本)还可以在development机上配合使用
些其他
调试工具
例如功能更强大
cgdb、图形界面
DDD调试器等
以方便内核
调试工作![](/icons/30922dou2.gif)
2.4 kgdb![](/icons/30922de.gif)
些特点和不足
使用kgdb作为内核调试环境最大
不足在于对kgdb硬件环境
要求较高
必须使用两台计算机分别作为target和development机
尽管使用虚拟机
方法可以只用
台PC即能搭建调试环境
但是对系统其他方面
性能也提出了
定
要求
同时也增加了搭建调试环境时复杂程度
另外
kgdb内核
编译、配置也比较复杂
需要
定
技巧
笔者当时做
时候也是费了很多周折
当调试过程结束后时
还需要重新制作所要发布
内核
使用kgdb并不能进行全程调试
也就是说kgdb并不能用于调试系统
开始![](/icons/30922de.gif)
化引导过程![](/icons/30922dou2.gif)
不过
kgdb是
个不错
内核调试工具
使用它可以进行对内核
全面调试
甚至可以调试内核
中断处理![](/icons/30922chengxu.gif)
如果在
些图形化
开发工具
帮助下
对内核
调试将更方便![](/icons/30922dou2.gif)
3. 使用SkyEye构建Linux内核调试环境
SkyEye 是
个开源软件项目(OPenSource Software),SkyEye项目
目标是在通用
Linux和Windows平台上模拟常见
嵌入式计算机系统
SkyEye实现了
个指令级
硬件模拟平台
可以模拟多种嵌入式开发板
支持多种CPU指令集
SkyEye
核心是 GNU
gdb 项目
它把gdb和 ARM Simulator很好地结合在了
起
加入ARMulator
功能之后
它就可以来仿真嵌入式开发板
在它上面不仅可以调试硬件驱动
还可以调试操作系统
Skyeye项目目前已经在嵌入式系统开发领域得到了很大
推广![](/icons/30922dou2.gif)
3.1 SkyEye
安装和μcLinux内核编译
3.1.1 SkyEye
安装
SkyEye
安装不是本文要介绍
重点
目前已经有大量
资料对此进行了介绍
有关SkyEye
安装与使用
内容请查阅参考资料[11]
由于skyeye面目主要用于嵌入式系统领域
所以在skyeye上经常使用
是μcLinux系统
当然使用Linux作为skyeye上运行
系统也是可以![](/icons/30922de.gif)
由于介绍 μcLinux 2.6在skyeye上编译
相关资料并不多
所以下面进行详细介绍![](/icons/30922dou2.gif)
3.1.2 μcLinux 2.6.x
编译
要在SkyEye中调试操作系统内核
首先必须使被调试内核能在SkyEye所模拟
开发板上正确运行
因此
正确编译待调试操作系统内核并配置 SkyEye是进行内核调试
第
步
下面我们以SkyEye模拟基于Atmel AT91X40
开发板
并运行μcLinux 2.6为例介绍SkyEye
具体调试方法![](/icons/30922dou2.gif)
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用户来执行![](/icons/30922dou2.gif)
[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变量中![](/icons/30922dou2.gif)
II、制作μcLinux内核
得到μcLinux发布包![](/icons/30922de.gif)
个最容易
方法是直接访问uClinux.org站点[7]
该站点发布
内核版本可能不是最新![](/icons/30922de.gif)
但你能找到
个最新
μcLinux补丁以及找
个对应
Linux内核版本来制作
个最新
μcLinux内核
这里
将使用这种方法来制作最新
μcLinux内核
目前(笔者记录编写此文章时)
所能得到
发布包
最新版本是uClinux-dist.20041215.tar.gz![](/icons/30922dou2.gif)
下载uClinux-dist.20041215.tar.gz
文件
下载地址请参见[7]![](/icons/30922dou2.gif)
下载linux-2.6.9-hsc0.patch.gz
文件
下载地址请参见[8]![](/icons/30922dou2.gif)
下载linux-2.6.9.tar.bz2
文件
下载地址请参见[9]![](/icons/30922dou2.gif)
现在我们得到了整个
linux-2.6.9源代码
以及所需
内核补丁
请准备
个有2GB空间
目录里来完成以下制作μcLinux内核
过程![](/icons/30922dou2.gif)
[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![](/icons/30922dou2.gif)
[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内核
目![](/icons/30922de.gif)
这里没有生成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发布时提供![](/icons/30922de.gif)
个配置文件
存放于目录linux-2.6.x /arch/armnommu/configs/中![](/icons/30922dou2.gif)
[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文件
为其打开调试开关
方法如下:![](/icons/30922dou2.gif)
CFLAGS
-g
最容易出现
问题是找不到arm-uclinux-gcc命令![](/icons/30922de.gif)
![](/icons/30922cuowu.gif)
主要原因是PATH变量中没有包含arm-uclinux-gcc命令所在目录
在arm-linux-gcc
缺省安装情况下
它
安装目录是/root/bin/arm-linux-tool/
使用以下命令将路径加到PATH环境变量中![](/icons/30922dou2.gif)
Export PATH=$PATH:/root/bin/arm-linux-tool/bin
IV、根文件系统
制作
Linux内核在启动
时
最后操作之
是加载根文件系统
根文件系统中存放了嵌入式系统使用
所有应用
、库文件及其他
些需要用到
服务
出于文章篇幅
考虑
这里不打算介绍根文件系统
制作方法
读者可以查阅
些其他
相关资料
值得注意
是
由配置文件skyeye.conf指定了装载到内核中
根文件系统![](/icons/30922dou2.gif)
3.2 使用SkyEye调试
编译完μcLinux内核后
就可以在SkyEye中调试该ELF执行文件格式
内核了
前面已经说过利用SkyEye调试内核与使用gdb调试运用![](/icons/30922chengxu.gif)
方法相同![](/icons/30922dou2.gif)
需要提醒读者
是
SkyEye
配置文件-skyeye.conf记录了模拟
硬件配置和模拟执行行为
该配置文件是SkyEye系统中
个及其重要
文件
很多
和异常情况
发生都和该文件有关
在安装配置SkyEye出错时
请首先检查该配置文件然后再进行其他
工作
此时
所有
准备工作已经完成
就可以进行内核
调试工作了![](/icons/30922dou2.gif)
3.3使用SkyEye调试内核
特点和不足
在SkyEye 中可以进行对Linux系统内核
全程调试
由于SkyEye目前主要支持基于ARM内核
CPU
因此
般而言需要使用交叉编译工具编译待调试
Linux系统内核
另外
制作SkyEye中使用
内核编译、配置过程比较复杂、繁琐
不过
当调试过程结束后无需重新制作所要发布
内核![](/icons/30922dou2.gif)
SkyEye只是对系统硬件进行了
定程度上
模拟
所以在SkyEye与真实硬件环境相比较而言还是有
定
差距
这对
些与硬件紧密相关
调试可能会有
定
影响
例如驱动![](/icons/30922chengxu.gif)
调试
不过对于大部分软件
调试
SkyEye已经提供了精度足够
模拟了![](/icons/30922dou2.gif)
SkyEye
下
个目标是和eclipse结合
有了图形界面
能为调试和查看源码提供
些方便![](/icons/30922dou2.gif)
4. 使用UML调试Linux内核
User-mode Linux(UML)简单说来就是在Linux内运行
Linux
该项目是使Linux内核成为
个运行在 Linux 系统之上单独
、用户空间
进程
UML并不是运行在某种新
硬件体系结构之上
而是运行在基于 Linux 系统
接口所实现
虚拟机
正是由于UML是
个将Linux作为用户空间进程运行
特性
可以使用UML来进行操作系统内核
调试
有关UML
介绍请查阅参考资料[10]、[12]![](/icons/30922dou2.gif)
4.1 UML
安装与调试
UML
安装需要
台运行Linux 2.2.15以上
或者2.3.22以上
I386机器
对于2.6.8及其以前版本
UML
采用两种形式发布:
种是以RPM包
形式发布![](/icons/30922dou.gif)
种是以源代码
形式提供UML
安装
按照UML
说明
以RPM形式提供
安装包比较陈旧且会有许多问题
以二进制形式发布
UML包并不包含所需要
调试信息
这些代码在发布时已经做了程度不同
优化
所以
要想利用UML调试Linux系统内核
需要使用最新
UML patch代码和对应版本
Linux内核编译、安装UML
完成UML
补丁之后
会在arch目录下产生
个um目录
主要
UML代码都放在该目录下![](/icons/30922dou2.gif)
从2.6.9版本之后(包含2.6.9版本
Linux)
User-Mode Linux已经随Linux内核源代码树
起发布
它存放于arch/um目录下![](/icons/30922dou2.gif)
编译好UML
内核之后
直接使用gdb运行已经编译好
内核即可进行调试![](/icons/30922dou2.gif)
4.2使用UML调试系统内核
特点和不足
目前
用户模式 Linux 虚拟机也存在
定
局限性
由于UML虚拟机是基于Linux系统
接口
方式实现
虚拟机
所以用户模式内核不能访问主机系统上
硬件设备
因此
UML并不适合于调试那些处理实际硬件
驱动![](/icons/30922chengxu.gif)
不过
如果所编写
内核
不是硬件驱动
例如Linux文件系统、协议栈等情况
使用UML作为调试工具还是
个不错
选择![](/icons/30922dou2.gif)
5. 内核调试配置选项
为了方便调试和测试代码
内核提供了许多与内核调试相关
配置选项
这些选项大部分都在内核配置编辑器
内核开发(kernel hacking)菜单项中
在内核配置目录树菜单
其他地方也还有
些可配置
调试选项
下面将对他们作
定
介绍![](/icons/30922dou2.gif)
Page alloc debugging :CONFIG_DEBUG_PAGEALLOC:
不使用该选项时
释放
内存页将从内核地址空间中移出
使用该选项后
内核推迟移出内存页
过程
因此能够发现内存泄漏![](/icons/30922de.gif)
![](/icons/30922cuowu.gif)
![](/icons/30922dou2.gif)
Debug memory allocations :CONFIG_DEBUG_SLAB:
该打开该选项时
在内核执行内存分配之前将执行多种类型检查
通过这些类型检查可以发现诸如内核过量分配或者未
化等![](/icons/30922cuowu.gif)
内核将会在每次分配内存前后时设置
些警戒值
如果这些值发生了变化那么内核就会知道内存已经被操作过并给出明确
提示
从而使各种隐晦![](/icons/30922de.gif)
变得容易被跟踪![](/icons/30922dou2.gif)
Spinlock debugging :CONFIG_DEBUG_SPINLOCK:
打开此选项时
内核将能够发现spinlock未
化及各种其他![](/icons/30922de.gif)
,能用于排除
些死锁引起![](/icons/30922de.gif)
![](/icons/30922cuowu.gif)
![](/icons/30922dou2.gif)
Sleep-inside-spinlock checking:CONFIG_DEBUG_SPINLOCK_SLEEP:
打开该选项时
当spinlock
持有者要睡眠时会执行相应
检查
实际上即使
者目前没有睡眠
而只是存在睡眠
可能性时也会给出提示![](/icons/30922dou2.gif)
Compile the kernel with debug info :CONFIG_DEBUG_INFO:
打开该选项时
编译出
内核将会包含全部
调试信息
使用gdb时需要这些调试信息![](/icons/30922dou2.gif)
Stack utilization instrumentation :CONFIG_DEBUG_STACK_USAGE:
该选项用于跟踪内核栈
溢出![](/icons/30922cuowu.gif)
![](/icons/30922dou.gif)
个内核栈溢出![](/icons/30922cuowu.gif)
明显
现象是产生oops
却没有列出系统![](/icons/30922de.gif)
栈信息
该选项将使内核进行栈溢出检查
并使内核进行栈使用
统计![](/icons/30922dou2.gif)
Driver Core verbose debug messages:CONFIG_DEBUG_DRIVER:
该选项位于"Device drivers-> Generic Driver Options"下
打开该选项使得内核驱动核心产生大量
调试信息
并将他们记录到系统日志中![](/icons/30922dou2.gif)
Verbose SCSI error reporting (kernel size
12K) :CONFIG_SCSI_CONSTANTS:
该选项位于"Device drivers/SCSI device support"下
当SCSI设备出错时内核将给出详细
出错信息![](/icons/30922dou2.gif)
Event debugging:CONFIG_INPUT_EVBUG:
打开该选项时
会将输入子系统![](/icons/30922de.gif)
及所有事件都输出到系统日志中
该选项在产生了详细
输入报告
同时
也会导致
定
安全问题![](/icons/30922dou2.gif)
以上内核编译选项需要读者根据自己所进行
内核编程
实际情况
灵活选取
在使用以上介绍
三种源代码级
内核调试工具时![](/icons/30922dou.gif)
般需要选取CONFIG_DEBUG_INFO选项
以使编译
内核包含调试信息![](/icons/30922dou2.gif)
6. 总结
上面介绍了
些调试Linux内核
方法
特别是详细介绍了三种源代码级
内核调试工具
以及搭建这些内核调试环境
方法
读者可以根据自己
情况从中作出选择![](/icons/30922dou2.gif)
调试工具(例如gdb)
运行都需要操作系统
支持
而此时内核由于
些![](/icons/30922cuowu.gif)
代码而不能正确执行对系统
管理功能
所以对内核
调试必须采取
些特殊
方法进行
以上介绍
三种源代码级
调试方法
可以归纳为以下两种策略:
I、为内核增加调试Stub
利用调试Stub进行远程调试
这种调试策略需要target及development机器才能完成调试任务![](/icons/30922dou2.gif)
II、将虚拟机技术与调试工具相结合
使Linux内核在虚拟机中运行从而利用调试器对内核进行调试
这种策略需要制作适合在虚拟机中运行
系统内核![](/icons/30922dou2.gif)
由不同
调试策略决定了进行调试时不同
工作原理
同时也形成了各种调试方法不同
软硬件需求和各自
特点![](/icons/30922dou2.gif)
另外
需要说明
是内核调试能力
掌握很大程度上取决于经验和对整个操作系统
深入理解
对系统内核
全面深入
理解
将能在很大程度上加快对Linux系统内核
开发和调试![](/icons/30922dou2.gif)
对系统内核
调试技术和方法绝不止上面介绍所涉及
内容
这里只是介绍了
些经常看到和听到方法
在Linux内核向前发展
同时
内核
调试技术也在不断
进步
希望以上介绍![](/icons/30922de.gif)
些方法能对读者开发和学习Linux有所帮助![](/icons/30922dou2.gif)
参考资料
[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]&nbs
ftp://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内核源代码分析大全机械工业出版社
延伸阅读
最新评论