Asterisk自定义模块set_stats符号未定义问题求助
问题:调用自定义Asterisk模块函数触发未定义符号错误
我编写了一个名为Reset Queue Statistics的Asterisk自定义模块,模块本身可正常加载并按配置每分钟输出日志,但在app_queue.c中调用模块内的set_stats函数时,加载app_queue.so时报错:
Error loading module 'app_queue.so': /usr/lib/asterisk/modules/app_queue.so: undefined symbol: set_stats
自定义模块代码(res_resetqueuestats.c)
#include <time.h> #include "asterisk/module.h" #include "asterisk/logger.h" #include "asterisk/res_resetqueuestats.h" static unsigned int interval = 1; int set_stats(const char *queuename){ ast_log(LOG_NOTICE, "SET STATS CALLED FOR %s", queuename); return 0; } static void write_log(void) { time_t now = time(NULL); struct tm *tm_now = localtime(&now); ast_log(LOG_NOTICE, "Current time interval: %02d:%02d\n", tm_now->tm_hour, tm_now->tm_min); } static void* log_thread(void* data) { while (1) { time_t now = time(NULL); struct tm *tm_now = localtime(&now); if (tm_now->tm_min % interval == 0) { write_log(); } usleep(60 * 1000 * 1000); } return NULL; } static int load_module(void) { ast_log(LOG_NOTICE, "Loading Reset Queue Statistics module.\n"); pthread_t thread; if (pthread_create(&thread, NULL, log_thread, NULL) != 0) { ast_log(LOG_ERROR, "Failed to create log thread.\n"); return AST_MODULE_LOAD_FAILURE; } return AST_MODULE_LOAD_SUCCESS; } static int unload_module(void) { ast_log(LOG_NOTICE, "Unloading Reset Queue Statistics module.\n"); return 0; } AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Reset Queue Statistics");
对应头文件(res_resetqueuestats.h)
#define _RES_RESETQUEUESTATS_H #include <asterisk.h> #include <time.h> #include "asterisk/module.h" #include "asterisk/logger.h" int set_stats(const char *queuename); #endif
app_queue.c中的调用位置
// ... 其他头文件引入 ... #include "asterisk/res_resetqueuestats.h" //... static void handle_hangup(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg) { // ... 原有业务代码省略 ... // 自定义函数调用 int result = set_stats(queue_data->queue->name); remove_stasis_subscriptions(queue_data); }
原因分析
- 动态模块独立加载特性:Asterisk的模块都是独立的动态链接库(
.so),app_queue.so和你的自定义模块res_resetqueuestats.so属于两个完全独立的动态库,编译和加载阶段没有建立链接关系,因此app_queue.so无法找到set_stats的符号。 - 符号可见性限制:即使头文件声明了
set_stats,但自定义模块编译时默认不会将该符号导出到全局符号表,其他模块无法直接访问。
解决方法
方法一:将自定义模块改为Asterisk内置静态模块(最直接)
- 将
res_resetqueuestats.c和头文件移动到Asterisk源码的res/目录下。 - 修改Asterisk根目录的
Makefile,将res_resetqueuestats.c添加到RES_SOURCES列表中。 - 重新编译Asterisk,此时自定义模块会被编译进核心程序,
app_queue作为内置模块可直接调用set_stats函数。
方法二:修改编译规则,让app_queue链接到自定义模块
- 在自定义模块的编译规则中添加
-fPIC参数,确保符号可导出(可在模块对应的Makefile中配置)。 - 修改
apps/Makefile,在app_queue.so的编译链接参数中添加-lresetqueuestats,让其链接到你的自定义模块。 - 注意:这种方法需要保证自定义模块先于
app_queue加载,否则仍会出现符号找不到的问题。
方法三:使用Asterisk规范的模块间交互机制(推荐)
Asterisk不推荐跨模块直接调用函数,建议通过以下规范方式实现:
- 注册模块服务:在自定义模块中注册一个服务,将
set_stats作为服务接口暴露:// 自定义模块中添加服务定义 typedef struct { int (*reset_stats)(const char *queuename); } reset_queue_stats_api; static reset_queue_stats_api api = { .reset_stats = set_stats, }; static int load_module(void) { // ... 原有代码 ... ast_module_register_api("res_resetqueuestats", &api); return AST_MODULE_LOAD_SUCCESS; } static int unload_module(void) { // ... 原有代码 ... ast_module_unregister_api("res_resetqueuestats"); return 0; } - 在app_queue中调用服务:
static void handle_hangup(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg) { // ... 原有代码 ... reset_queue_stats_api *api = ast_module_get_api("res_resetqueuestats"); if (api) { api->reset_stats(queue_data->queue->name); } // ... 原有代码 ... }
这种方式符合Asterisk的模块设计规范,避免了直接依赖问题,同时支持模块动态加载卸载。
内容的提问来源于stack exchange,提问作者Flamur Dervishi
相关产品推荐
相关产品推荐

