Linux内核中symbol_request()与symbol_get()的差异及实现需求咨询
Linux内核符号获取工具差异与等待重试实现
1. symbol_request() 与 symbol_get() 的功能差异
从内核源码的实际逻辑来看,二者的核心差异在于同步性和行为目的:
- symbol_get(sym):这是同步操作,直接尝试获取目标符号的引用计数。若符号当前已存在(所属模块已加载),会递增引用计数并返回符号指针;若符号不存在,直接返回NULL。核心是“立即确认符号可用性并获取”,适合需要同步判断结果的场景。
- symbol_request(sym):这是异步请求操作,不会立即检查符号是否存在,仅向内核注册“需要该符号”的请求。当后续提供该符号的模块被加载时,内核会自动完成符号解析绑定。它的返回值仅表示请求是否成功提交(而非符号是否可用),适合不需要立即使用符号、可等待模块后续加载的场景。
额外注意:symbol_request() 本身不会递增符号引用计数,后续实际使用符号前仍需调用symbol_get()获取引用;而symbol_get()成功时已自动完成引用计数递增。
2. 实现符号等待/重试机制的方案
根据不同上下文场景,可选择以下几种实现方式:
方案一:异步请求+定时轮询(适合非阻塞场景)
先通过symbol_request()注册符号请求,再用延迟工作队列定时检查符号是否可用,直到获取成功或超时:
#include <linux/workqueue.h> #include <linux/module.h> static struct delayed_work check_symbol_work; static void (*target_sym)(void); // 目标符号指针 static void check_symbol_handler(struct work_struct *work) { // 尝试获取符号 target_sym = symbol_get(target_sym_name); if (target_sym) { // 符号获取成功,执行后续逻辑 pr_info("Symbol acquired successfully\n"); target_sym(); } else { // 未获取到,1秒后重试 schedule_delayed_work(&check_symbol_work, msecs_to_jiffies(1000)); } } static int __init my_module_init(void) { // 注册符号请求 if (!symbol_request(target_sym_name)) { pr_err("Failed to request symbol\n"); return -EINVAL; } // 初始化并调度第一次检查 INIT_DELAYED_WORK(&check_symbol_work, check_symbol_handler); schedule_delayed_work(&check_symbol_work, msecs_to_jiffies(1000)); return 0; } static void __exit my_module_exit(void) { cancel_delayed_work_sync(&check_symbol_work); if (target_sym) { symbol_put(target_sym_name); } symbol_put(target_sym_name); // 释放symbol_request的请求 } module_init(my_module_init); module_exit(my_module_exit);
方案二:同步加载模块+直接获取(适合允许阻塞的场景)
若知道符号所属模块名称,可先调用request_module()同步加载模块,再尝试获取符号:
#include <linux/module.h> static void (*target_sym)(void); static int __init my_module_init(void) { // 同步加载目标模块 if (request_module("target_module_name")) { pr_err("Failed to load target module\n"); return -ENODEV; } // 加载后尝试获取符号 target_sym = symbol_get(target_sym_name); if (!target_sym) { pr_err("Failed to get symbol\n"); return -EINVAL; } // 使用符号 target_sym(); return 0; } static void __exit my_module_exit(void) { if (target_sym) { symbol_put(target_sym_name); } } module_init(my_module_init); module_exit(my_module_exit);
注意:request_module()会阻塞当前进程,不能在中断上下文或持有自旋锁的场景中使用。
方案三:手动轮询(简单场景)
在进程上下文中,通过循环+延迟的方式手动重试获取符号:
#include <linux/module.h> #include <linux/delay.h> static void (*target_sym)(void); static int __init my_module_init(void) { int retry_count = 0; const int max_retry = 10; // 最大重试次数 // 注册符号请求,确保模块加载时触发绑定 if (!symbol_request(target_sym_name)) { pr_err("Failed to request symbol\n"); return -EINVAL; } // 轮询获取符号 while (retry_count < max_retry) { target_sym = symbol_get(target_sym_name); if (target_sym) { break; } msleep(1000); // 每次重试间隔1秒 retry_count++; } if (!target_sym) { pr_err("Failed to get symbol after %d retries\n", max_retry); symbol_put(target_sym_name); return -EINVAL; } // 使用符号 target_sym(); return 0; } static void __exit my_module_exit(void) { if (target_sym) { symbol_put(target_sym_name); } symbol_put(target_sym_name); } module_init(my_module_init); module_exit(my_module_exit);
通用注意事项
- 无论哪种方式,获取符号后必须调用
symbol_put(sym)释放引用,避免模块无法卸载或内存泄漏。 - 中断上下文禁止使用阻塞操作(如request_module()、msleep()),这类场景只能选择异步方案。
- 建议设置超时或最大重试次数,避免无限等待。
内容的提问来源于stack exchange,提问作者Rathnavel
相关产品推荐
相关产品推荐

