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

Linux内核模块调用get_futex_key函数的问题求助

内核模块调用get_futex_key的编译问题与解决

需求背景

开发Linux内核模块,目标是调用get_futex_key获取指定futex对应的key,最终通过内核函数拿到持有该futex的线程列表。

现状与问题

  1. 已实现基础内核模块(类似hello world),明确printk等导出符号可直接调用(通过/proc/kallsyms确认)
  2. get_futex_key在/proc/kallsyms中可见,但定义在futex.c而非对外头文件中
  3. 通过strace拿到目标futex的用户空间地址0x20566a0
  4. 编写的模块代码编译报错,错误信息:
/home/myusername/git/LinuxExamples/kernel/fut/fut.c:5:10: fatal error: kernel/futex.c: No such file or directory
 #include <kernel/futex.c>
          ^~~~~~~~~~~~~~~~
compilation terminated.

解决方案

1. 解决编译错误:移除.c文件包含

内核模块绝对不能直接包含.c源文件,这些文件已编译进内核,且仅头文件用于对外暴露接口。直接删除代码中的#include <kernel/futex.c>即可解决此编译错误。

2. 补充函数声明

get_futex_key是futex.c内部的静态函数,其声明未对外暴露在linux/futex.h中,需要在模块中手动添加匹配内核源码的函数原型:

static int get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw);

注:原型需与你当前内核版本的futex.c中定义完全一致

3. 解决符号链接问题

从/proc/kallsyms输出可见,get_futex_key的符号标记为t,说明它是静态函数,内核未将其导出为全局可见符号(导出符号标记为T,如printk)。直接调用会触发链接错误,有两种解决方式:

方式1:修改内核源码导出符号(需重新编译内核)
  • 找到内核源码中的futex.c文件
  • 在get_futex_key函数定义后添加导出宏:
    EXPORT_SYMBOL(get_futex_key);
    
  • 重新编译并安装内核,之后模块可直接调用该函数
方式2:动态通过kallsyms获取函数地址(无需重新编译内核)

通过kallsyms_lookup_name解析/proc/kallsyms获取函数地址,转换为函数指针调用,修改后的模块代码示例:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/futex.h>
#include <linux/kallsyms.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("FutexTestAuthor");
MODULE_DESCRIPTION("Futex Examiner");

// 定义函数指针类型
typedef int (*get_futex_key_t)(u32 __user *uaddr, int fshared, union futex_key *key, int rw);
static get_futex_key_t get_futex_key_ptr;

static int __init hello_init(void)
{
    union futex_key key1 = FUTEX_KEY_INIT;
    printk(KERN_INFO "Get futex key\n");

    // 从kallsyms获取函数地址
    get_futex_key_ptr = (get_futex_key_t)kallsyms_lookup_name("get_futex_key");
    if (!get_futex_key_ptr) {
        printk(KERN_ERR "Failed to find get_futex_key symbol\n");
        return -ENOENT;
    }

    // 验证用户空间地址合法性(可选但推荐)
    if (!access_ok(VERIFY_READ, (void __user *)0x20566a0, sizeof(u32))) {
        printk(KERN_ERR "Invalid user address\n");
        return -EFAULT;
    }

    // 调用函数,fshared传0对应私有futex(匹配strace中的PRIVATE标记)
    int ret = get_futex_key_ptr((u32 __user *)0x20566a0, 0, &key1, VERIFY_READ);
    if (ret != 0) {
        printk(KERN_ERR "get_futex_key failed with ret: %d\n", ret);
        return ret;
    }

    printk(KERN_INFO "key1 = %p\n", &key1);
    // 使用完key后需调用put_futex_key释放资源(若涉及引用计数)
    put_futex_key(&key1);
    return 0;
}

static void __exit hello_cleanup(void)
{
    printk(KERN_INFO "Cleaning up futex module.\n");
}

module_init(hello_init);
module_exit(hello_cleanup);

注:使用kallsyms_lookup_name需要内核开启CONFIG_KALLSYMS和CONFIG_KALLSYMS_ALL(多数发行版默认开启)


额外注意事项

  • fshared参数需与目标futex的属性匹配:0为私有(进程内),非0为共享(进程间),对应strace中的PRIVATE/SHARED标记
  • 调用get_futex_key后,若key涉及共享内存等引用计数场景,必须调用put_futex_key释放资源,避免内存泄漏
  • 模块必须声明MODULE_LICENSE("GPL"),否则无法调用部分内核符号

内容的提问来源于stack exchange,提问作者Jacob Quisenberry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 17:17:31