内核空间的代码(包括内核和模块)无法像应用程序那样使用gdb进行简单直观的调试.
代码调试一般有两种方法:
一种是通过调试器辅助调试, 如gdb, Visual C++的集成调试环境, JTAG仿真器的集成调试环境; 这种调试方法可以对代码进行单步跟踪, 所以一次运行基本上就可以排查一条运行路径; 也可以在程序出错时检查调用栈信息, 从而迅速定位问题所在; 所以这种调试方法尤其适合于初期排查代码中的初级/低级错误, 可以大量的节省调试时间, 尤其是对那些一次编译和部署需要耗费大量时间的代码工程, 节省的时间更是可观.
另一种是打印/记录程序运行状态来间接观察程序的运行逻辑是否正确. 这种方法需要仔细分析程序的状态信息, 而且对于一个bug, 往往需要多次增加打印/记录代码才能彻底定位问题真正所在, 所以调试效率非常低; 而且一次编译部署运行一般只能定位一个错误, 因为一旦程序运行出错, 后续的状态信息基本上也就没有分析价值了. 所以这种调试方法一般不用于初期排查代码中的初级/低级错误(一般只在没有合适的辅助调试器的情况下选用). 但这种方法广泛用于后期程序的性能瓶颈问题/时序相关问题的调试. 因为调试器辅助调试往往会影响程序的执行性能和代码的执行时序, 从而无法暴露性能问题/时序问题的真正所在, 所以不适合用于调试此类要求严格贴近程序真实运行环境才能复现的问题.
所以KGDB对于内核空间代码早期错误排查具有显著意义, 能够显著加快早期的调试进度.
以下是使用KGDB进行内核和模块代码调试的简略过程:
1.准备开发机和调试机. 调试机运行的内核和模块必须是开发机上编译出来的. 开发机和调试机之间通过串口连接; 如果内核和模块调试不影响网络, 也可以通过网口连接进行调试; 一般都通过串口连接进行调试.
2.参照
3.修改调试机上新安装的内核的启动参数, 增加" kgdboc=ttyS0,115200 kgdbwait"参数(参考<修改Linux内核启动参数>一文). 重新引导系统并选择刚才修改的内核启动项.
4.在开发机内核编译目录下运行:
gdb vmlinux
进入gdb界面后运行下面两条命令连接调试机:
set remotebaud 115200
target remote /dev/ttyS0
连接上调试机后其内核会被断在"wmb()"函数处, 键入"c"命令继续引导调试机内核
5.在调试机内:
1) "sudo chmod 222 /proc/sysrq_trigger" 使得任意用户可以写访问该文件(否则只有root用户可以写访问该文件, sudo都不行)
2) 任意时刻运行 "echo g > /proc/sysrq-trigger" 可使调试机进入kgdb中断状态; 开发机在kgdb中断状态下可对调试机上代码设置断点, 加载模块的符号文件等;
6.模块调试:
在开发机内:
"scp test.ko user@host:/directory/": 将开发机内编译好的模块拷贝到调试机内
在调试机内:
"insmod test.ko": 加载模块
"cat /sys/module/test/sections/.text": 获得模块在内核中的加载地址, 如"0xc0ae7000"
"echo g > /proc/sysrq-trigger": 使调试机进入中断状态
在开发机内:
gdb进入中断状态
"add-symbol-file /test/test.ko 0xc0ae7000": 加载模块符号文件; "0xc0ae7000"为调试机中模块在内核中的加载地址.
现在就可以像gdb调试应用程序一样调试内核模块了, 诸如设置断点; 模块进入断点后单步跟踪, 查看变量等等.
7.也可以在两台虚拟机上使用KGDB进行内核和模块的调试.
使用虚拟机进行调试部署更方便, 对硬件环境要求更低(一台主机即可, 但配置要高一些, 要能跑得动两台虚拟机).
先在开发机上将要调试的内核和模块编译好, 然后将开发机克隆一份为调试机, 这样可以简化开发机和调试机的部署过程;
参照
后续的步骤就和上面的描述一样了.
8.内核自2.6.22版本开始内嵌KGDB支持; 2.6.16之前版本需要在网上找kgdb的补丁; 2.6.16和2.6.22之间的版本在网上找不到合用的补丁; 对于低于2.6.22版本的内核空间代码, 在实际调试时, 如果不是特别复杂, 建议花点时间先移植到2.6.22以上的版本上并调试通过, 然后再回到实际要求内核版本上进行调试.
参考资料:
http://blog.csdn.net/jie12310/article/details/4564853