⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 linux调试技术介绍.txt

📁 Linux网络编程
💻 TXT
📖 第 1 页 / 共 4 页
字号:
在/proc/ksyms中。由于这两个文件的格式不同,使用了sed和awk将所有的文本行转换
为一种合适的格式。然后对这张表排序,去除重复部分,这样ksymoops就可以用
了。

如果我们重新运行ksymoops,它从新的符号表中截取出如下信息:

(代码)

正如你所见到的,当跟踪与模块有关的oops消息时,创建一个修订的系统表是很有
助益的:现在ksymoops能够对指令指针解码并完成整个调用轨迹了。还要注意,显
式反汇编码的格式和objdump所使用的格式一样。objdump也是一个功能强大的工具;
如果你需要查看失败前的指令,你调用命令objdump �d faulty.o。

在文件的汇编列表中,字串faulty_read+45/60标记为失效行。有关objdump的更多的信息
和它的命令行选项可以参见该命令的手册。

即便你构建了你自己的修订版符号表,上面提到的有关调用轨迹的问题仍然存在:
虽然0x202xxxx指针被解码了,但仍然是假的。

学会解码oops消息需要一定的经验,但是确实值得一做。用来学习的时间很快就会
有所回报。不过由于机器指令的Unix语法与Intel语法不同,唯一的问题在于从哪获得
有关汇编语言的文档;尽管你了解PC汇编语言,但你的经验都是用Intel语法的编程
获得的。在参考书目中,我给一些有所补益的书籍。

使用oops

使用ksymoops有些繁琐。你需要C++编译器编译它,你还要构建你自己的符号表来充
分发挥程序的能力,你还要将原始消息和ksymoops输出合在一起组成可用的信息。

如果你不想找这么多麻烦,你可以使用oops程序。oops在本书的O’Reilly FTP站点给出
的源码中。它源自最初的ksymoops工具,现在它的作者已经不维护这个工具了。oops
是用C语言写成的,而且直接查看/proc/ksyms而无需用户每次加载模块后构建新的符
号表。

该程序试图解码所有的处理器寄存器并堆栈轨迹解析为符号值。它的缺点是,它要
比ksymoops罗嗦些,但通常你所有的信息越多,你发现错误也就越快。oops的另一个
优点是,它可以解析x86,Alpha和Sparc的oops消息。与内核源码相同,这个程序也按GPL
发行。

oops产生的输出与ksymoops的类似,但是更完全。这里给出前一个oops输出的开始部
分�由于在这个oops消息中堆栈没保存什么有用的东西,我不认为应该显示整个堆
栈轨迹:

(代码)

当你调试“真正的”模块(faulty太短了,没有什么意义)时,将寄存器和堆栈解码
是非常有益的,而且如果被调试的所有模块符号都开放出来时更有帮助。在失效
时,处理器寄存器一般不会指向模块的符号,只有当符号表开放给/proc/ksyms时,你
才能输出中标别它们。

我们可以用一下步骤制作一张更完整的符号表。首先,我们不应在模块中声明静态
变量,否则我们就无法用insmod开放它们了。第二,如下面的截取自scull的init_module
函数的代码所示,我们可以用#ifdef SCULL_DEBUG或类似的宏屏蔽register_symtab调用。

(代码)

我们在第2章“编写和运行模块”的“注册符号表”一节中已经看到了类似内容,
那里说,如果模块不注册符号表,所有的全局符号就都开放。尽管这一功能仅在SCULL_DEBUG
被激活时才有效,为了避免内核中的名字空间污染,所有的全局符号有合适的前缀
(参见第2章的“模块与应用程序”一节)。

使用klogd

klogd守护进程的近期版本可以在oops存放到记录文件前对oops消息解码。解码过程只
由版本1.3或更新版本的守护进程完成,而且只有将-k /usr/src/linux/System.map做为参数传
递给守护进程时才解码。(你可以用其他符号表文件代替System.map)

有新的klogd给出的faulty的oops如下所示,它写到了系统记录中:

(代码)

我想能解码的klogd对于调试一般的Linux安装的核心来说是很好的工具。由klogd解码
的消息包括大部分ksymoops的功能,而且也要求用户编译额外的工具,或是,当系
统出现故障时,为了给出完整的错误报告而合并两个输出。当oops发生在内核时,
守护进程还会正确地解码指令指针。它并不反汇编代码,但这不是问题,当错误报
告给出消息时,二进制数据仍然存在,可以离线反汇编代码。

守护进程的另一个功能就是,如果符号表版本与当前内核不匹配,它会拒绝解析符
号。如果在系统记录中解析出了符号,你可以确信它是正确的解码。

然而,尽管它对Linux用户很有帮助,这个工具在调试模块时没有什么帮助。我个人
没有在开放软件的电脑里使用解码选项。klogd的问题是它不解析模块中的符号;因
为守护进程在程序员加载模块前就已经运行了,即使读了/proc/ksyms也不会有什么帮
助。记录文件中存在解析后的符号会使oops和ksymoops混淆,造成进一步解析的困
难。

如果你需要使用klogd调试你的模块,最新版本的守护进程需要加入一些新的特殊支
持,我期待它的完成,只要给内核打一个小补丁就可以了。

系统挂起

尽管内核代码中的大多数错误仅会导致一个oops消息,有时它们困难完全将系统挂
起。如果系统挂起了,没有消息能够打印出来。例如,如果代码遇到一个死循环,
内核停止了调度过程,系统不会再响应任何动作,包括魔法键Ctrl-Alt-Del组合。

处理系统挂起有两个选择――一个是防范与未然,另一个就是亡羊补牢,在发生挂
起后调试代码。

