前言
EXPORT_SYMBOL
宏的使用时出现在 Linux 2.6 版本之后,在 Linux 2.4 内核中,默认的非 static 函数和变量都会自动导入到内核空间,不需要用 EXPORT_SYMBOL()
做标记的。出于安全考虑,不久之后 Linux 2.6 修改为默认不导出所有的符号,需要导出的符号使用 EXPORT_SYMBOL()
进行标记。
一、EXPORT_SYMBOL 宏的作用
EXPORT_SYMBOL
标签内指定的符号(函数或变量)对全部内核代码公开,不用修改内核代码就可以在内核模块中直接调用,即使用 EXPORT_SYMBOL
可以将一个函数以符号的方式导出给其他模块使用。简单来说,EXPORT_SYMBOL
的作用就是导出符号到内核符号表以供其他模块使用。
符号的意思就是函数的入口地址,或者说是把这些符号和对应的地址保存起来的,在内核运行的过程中,可以找到这些符号对应的地址的。
EXPORT_SYMBOL_GPL 宏
除了使用 EXPORT_SYMBOL
宏,Linux 内核还提供了 EXPORT_SYMBOL_GPL
宏,它们的作用都是将符号导出到内核符号表。区别在于 EXPORT_SYMBOL_GPL
仅将符号导出到 GPL 许可的模块。
EXPORT_SYMBOL exports the symbol to any loadable module.
EXPORT_SYMBOL_GPL exports the symbol only to GPL-licensed modules.
二、EXPORT_SYMBOL 使用方法
- 在模块函数定义之后使用
EXPORT_SYMBOL(函数名)
- 在调用该函数的模块中使用
extern
对要使用的符号或者函数进行声明 - 首先加载定义该函数的模块,再加载调用该函数的模块
使用范例
可以看到,mg_cpld 定义了接口函数 common_lpc_to_cpld_get(),并且使用 EXPORT_SYMBOL_GPL 导出该函数。psu 模块使用 extern 表示 common_lpc_to_cpld_get() 已在其他模块中定义。
root@local:/home/admin#
root@local:/home/admin# insmod mg_cpld.ko
root@local:/home/admin# insmod cpld_psu_ym2651y.ko
root@local:/home/admin# dmesg -c
[ 569.496376] psu_mod_init success!
可以看到,先加载定义了 common_lpc_to_cpld_get() 函数的 mg_cpld.ko,接着加载引用了 common_lpc_to_cpld_get() 函数的 cpld_psu_ym2651y.ko,正常。
root@local:/home/admin# rmmod mg_cpld.ko
root@local:/home/admin# rmmod cpld_psu_ym2651y.ko
root@local:/home/admin# dmesg -c
[ 694.830738] psu_mod_exit success!
root@local:/home/admin#
root@local:/home/admin# insmod cpld_psu_ym2651y.ko
insmod: ERROR: could not insert module cpld_psu_ym2651y.ko: Unknown symbol in module
root@local:/home/admin# dmesg -c
[ 725.700745] cpld_psu_ym2651y: Unknown symbol common_lpc_to_cpld_get (err 0)
root@local:/home/admin#
可以看到,如果我们先加载引用了 common_lpc_to_cpld_get() 函数的 cpld_psu_ym2651y.ko,而不是先加载定义了 common_lpc_to_cpld_get() 函数的 mg_cpld.ko,就会有依赖错误:找不到 common_lpc_to_cpld_get() 标识符。
注意:如果 export 导出的函数是静态函数,编译时可能会产生警告甚至报错。
三、EXPORT_SYMBOL 的另一种补充
EXPORT_SYMBOL
会导致 linux 驱动出现模块依赖的问题。
如果多个驱动模块都是调用同一个 公共接口代码
的库(common code),我们也可以把所有的公共接口代码单独存放一个文件,然后把该文件编译进驱动模块中。
示例如下:
公共代码库:
Makefile:将公共代码与驱动源文件一起编译成一个 .ko 驱动模块