镇江市网站建设_网站建设公司_Java_seo优化
2026/1/2 9:22:49 网站建设 项目流程


大家好,我是bug菌~

如何在内核panic时注册回调函数,有时候系统panic后需要进行一些收尾工作:

完整的示例代码

#include<linux/notifier.h>#include<linux/init.h>#include<linux/module.h>#include<linux/kernel.h>#include<linux/reboot.h>// 可能需要#include<linux/delay.h>staticintmy_panic_handler(structnotifier_block*nb,unsignedlongreason,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_FAILEDpr_emerg("Kernel panic detected in my module!\n");// 获取panic消息(如果有)constchar*msg=(constchar*)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);returnNOTIFY_DONE;}staticstructnotifier_blockpanic_nb={.notifier_call=my_panic_handler,.priority=INT_MAX,// 优先级:数字越大,优先级越高// 或者使用 .next = NULL,};staticint__initpanic_module_init(void){intret;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);returnret;}// 也可以注册到重启通知链,如果系统会重启// register_reboot_notifier(&reboot_nb);return0;}staticvoid__exitpanic_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");

关键注意事项

  1. 操作限制
// 在panic处理程序中应避免:-内存分配(kmalloc,vmalloc等)-获取锁(mutex_lock,spin_lock等)-调度相关操作(schedule,sleep等)-复杂I/O操作-可能失败的操作// 应该做:-简单的硬件寄存器操作-紧急状态保存到非易失存储-发送硬件信号或中断-打印关键调试信息
  1. 优先级控制
staticstructnotifier_blockpanic_nb={.notifier_call=my_panic_handler,.priority=INT_MAX,// 最高优先级// 或者:// .priority = 0, // 最低优先级// .priority = 100, // 自定义优先级};
  1. 多处理器注意事项
staticintmy_panic_handler(structnotifier_block*nb,unsignedlongreason,void*arg){// panic处理程序在所有CPU上都会运行!// 使用 smp_processor_id() 获取当前CPU IDintcpu=smp_processor_id();// 如果是第一个panic的CPU,执行清理操作if(cpu==0){// 主清理操作}else{// 从处理器只需简单清理}returnNOTIFY_DONE;}
  1. 系统挂起处理
#include<linux/freezer.h>staticintmy_panic_handler(structnotifier_block*nb,unsignedlongreason,void*arg){// 尝试解冻进程(如果需要)thaw_processes();// 停止所有可停止的进程// emergency_ops();returnNOTIFY_DONE;}

调试技巧

  1. 添加调试信息
staticintmy_panic_handler(structnotifier_block*nb,unsignedlongreason,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());returnNOTIFY_DONE;}
  1. 使用Kprobes进行测试
// 可以通过Kprobes触发panic来测试#include<linux/kprobes.h>staticstructkprobekp={.symbol_name="panic",};staticinthandler_pre(structkprobe*p,structpt_regs*regs){pr_info("About to trigger panic\n");return0;}staticint__inittest_init(void){intret;kp.pre_handler=handler_pre;ret=register_kprobe(&kp);// 之后可以手动触发panic来测试// panic("Test panic");returnret;}

替代方案

  1. 使用die_notifier
#include<linux/kdebug.h>staticintmy_die_handler(structnotifier_block*self,unsignedlongval,void*data){structdie_args*args=data;if(val==DIE_OOPS||val==DIE_PANIC){pr_emerg("Die notifier triggered\n");}returnNOTIFY_DONE;}staticstructnotifier_blockdie_nb={.notifier_call=my_die_handler,};
  1. 使用module参数控制
staticbool enable_panic_handler=true;module_param(enable_panic_handler,bool,0644);staticintmy_panic_handler(structnotifier_block*nb,unsignedlongreason,void*arg){if(!enable_panic_handler)returnNOTIFY_DONE;// 处理逻辑returnNOTIFY_DONE;}

最佳实践

  1. 保持处理程序简单 - 系统已不稳定,复杂操作可能失败
  2. 避免依赖其他模块 - 其他模块可能已经卸载或损坏
  3. 记录关键状态 - 保存到不易丢失的存储中
  4. 考虑硬件状态 - 确保硬件处于安全状态
  5. 测试充分 - 在实际panic场景测试,而不仅仅是模拟

以上就是我的一些技巧和注意事项分享了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询