通过在策略点上插入schedule调用可以防止死循环。schedule调用(正如你所猜想到
的)调用调度器,因此允许其他进程偷取当然进程的CPU时间。如果进程因你的驱
动程序中的错误而在内核空间循环,你可以在跟踪到这种情况后杀掉这个进程。

在驱动程序代码中插入schedule调用会给程序员带来新的“问题”:函数,,以及调用
轨迹中的所有函数,必须是可重入的。在正常环境下,由于不同的进程可能并发地
访问设备,驱动程序做为整体是可重入的,但由于Linux内核是不可抢占的,不必每
个函数都是可重入的。但如果驱动程序函数允许调度器中断当前进程,另一个不同
的进程可能会进入同一个函数。如果schedule调用仅在调试期间打开,如果你不允
许,你可以避免两个并发进程访问驱动程序,所以并发性倒不是什么非常重要的问
题。在介绍阻塞型操作时(第5章的“写可重入代码”)我们再详细介绍并发性问
题。

如果要调试死循环,你可以利用Linux键盘的特殊键。默认情况下,如果和修饰键一
起按了PrScr键(键码是70),系统会向当前控制台打印有关机器状态的有用信息。
这一功能在x86和Alpha系统都有。Linux的Sparc移植也有同样的功能,但它使用了标记
为“Break/Scroll Lock”的键(键码是30)。

每一个特殊函数都有一个名字,并如下面所示都有一个按键事件与之对应。组合键
之后的括号里是函数名。

Shift-PrScr(Show_Memory)

打印若干行关于内存使用的信息,尤其是有关缓冲区高速缓存的使用情况。

Control-PrScr(Show_State)

针对系统里的每一个处理器打印一行信息,同时还打印内部进程树。对当前进程进
行标记。

RightAlt-PrScr(Show_Registers)

由于它可以打印按键时的处理器寄存器内容,它是系统挂起时最重要的一个键了。
如果有当前内核的系统表的话,查看指令计数器以及它如何随时间变化,对了解代
码在何处循环非常有帮助。

如果想将这些函数映射到不同的键上,每一个函数名都可以做为参数传递给loadkeys。
键盘映射表可以任意修改(这是“策略无关的”)。

如果console_loglevel足够到的话,这些函数打印的消息会出现在控制台上。如果不是
你运行了一个旧klogd和一个新内核的话,默认记录级应该足够了。如果没有出现消
息,你可以象以前说的那样提升记录级。“足够高”的具体值与你使用的内核版本
有关。对于Linux 2.0或更新的版本来说是5。

即便当系统挂起时,消息也会打印到控制台上,确认记录级足够高是非常重要的。
消息是在产生中断时生成的,因此即便有错的进程不释放CPU也可以运行――当
然,除非中断被屏蔽了,不过如果发生这种情况既不太可能也非常不幸。

有时系统看起来象是挂起了,但其实不是。例如,如果键盘因某种奇怪的原因被锁
住了就会发生这种情况。这种假挂起可以通过查看你为探明此种情况而运行的程序
输出来判断。我有一个程序会不断地更新LED显示器上的时钟,我发现这个对于验
证调度器尚在运行非常有用。你可以不必依赖外部设备就可以检查调度器,你可以
实现一个程序让键盘LED闪烁,或是不断地打开关闭软盘马达,或是不断触动扬声
器――不过我个人认为,通常的蜂鸣声很烦人,应该尽量避免。看看ioctl命令KDMKTONE。O
’Reilly FTP站点上的例子程序(misc-progs/heartbeat.c)中有一个是让键盘LED不断闪烁
的。

如果键盘不接收输入了,最佳的处理手段是从网络登录在系统中,杀掉任何违例的
进程,或是重新设置键盘(用kdb_mode -a)。然而,如果你没有网络可用来恢复的
话,发现系统挂起是由键盘锁死造成的一点儿用也没有。如果情况确实是这样,你
应该配置一种替代输入设备,至少可以保证正常地重启系统。对于你的计算机来
说,关闭系统或重启比起所谓的按“大红钮”要更方便一些,至少它可以免去长时
间地fsck扫描磁盘。

这种替代输入设备可以是游戏杆或是鼠标。在sunsite.edu.cn上有一个游戏杆重启守护
进程,gpm-1.10或更新的鼠标服务器可以通过命令行选项支持类似的功能。如果键盘
没有锁死,但是却误入“原始”模式,你可以看看kdb包中文档介绍的一些小技
巧。我建议最好在问题出现以前就看看这些文档,否则就太晚了。另一种可能是配
置gpm-root菜单,增添一个“reboot”或“reset keyboard”菜单项;gpm-root一个响应控制
鼠标事件的守护进程,它用来在屏幕上显示菜单和执行所配置的动作。

最好,你会可以按“留意安全键”(SAK),一个用于将系统恢复为可用状态的特
殊键。由于不是所有的实现都能用,当前Linux版本的默认键盘表中没有为此键特设
一项。不过你还是可以用loadkeys将你的键盘上的一个键映射为SAK。你应该看看drivers/char
目录中的SAK实现。代码中的注释解释了为什么这个键在Linux 2.0中不是总能工作,
这里我就不多说了。

不过,如果你运行版本2.1.9或是更新的版本,你就可以使用非常可靠地留意安全键
了。此外,2.1.43及后续版本内核还有一个编译选项选择是否打开“SysRq魔法键”;
我建议你看一看drivers/char/sysrq.c中的代码并使用这项新技术。

如果你的驱动程序真的将系统挂起了,而且你有不知道在哪插入schedule调用,最佳
的处理方法就是加一些打印消息,并将它们打印到控制台上(通过修改console_loglevel

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -