Linux 的进程有自己的用户空间,使用自己的虚拟内存空间,因此进程之间无法调用彼此的函数
而 Linux 的模块都加载在内核空间,共享内存,因此可以从一个模块访问另一个模块的资源,而这需要符号表的导出与导入
EXPORT_SYMBOL(变量名|函数名)
或者
EXPORT_SYMBOL_GPL(变量名|函数名)
.
├── 1
│ ├── demo1.c
│ └── Makefile
└── 2
├── demo2.c
└── Makefil
Makefile 使用 Linux 内核模块编程 第 4.2 节的架构通用 Makefile
// demo1.c
#include <linux/init.h>
#include <linux/module.h>
// 定义函数
int add(int i, int j) { return i + j; }
static int __init mycdev_init(void) { return 0; }
// 声明导出当前函数的符号表
EXPORT_SYMBOL(add);
static void __exit mycdev_exit(void) {}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL")
// demo2.c
#include <linux/init.h>
#include <linux/module.h>
extern int add(int i, int j);
static int __init mycdev_init(void) {
printk("add(3, 4) = %d\\n", add(3, 4));
return 0;
}
static void __exit mycdev_exit(void) {}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL")
在 1 目录下编译 make arch=x86 modname=demo1 ,生成 Module.symvers ,将其拷贝到 2 目录下,make arch=x86 modname=demo2 进行编译。
<aside>
🛠 版本较新的 Linux 内核不再支持这种编译方式,需要修改 Makefile,在 2 目录下的 Makefile 中添加变量 KBUILD_EXTRA_SYMBOLS+=$(shell pwd)/../1/Module.symvers ,且不再需要拷贝符号表文件。
</aside>
编译完成的 demo2.ko 可以通过 modinfo 查看到它的依赖模块:
filename: /sys_import/2/demo2.ko
license: GPL
srcversion: 160996D8382541A4A59A2DE
depends: demo1
retpoline: Y
name: demo2
vermagic: 6.2.0-24-generic SMP preempt mod_unload modversions
通过按顺序加载 insmod demo1.ko 和 insmod demo2.ko ,可以在 dmesg 中查看到输出结果:
[38917.418050] add(3, 4) = 7
卸载模块时,需要先卸载 demo2 再卸载 demo1