[英]How to call exported kernel module functions from another module?
我正在编写一个 API 作为内核模块,为设备驱动程序提供各种功能。 我在mycode.c 中写了三个函数。 然后我构建并加载了模块,然后将mycode.h复制到<内核>/include/linux 中。 在设备驱动程序中,我有一个#include < linux/mycode.h >并调用这三个函数。 但是当我构建驱动程序模块时,我收到三个链接器警告,说这些函数是 undefined 。
笔记:
很明显,函数被正确导出,内核知道它们是什么以及它们在哪里。 那么为什么司机看不到他们的定义呢? 知道我错过了什么吗?
编辑:我在这里找到了一些关于此的信息: http : //www.kernel.org/doc/Documentation/kbuild/modules.txt
有时,外部模块使用从另一个外部模块导出的符号。 kbuild 需要完全了解所有符号,以避免发出有关未定义符号的警告。 针对这种情况,存在三种解决方案。
注意:建议使用具有顶级 kbuild 文件的方法,但在某些情况下可能不切实际。
使用顶级 kbuild 文件 如果您有两个模块 foo.ko 和 bar.ko,其中 foo.ko 需要来自 bar.ko 的符号,您可以使用一个通用的顶级 kbuild 文件,以便两个模块在同一建造。 考虑以下目录布局:
./foo/ <= contains foo.ko ./bar/ <= contains bar.ko
顶级 kbuild 文件将如下所示:
#./Kbuild (or ./Makefile): obj-y := foo/ bar/
并执行
$ make -C $KDIR M=$PWD
然后将执行预期并编译两个模块,并充分了解任一模块的符号。
使用额外的 Module.symvers 文件 构建外部模块时,会生成一个 Module.symvers 文件,其中包含所有未在内核中定义的导出符号。 要从 bar.ko 访问符号,请将编译后的 bar.ko 中的 Module.symvers 文件复制到构建 foo.ko 的目录中。 在模块构建过程中,kbuild 将读取外部模块目录中的 Module.symvers 文件,当构建完成时,会创建一个新的 Module.symvers 文件,其中包含定义的所有符号的总和,而不是内核的一部分。
使用“make”变量 KBUILD_EXTRA_SYMBOLS 如果从另一个模块复制 Module.symvers 不切实际,您可以在构建文件中为 KBUILD_EXTRA_SYMBOLS 分配一个空格分隔的文件列表。 这些文件将由 modpost 在其符号表初始化期间加载。
但是对于所有这三个解决方案,为了让任何驱动程序使用我的 API,它必须创建一个新的 Makefile 或直接访问我的 Module.symvers 文件? 这似乎有点不方便。 我希望他们能够#include 我的头文件并且一切顺利。 不存在其他替代方案吗?
从我的研究来看,这似乎是处理这种情况的唯一三种方法,而且我已经让它们中的每一种都起作用,所以我想我会从这些中挑选出我最喜欢的。
最小 QEMU + Buildroot 示例
我已经在完全可重现的 QEMU + Buildroot 环境中测试了以下内容,所以也许拥有这个工作版本将帮助您找出代码中的问题。
GitHub 上游以文件为中心:
dep.c
#include <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
int lkmc_dep = 0;
EXPORT_SYMBOL(lkmc_dep);
static struct task_struct *kthread;
static int work_func(void *data)
{
while (!kthread_should_stop()) {
printk(KERN_INFO "%d\n", lkmc_dep);
usleep_range(1000000, 1000001);
}
return 0;
}
static int myinit(void)
{
kthread = kthread_create(work_func, NULL, "mykthread");
wake_up_process(kthread);
return 0;
}
static void myexit(void)
{
kthread_stop(kthread);
}
module_init(myinit)
module_exit(myexit)
dep2.c
#include <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
extern int lkmc_dep;
static struct task_struct *kthread;
static int work_func(void *data)
{
while (!kthread_should_stop()) {
usleep_range(1000000, 1000001);
lkmc_dep++;
}
return 0;
}
static int myinit(void)
{
kthread = kthread_create(work_func, NULL, "mykthread");
wake_up_process(kthread);
return 0;
}
static void myexit(void)
{
kthread_stop(kthread);
}
module_init(myinit)
module_exit(myexit)
现在你可以这样做:
insmod dep.ko
insmod dep2.ko
使用 Buildroot 设置,事情已经使用依赖项配置了 depmod /lib/module/*/depmod
,所以这足以加载两者:
modprobe dep
此外,如果您使用CONFIG_KALLSYMS_ALL=y
构建内核,则可以通过以下方式查看导出的符号:
grep lkmc_dep /proc/kallsyms
另请参阅: kallsyms 是否具有内核函数的所有符号?
好的:你有一个模块,该功能所在的模块和一个想要导入它的地方,对吗?
你必须在函数所在的地方使用"EXPORT_SYMBOL("函数名"),比如foo。所以在"c"文件中定义了函数"foo"并放入:EXPORT_SYMBOL(foo)
确保在一个公共头文件中拥有“foo”的原型(你可以将它放在每个模块的不同位置,它会工作,但如果签名发生变化,你会遇到麻烦)。 所以说: void foo(void *arg);
然后另一个需要它的模块调用“foo”,你很好。
另外:确保首先使用 foo 加载模块。 如果你有像 module2 需要来自 module1 的 foo 和 module1 需要来自 module2 的 bar 这样的交叉依赖,你需要有一个注册函数和另一个。 如果您想知道,请单独提问。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.