Linux kernel 调试方法总结

2024-07-09 19:26:58 浏览数 (2)

本文旨在介绍下几种常见的调试方法gdb、crash、kgdb and kdb 以及dynamic debug. 关于在 Linux 内核上使用debuggers,Linus Torvalds 长期以来对它们不太喜欢。简短地解释这种态度是,依赖调试器可能鼓励用权宜之计而非深思熟虑来解决问题,这会导致代码质量恶化。详细解释可以参考https://lwn.net/2000/0914/a/lt-debugger.php3

1. Linux 开发过程中会遇到的问题

Oops:错误报告,可能导致系统不稳定。

Kernel Crash:严重错误导致的系统完全崩溃。

Panic:严重错误,系统停止运行,通常需要重启。

OOM:内存耗尽,触发 OOM Killer。

1.1 Oops

定义:Oops 是 Linux 内核中的一种错误报告,它发生在内核检测到某些违反系统完整性的问题时。通常,这些问题包括非法内存访问、使用未初始化的内存、空指针解引用等。

影响:发生 Oops 后,内核会尝试继续运行,但系统的稳定性可能会受到影响,因为已经发生了内存损坏或其他严重的内核错误。

处理:内核通常会打印错误信息和内核调用栈到系统日志中,这有助于开发人员诊断和修复问题。

1.2 Kernel Crash

定义:Kernel Crash 指的是内核因为严重错误而完全失去功能的情况。它可能是由 Oops 导致的,也可能是由硬件故障、驱动程序错误或其他严重的内核级别问题引起的。

影响:当内核崩溃时,系统通常无法继续运行,需要重启。

处理:系统管理员需要查看崩溃转储或日志文件来分析原因,并采取措施防止未来发生类似崩溃。

1.3 Panic

定义:Kernel Panic 是一种特殊类型的错误,当内核检测到无法恢复的系统错误时触发。这通常表示系统的关键部分已损坏或遇到不可恢复的操作错误。

影响:Panic 通常会导致系统完全停止响应,需要重新启动。

处理:内核会在控制台输出 panic 相关的信息,包括错误描述和内核调用栈。系统通常需要重启才能恢复。

1.4 OOM (Out of Memory)

定义:OOM 错误发生在系统物理内存和交换空间都耗尽时,内核无法满足进程的内存分配请求。

影响:当发生 OOM 时,内核会触发 OOM Killer,尝试终止一个或多个进程来释放内存。

处理:内核选择杀死占用大量内存但相对不重要的进程。这个决定基于一系列启发式评分算法,以最小化对系统整体运行的影响。

2. Linux中常用的调试(debuggers)

2.1 gdb

代码语言:bash复制
gdb /boot/vmlinux /proc/kcore

当使用上面的命令的时候,实际上是进行的事后调试Post-mortem Debugging

其中第一个参数是当前运行的未压缩的内核。要调试的内核必须用-g选线编译并且获得调试信息。vmlinuz 是 vmlinux 的压缩版本,添加了自解压头部,使其可以自我解压并执行。

上面的命令需要在编译内核的时候打开下面的选项,其实也就是CONFIG_DEBUG_INFO

代码语言:txt复制
Kernel hacking  --->
    [*] Compile the kernel with debug info

/proc/kcore 是一个虚拟文件,提供了对当前运行系统物理内存的映射,其格式模仿了一个核心转储(core dump)。虽然 /proc/kcore 表现得像是一个内存转储文件,但它实际上是一个实时的视图,反映了当前系统的内存状态。

2.2 crash

使用 crash 工具来分析 Linux 内核崩溃是一个强大的方法,它可以帮助你理解内核崩溃时的状态,包括堆栈跟踪、内存状态、寄存器内容等。crash 主要用于分析由 kdump 服务生成的内核崩溃转储(vmcore 文件)。以下是如何设置和使用 crash 的步骤和示例:

