1. 概述

Linux 的进程有自己的用户空间,使用自己的虚拟内存空间,因此进程之间无法调用彼此的函数

UART分析框图.svg

而 Linux 的模块都加载在内核空间,共享内存,因此可以从一个模块访问另一个模块的资源,而这需要符号表的导出与导入

UART分析框图.svg

2. API

EXPORT_SYMBOL(变量名|函数名)
或者 
EXPORT_SYMBOL_GPL(变量名|函数名)

3. 导出实例

.
├── 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.koinsmod demo2.ko ,可以在 dmesg 中查看到输出结果:

[38917.418050] add(3, 4) = 7

卸载模块时,需要先卸载 demo2 再卸载 demo1