如何从gawk C扩展中调用用户自定义的gawk函数?
实现gawk扩展中调用用户自定义函数的方法
要在gawk扩展里调用用户自定义的gawk函数,核心是利用gawk API中的awk_call_func函数(该函数在gawkapi.h中声明,容易被忽略)。以下是完整实现方案:
核心步骤
- 从用户传入的参数中提取自定义gawk函数名并保存
- 信号触发时,通过
awk_lookup在符号表中找到函数对应的条目 - 用
awk_call_func调用目标函数,同时注意gawk的线程安全处理
完整代码示例
#include <stdio.h> #include <errno.h> #include <stddef.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <signal.h> #include <gawkapi.h> static const gawk_api_t *api; static awk_ext_id_t *ext_id; static const char *ext_version = NULL; static awk_bool_t (*init_func)(void) = NULL; int plugin_is_GPL_compatible; // 存储注册的信号与对应gawk函数名的映射 typedef struct { int signum; char func_name[256]; } signal_handler_t; static signal_handler_t registered_handlers[10]; static int handler_count = 0; // 信号处理回调 static void signal_handler(int signum) { for (int i = 0; i < handler_count; i++) { if (registered_handlers[i].signum == signum) { // 进入非阻塞模式,避免信号处理阻塞gawk主逻辑 api->awk_begin_non_blocking(); // 查找用户定义的gawk函数 awk_symtab_entry_t *func_entry = api->awk_lookup(registered_handlers[i].func_name, strlen(registered_handlers[i].func_name)); if (func_entry != NULL && api->awk_is_func(func_entry)) { awk_value_t result; // 调用函数(此处传入0个参数,可按需调整) if (! api->awk_call_func(func_entry, 0, NULL, &result)) { fprintf(stderr, "Failed to call gawk function %s\n", registered_handlers[i].func_name); } } else { fprintf(stderr, "Gawk function %s not found\n", registered_handlers[i].func_name); } api->awk_end_non_blocking(); break; } } } static awk_value_t* do_signal(int nargs, awk_value_t *result, struct awk_ext_func *unused) { awk_value_t signum_val, func_name_val; // 校验参数数量:需要信号编号和函数名两个参数 if (nargs != 2) { api->awk_vwarnx(ext_id, "signal() requires 2 arguments: signal number and function name"); return make_number(-1, result); } // 获取信号编号参数 if (! api->get_argument(0, &signum_val) || signum_val.type != AWK_NUMBER) { api->awk_vwarnx(ext_id, "Signal number must be a numeric value"); return make_number(-1, result); } int signum = (int) signum_val.num_value; // 获取函数名参数 if (! api->get_argument(1, &func_name_val) || func_name_val.type != AWK_STRING) { api->awk_vwarnx(ext_id, "Function name must be a string"); return make_number(-1, result); } // 注册系统信号处理器 struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = signal_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; if (sigaction(signum, &sa, NULL) == -1) { api->awk_vwarn(ext_id, "sigaction failed for signal %d", signum); return make_number(-1, result); } // 保存信号与函数名的映射 if (handler_count < sizeof(registered_handlers)/sizeof(registered_handlers[0])) { registered_handlers[handler_count].signum = signum; strncpy(registered_handlers[handler_count].func_name, func_name_val.str_value.str, sizeof(registered_handlers[handler_count].func_name)-1); registered_handlers[handler_count].func_name[sizeof(registered_handlers[handler_count].func_name)-1] = '\0'; handler_count++; } else { api->awk_vwarnx(ext_id, "Too many signal handlers registered"); return make_number(-1, result); } return make_number(0, result); // 成功返回0 } static awk_ext_func_t func_table[] = { { "signal", do_signal, 2, 0, awk_false, NULL } }; dl_load_func(func_table, some_name, "")
使用示例
- 编译扩展:
gcc -shared -fPIC -o signal.so signal.c -lgawk
- gawk脚本示例:
function handle_sigint() { print "Caught SIGINT!" } # 注册SIGINT信号处理器 signal(2, "handle_sigint") # 保持运行等待信号 while (1) { sleep(1) }
- 运行命令:
gawk -l ./signal.so script.awk
按下Ctrl+C即可触发自定义的handle_sigint函数。
注意事项
awk_call_func是调用gawk函数的核心API,需传入函数符号表条目、参数数量、参数数组和结果存储变量- 信号处理函数中必须用
awk_begin_non_blocking和awk_end_non_blocking包裹API调用,避免死锁 - 信号处理逻辑尽量简洁,避免执行复杂IO或耗时操作
内容的提问来源于stack exchange,提问作者user25499713
相关产品推荐
相关产品推荐

