You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 09:51:23