
大家好,我是bug菌~
如何在内核panic时注册回调函数,有时候系统panic后需要进行一些收尾工作:
完整的示例代码
c
#include <linux/notifier.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/reboot.h> // 可能需要
#include <linux/delay.h>
static int my_panic_handler(struct notifier_block *nb,
unsigned long reason, void *arg)
{
// reason 参数表示panic的原因,可以是:
// PANIC_REASON_GENERIC
// PANIC_REASON_HW_BUG
// PANIC_REASON_HUNG_TASK
// PANIC_REASON_STACKLEAK
// PANIC_REASON_CORRUPTED_STACK
// PANIC_REASON_CGROUP_DESTROY_FAILED
pr_emerg("Kernel panic detected in my module!\n");
// 获取panic消息(如果有)
const char *msg = (const char *)arg;
if (msg)
pr_emerg("Panic message: %s\n", msg);
// 紧急操作 - 注意:
// 1. 不要分配内存(如kmalloc)
// 2. 不要获取可能已持有的锁
// 3. 避免复杂操作,系统可能已不稳定
// 示例:通知硬件模块
// hardware_emergency_shutdown();
// 示例:保存关键数据到持久存储
// emergency_data_flush();
// 示例:发送信号到其他处理器
// send_ipi_to_all_cpus(EMERGENCY_IPI);
return NOTIFY_DONE;
}
static struct notifier_block panic_nb = {
.notifier_call = my_panic_handler,
.priority = INT_MAX, // 优先级:数字越大,优先级越高
// 或者使用 .next = NULL,
};
static int __init panic_module_init(void)
{
int ret;
pr_info("Registering panic notifier\n");
// 注册到panic通知链
ret = atomic_notifier_chain_register(&panic_notifier_list, &panic_nb);
if (ret) {
pr_err("Failed to register panic notifier: %d\n", ret);
return ret;
}
// 也可以注册到重启通知链,如果系统会重启
// register_reboot_notifier(&reboot_nb);
return 0;
}
static void __exit panic_module_exit(void)
{
pr_info("Unregistering panic notifier\n");
// 注销通知
atomic_notifier_chain_unregister(&panic_notifier_list, &panic_nb);
}
module_init(panic_module_init);
module_exit(panic_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Kernel panic handler module");
关键注意事项
- 操作限制
c
// 在panic处理程序中应避免:
- 内存分配(kmalloc, vmalloc等)
- 获取锁(mutex_lock, spin_lock等)
- 调度相关操作(schedule, sleep等)
- 复杂I/O操作
- 可能失败的操作
// 应该做:
- 简单的硬件寄存器操作
- 紧急状态保存到非易失存储
- 发送硬件信号或中断
- 打印关键调试信息
- 优先级控制
c
static struct notifier_block panic_nb = {
.notifier_call = my_panic_handler,
.priority = INT_MAX, // 最高优先级
// 或者:
// .priority = 0, // 最低优先级
// .priority = 100, // 自定义优先级
};
- 多处理器注意事项
c
static int my_panic_handler(struct notifier_block *nb,
unsigned long reason, void *arg)
{
// panic处理程序在所有CPU上都会运行!
// 使用 smp_processor_id() 获取当前CPU ID
int cpu = smp_processor_id();
// 如果是第一个panic的CPU,执行清理操作
if (cpu == 0) {
// 主清理操作
} else {
// 从处理器只需简单清理
}
return NOTIFY_DONE;
}
- 系统挂起处理
c
#include <linux/freezer.h>
static int my_panic_handler(struct notifier_block *nb,
unsigned long reason, void *arg)
{
// 尝试解冻进程(如果需要)
thaw_processes();
// 停止所有可停止的进程
// emergency_ops();
return NOTIFY_DONE;
}
调试技巧
- 添加调试信息
c
static int my_panic_handler(struct notifier_block *nb,
unsigned long reason, void *arg)
{
pr_emerg("=== My Module Panic Handler ===\n");
pr_emerg("CPU: %d\n", smp_processor_id());
pr_emerg("Panic reason: %lu\n", reason);
pr_emerg("Stack trace:\n");
dump_stack(); // 打印堆栈跟踪
// 保存寄存器状态
show_regs(get_irq_regs());
return NOTIFY_DONE;
}
- 使用Kprobes进行测试
c
// 可以通过Kprobes触发panic来测试
#include <linux/kprobes.h>
static struct kprobe kp = {
.symbol_name = "panic",
};
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
{
pr_info("About to trigger panic\n");
return 0;
}
static int __init test_init(void)
{
int ret;
kp.pre_handler = handler_pre;
ret = register_kprobe(&kp);
// 之后可以手动触发panic来测试
// panic("Test panic");
return ret;
}
替代方案
- 使用die_notifier
c
#include <linux/kdebug.h>
static int my_die_handler(struct notifier_block *self,
unsigned long val, void *data)
{
struct die_args *args = data;
if (val == DIE_OOPS || val == DIE_PANIC) {
pr_emerg("Die notifier triggered\n");
}
return NOTIFY_DONE;
}
static struct notifier_block die_nb = {
.notifier_call = my_die_handler,
};
- 使用module参数控制
c
static bool enable_panic_handler = true;
module_param(enable_panic_handler, bool, 0644);
static int my_panic_handler(struct notifier_block *nb,
unsigned long reason, void *arg)
{
if (!enable_panic_handler)
return NOTIFY_DONE;
// 处理逻辑
return NOTIFY_DONE;
}
最佳实践
- 保持处理程序简单 - 系统已不稳定,复杂操作可能失败
- 避免依赖其他模块 - 其他模块可能已经卸载或损坏
- 记录关键状态 - 保存到不易丢失的存储中
- 考虑硬件状态 - 确保硬件处于安全状态
- 测试充分 - 在实际panic场景测试,而不仅仅是模拟
以上就是我的一些技巧和注意事项分享了。