1. Linux内核模块与符号导出的本质
当我们在Linux系统上插入一个内核模块时,这个模块本质上是一个可以动态加载到内核地址空间的二进制对象。与用户态程序不同,内核模块运行在特权模式下,直接操作硬件和内核数据结构。在这个过程中,模块间的函数和变量访问就成为了一个关键问题。
内核符号表就像是一个全局通讯录,记录了所有可供引用的函数和变量的地址。默认情况下,模块编译后生成的符号都是局部(local)的,只能在模块内部使用。这就像公司里每个部门都有自己的内部通讯录,其他部门无法直接查看。而符号导出机制,就是允许特定符号出现在全局通讯录中,让其他模块也能"打电话"联系到这些资源。
注意:内核符号的可见性不同于用户空间的动态链接。内核模块间的符号解析发生在加载时(insmod阶段),而不是运行时。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. EXPORT_SYMBOL家族详解
2.1 基础导出宏
EXPORT_SYMBOL()是最基础的导出宏,其实现本质是将符号添加到内核特殊的.gnu.linkonce.this_module段中。当模块加载时,这些符号会被注册到全局符号表。例如:
c复制// 模块A中导出函数
void shared_func(void) {
printk(KERN_INFO "Function called\n");
}
EXPORT_SYMBOL(shared_func);
// 模块B中使用
extern void shared_func(void);
shared_func();
2.2 带GPL约束的导出
EXPORT_SYMBOL_GPL()是Linux内核体现GPL许可的重要机制。被此宏导出的符号只能被标记为GPL兼容的模块使用:
c复制// 模块A
void gpl_func(void) { /*...*/ }
EXPORT_SYMBOL_GPL(gpl_func);
// 模块B必须声明MODULE_LICENSE("GPL")才能使用gpl_func
内核在module.c中通过check_module_license()函数验证调用者的许可证。这种设计强制要求衍生作品遵循GPL,是Linux法律架构的精妙之处。
2.3 新版导出宏变体
从内核2.6.
