为了充分利用 Linux 操作系统,原始设备制造商(OEM)可选择与商用 Linux 供应商合作,或在内部增添工程能力。这两种模式都已被证明是成功的,但是每种做法都需各自的成本。
不管 OEM 如何选择,他们的工程师所使用的典型调试模型都是相同的:基于 GDB(GNU Debugger)的客户服务器环境。如图1所示,它描述了在调试目标时,附加并运行在每个 Linux 进程中的 GDBSERVER 例示。每个 GDBSERVER 都通过一个以太网端口与主机通信。
另外,需要特别注意的是,采用这种调试方法,标准 Linux内核被替换成一种“静态”版本。仅有少数例外,所有通过 KGDB的目标调试通信都被限制在 RS232 串行链路。
图1: 标准 Linux 调试模型。
这种方法给开发人员带来了另一个挑战,即要使用 Linux内核的测试版(instrumented version)。虽然这是可接受的默认Linux调试环境,但这种方法有一些很明确的局限性。例如,采用多进程组成的应用,需要多个 GDBSERVER 运行于有限的目标存储器上。这可能影响被调试目标的性能,在一些情况下目标性能可降低50%以上。
即使在所有的内核工具和通信通道均可用的最佳情形下,仍有一些区域的代码在这个调试范例下难以到达。图2中说明的“问题”区域给内核和应用程序开发人员提出了更多挑战。这些区域包括每个进程下大量的线程,以及代码独立和数据位置独立的内核可加载模块。尽管对于熟练的开发人员来说,有可能利用现有技术合成一个环境,来满足这些区域的调试需要,但是这种环境对用户非常不友好,且在负载下无法扩展。
图 2: “问题”区域。
接下来我们看看在Linux 内核可加载模块的例子中,模块加载时间调用的初始化程序由哪些部分组成。目前的调试范例表明加载了这些模块,然后利用调试器对其代码和数据偏移进行调整(手动和自动)。但是,这时模块的初始化代码已经执行了,无法在代码所在区域对问题进行调试。另一个使用情形涉及共享库,这经常无法由 GDBSERVER 或其他类似程序很好地处理。即使存在这些问题,许多工程师仍在采用 printf(用户空间)和 printk(内核空间)作为主要调试帮助。一些调试“工具”带来新的软件问题或可能掩盖现有的问题。