linux 内核warn_on/Bug_on

1,warn_on()

warn_on() 是 Linux 内核中用于报告潜在问题或警告的宏。与 bug_on() 不同,bug_on() 通常用于报告严重错误,其触发往往会导致内核Oops或panic,而 warn_on() 则用于报告不太严重的、可能只是潜在问题或预期外情况的情况。它的触发通常不会立即导致系统崩溃,而是记录警告信息并打印 call trace,让开发者或管理员了解发生了什么。

warn_on() 的工作原理(简化版):

  1. 条件判断warn_on(condition) 宏会被展开为类似 if (unlikely(condition)) 的代码。

  2. 触发警告 :如果 condition 为真,代码会执行到一个内核函数,通常是 WARN_ON() 宏或相关的函数(如 printk() 加上特定的格式和调试信息)。

  3. 打印信息 :这个函数会打印出警告信息,包括:触发警告的文件名和行号。内核版本信息。Call Trace : 显示触发警告的函数调用链。这是 warn_on() 提供的最有价值的信息之一。其他可能的调试信息(如寄存器状态,取决于内核配置)。

  4. 继续执行 :与 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() 的工作原理(简化版):

  1. 条件判断bug_on(condition) 宏会被展开为类似 if (unlikely(condition)) { ... } 的代码。

  2. 触发错误 :如果 condition 为真,代码会执行到一个内核函数,通常是 BUG() 或相关的宏/函数。

  3. 打印信息 :这个函数会打印出错误信息,包括:

    • 触发 BUG 的文件名和行号。
    • 内核版本信息。
    • Call Trace : 这是最关键的部分,它显示了触发 BUG 的函数调用链。
    • CPU 信息、寄存器状态等(取决于内核配置和 Oops 类型)。
  4. 处理后果 :通常会导致进程终止(如果是进程中的错误)或系统进入 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 的输出

  1. 串口 (Serial Console): 如果你的系统通过串口连接,错误信息通常会直接输出到串口。
  2. tty 控制台 : 如果 bug_on 发生在直接连接的显示器上,信息会打印在那里。
  3. dmesg 命令 : 在 bug_on 发生后,即使系统可能已经挂起或重启,你通常也可以使用 dmesg 命令来查看内核环缓冲区(kernel ring buffer)中的消息,包括 Oops 信息和 Call Trace。
  4. /var/log/kern.log/var/log/syslog : 在某些系统上,kern.logsyslog 文件会记录内核消息,包括 Oops。
相关推荐
Zyed1 小时前
[STM32]Day15读写FLASH+读取ID
前端·stm32·性能优化
深蓝电商API6 小时前
无头浏览器性能优化:内存占用从2GB降到200MB
爬虫·性能优化
cfm_29147 小时前
JVM垃圾收集算法与收集器深度解析
jvm·测试工具·算法·性能优化
189228048618 小时前
NV114固态MT29F16T08EWLEHD6-MES:E
人工智能·算法·缓存·性能优化
超哥--9 小时前
B站视频内容智能分析系统(十):踩坑记录与性能优化
性能优化·音视频·ai编程
Gong-Yu9 小时前
MySQL数据库运维——性能优化进阶2️⃣
运维·数据库·mysql·性能优化
hai3152475439 小时前
九章编程法 · 字典引擎【0/1拓扑步进 · 矩阵压缩·终极封版】
人工智能·数学建模·性能优化·动态规划·代码复审·傅立叶分析·极限编程
爱喝水的鱼丶19 小时前
SAP-ABAP:SAP视图开发入门:四类标准视图的适用场景与创建步骤详解
服务器·数据库·性能优化·sap·abap
ViavaCos1 天前
AI 帮我写代码,我帮 AI 踩坑:Vue 大数据表格优化全记录
前端·性能优化
沙漠1 天前
ReactNative总结系列四 --- FlatList白屏卡顿优化
react native·性能优化