代码语言:bash复制
sudo apt install kdump-tools crash
sudo systemctl enable kdump
sudo systemctl start kdump
#trigger crash for test purpose
echo c > /proc/sysrq-trigger
sudo crash /path/to/vmlinux /path/to/vmcore

在 crash 环境中,你可以执行多种命令来分析崩溃:

代码语言:bash复制
	bt:显示当前 CPU 或特定进程的堆栈跟踪。
	ps:显示系统中的进程状态。
	vm:查看内存信息。
	log:显示内核日志。
	例如,要获取当前环境的堆栈跟踪,可以运行:
    bt

假设系统因为某个驱动错误而崩溃,已经通过上述步骤获得了 vmcore 文件。现在,可以使用 crash 来分析驱动中可能的错误位置,检查在崩溃时的函数调用堆栈,以及查看那时的内存状态和变量。

通过这样的分析,可以精确地定位到问题发生的代码行,从而更有针对性地解决问题。此外,分析内核日志(通过 log 命令)可以帮助了解crash前发生了什么,这对于理解错误的上下文非常有帮助。

2.3 kgdb

KGDB 适合深入的远程内核调试,而 KDB 更适合快速本地访问和简单问题的诊断。两者的使用依赖于具体的调试需求和环境设置。

kgdb的使用步骤如下:

2.3.1 准备内核

代码语言:txt复制
Kernel hacking  --->
  <*> KGDB: kernel debugger  --->
    [*] KGDB: use kgdb over the serial console
    [ ] KGDB: internal test suite
    [ ] KGDB_KDB: include kdb frontend for kgdb
    [ ] KGDB over Ethernet

2.3.2 添加启动参数

代码语言:txt复制
kgdboc=ttyS0,115200 kgdbwait

2.3.3 调试机(host)上启动gdb作为前端

代码语言:bash复制
gdb /path/to/vmlinux

2.3.4 设置远程调试目标:

代码语言:bash复制
(gdb) target remote /dev/ttyS0

一旦连接成功,可以使用 GDB 的各种命令来进行断点设置、单步执行、变量检查等调试任务。

2.4 kdb

KDB 是内核内置的调试器,可以通过键盘直接激活

2.4.1 准备内核

代码语言:txt复制
Kernel hacking  --->
  <*> Kernel debugging  --->
    <*> Kernel debugger (KDB)
    <*>   Support for kgdb over the serial console

2.4.2 启动配置了KDB支持的内核,无需额外启动参数

2.4.3 激活KDB

通过触发系统崩溃(如 Magic SysRq 键组合)或通过预设断点来激活 KDB。

在键盘上按下 Alt SysRq G 可以激活 KDB。

2.4.4 使用KDB

代码语言:txt复制
	在 KDB 提示符下,你可以使用命令来查看堆栈、寄存器、内存等:
	bt:查看当前的调用堆栈。
	rd:查看寄存器内容。
	md:查看内存地址的内容。

2.5 dynamic debug

dynamic debug 无需重新编译内核,可以根据需求打开特定的模块的打印选项。这对于理解和调试内核非常有用。可以参考下面的文档。

2.5.1 准备内核

代码语言:txt复制
Kernel hacking  --->
  [*] Enable dynamic printk() call support

2.5.2 查看可用的动态调试点

代码语言:bash复制
cat /sys/kernel/debug/dynamic_debug/control

2.5.3 启用调试命令

代码语言:bash复制
echo 'func my_function  p' > /sys/kernel/debug/dynamic_debug/control

更多细节请参考动态debug的内核

https://www.kernel.org/doc/html/v4.14/admin-guide/dynamic-debug-howto.html

3. 结束语

通过有效地使用这些工具,Linux 内核开发者可以更有效地定位和解决内核级别的问题。从实时调试复杂的驱动问题(使用 KGDB)到快速查看系统状态(使用 KDB),或者动态调整调试输出(使用 Dynamic Debug),这些工具为我们提供了强大的支持。随着技术的进步和内核的发展,这些调试方法将继续发挥关键作用,帮助开发者优化内核性能和稳定性。

0 人点赞