1,warn_on()
warn_on()
是 Linux 内核中用于报告潜在问题或警告的宏。与 bug_on()
不同,bug_on()
通常用于报告严重错误,其触发往往会导致内核Oops或panic,而 warn_on()
则用于报告不太严重的、可能只是潜在问题或预期外情况的情况。它的触发通常不会立即导致系统崩溃,而是记录警告信息并打印 call trace,让开发者或管理员了解发生了什么。
warn_on()
的工作原理(简化版):
条件判断:
warn_on(condition)
宏会被展开为类似if (unlikely(condition))
的代码。触发警告:如果
condition
为真,代码会执行到一个内核函数,通常是WARN_ON()
宏或相关的函数(如printk()
加上特定的格式和调试信息)。打印信息:这个函数会打印出警告信息,包括:触发警告的文件名和行号。内核版本信息。Call Trace: 显示触发警告的函数调用链。这是
warn_on()
提供的最有价值的信息之一。其他可能的调试信息(如寄存器状态,取决于内核配置)。继续执行:与
bug_on()
不同,warn_on()
通常会打印信息后继续执行下一条指令。这允许系统在记录问题后尝试从警告状态恢复(尽管有时警告也预示着严重问题)。
举例:
#include <linux/kernel.h>
#include <linux/module.h>
void my_driver_function(struct my_data *data) {
// ... 一些代码 ...
// 检查指针是否为 NULL,如果是则发出警告
WARN_ON(!data); // 在 my_driver.c:100
// ... 更多代码 ...
}
static int __init my_driver_init(void) {
// ... 初始化代码 ...
my_driver_function(NULL); // 故意传递 NULL 来触发警告
// ... 更多初始化代码 ...
return 0;
}
static void __exit my_driver_exit(void) {
// ... 清理代码 ...
}
module_init(my_driver_init);
module_exit(my_driver_exit);
当 my_driver_function(NULL)
被调用时,WARN_ON(!data)
的条件为真(因为 data
是 NULL),就会触发警告。
假设这个警告发生在 CPU 0 上,内核环缓冲区 (dmesg
) 中可能会看到类似如下的输出(具体格式可能因内核版本和配置略有不同):
[ 123.456789] WARNING: CPU: 0 PID: 1234 at drivers/my_driver/my_driver.c:100 my_driver_function+0x5a/0x120 (not tainted)
[ 123.456789] Hardware name: ...
[ 123.456789] Call Trace:
[ 123.456789] dump_stack+0x4c/0x73
[ 123.456789] ? my_driver_function+0x5a/0x120
[ 123.456789] ? WARN_ON+0x7b/0x90
[ 123.456789] my_driver_function+0x5a/0x120 [my_driver]
[ 123.456789] my_driver_init+0x40/0x100 [my_driver]
[ 123.456789] do_one_initcall+0x130/0x170
[ 123.456789] kernel_init_freeable+0x130/0x170
[ 123.456789] ? kernel_init+0x10/0x10
[ 123.456789] kernel_init+0x10/0x170
[ 123.456789] ret_from_fork+0x22/0x30
[ 123.456789] ---[ end trace 123456789abcdef ]---
2,bug_on()
bug_on()
触发时如何打印 call trace(调用跟踪)。
bug_on()
是 Linux 内核中用于检测和报告严重错误的宏。它的作用类似于 C 语言中的 assert()
, 但专门用于内核环境。当传递给 bug_on()
的条件为真(非零)时,它会触发一个内核 Oops(类似于用户空间的段错误),并打印出非常有用的调试信息,其中就包括 call trace。
bug_on()
的工作原理(简化版):
- 条件判断:
bug_on(condition)
宏会被展开为类似if (unlikely(condition)) { ... }
的代码。 - 触发错误:如果
condition
为真,代码会执行到一个内核函数,通常是BUG()
或相关的宏/函数。 - 打印信息:这个函数会打印出错误信息,包括:
- 触发
BUG
的文件名和行号。 - 内核版本信息。
- Call Trace: 这是最关键的部分,它显示了触发
BUG
的函数调用链。 - CPU 信息、寄存器状态等(取决于内核配置和 Oops 类型)。
- 触发
- 处理后果:通常会导致进程终止(如果是进程中的错误)或系统进入 Oops 状态,可能需要手动重启。在启用
CONFIG_PANIC_ON_OOPS=y
的情况下,可能会直接触发panic()
导致系统崩溃。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/printk.h> // for pr_info
// 假设这个函数不应该被调用
static void do_something_undefined(void)
{
// 触发 bug_on: 假设某个不应该为真的条件发生了
// 这里我们强制让它为真来演示
int should_be_false = 1;
// 当 should_be_false 为真时,bug_on 会触发
// 内核宏通常写成 bug_on(should_be_false)
// 为了演示,我们假设它触发了
// --- 以下是模拟的 bug_on 触发后的内核输出 ---
// 文件: my_module.c
// 函数: do_something_undefined
// 行号: 15
// --- 内核实际打印信息 ---
// BUG: Badness at my_module.c:15 in do_something_undefined()
// Modules linked in: my_module
// Pid: 1234, comm: insmod Tainted: G W ...
// RIP: 0010:[do_something_undefined+0x10/0x30]
// Code: Bad RIP value.
// RSP: 0000:ffffc900003aefb8 EFLAGS: 00010206
// RAX: 0 RAX: 0 RAX: 0 RAX: 0
// ...
// Call Trace:
// my_function+0x20/0x50 [my_module]
// another_function+0x15/0x40 [my_module]
// my_init+0x25/0x80 [my_module]
// do_one_initcall+0x80/0x300
// kernel_init_freeable+0x130/0x170
// ? kthreadd+0x70/0x70
// kernel_init+0x10/0x100
// ret_from_fork+0x22/0x30
// --- 内核实际打印信息结束 ---
pr_info("This line won't be reached if bug_on triggers.\n");
}
static int my_function(void)
{
pr_info("Inside my_function\n");
do_something_undefined(); // 调用那个会触发 bug_on 的函数
return 0;
}
static int another_function(void)
{
pr_info("Inside another_function\n");
return my_function(); // 调用 my_function
}
static int __init my_init(void)
{
pr_info("Initializing my module\n");
return another_function(); // 调用 another_function
}
static void __exit my_exit(void)
{
pr_info("Exiting my module\n");
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A module demonstrating bug_on call trace");
异常打印如下:
BUG: Badness at my_module.c:15 in do_something_undefined()
Modules linked in: my_module
...
Pid: [PID], comm: [Command that loaded the module, e.g., insmod]
RIP: [Instruction pointer at the bug_on location]
Code: [Opcode at RIP]
RSP: [Stack pointer]
...
Call Trace:
do_something_undefined+0x10/0x30 [my_module]
my_function+0x20/0x50 [my_module]
another_function+0x15/0x40 [my_module]
my_init+0x25/0x80 [my_module]
do_one_initcall+0x80/0x300
kernel_init_freeable+0x130/0x170
? kthreadd+0x70/0x70
kernel_init+0x10/0x100
ret_from_fork+0x22/0x30
如何获取 bug_on
的输出
- 串口 (Serial Console): 如果你的系统通过串口连接,错误信息通常会直接输出到串口。
- tty 控制台: 如果
bug_on
发生在直接连接的显示器上,信息会打印在那里。 dmesg
命令: 在bug_on
发生后,即使系统可能已经挂起或重启,你通常也可以使用dmesg
命令来查看内核环缓冲区(kernel ring buffer)中的消息,包括 Oops 信息和 Call Trace。/var/log/kern.log
或/var/log/syslog
: 在某些系统上,kern.log
或syslog
文件会记录内核消息,包括 Oops。