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。