Linux内核模块调用get_futex_key函数的问题求助
内核模块调用get_futex_key的编译问题与解决
需求背景
开发Linux内核模块,目标是调用get_futex_key获取指定futex对应的key,最终通过内核函数拿到持有该futex的线程列表。
现状与问题
- 已实现基础内核模块(类似hello world),明确
printk等导出符号可直接调用(通过/proc/kallsyms确认) get_futex_key在/proc/kallsyms中可见,但定义在futex.c而非对外头文件中- 通过
strace拿到目标futex的用户空间地址0x20566a0 - 编写的模块代码编译报错,错误信息:
/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
相关产品推荐
相关产品推荐

