linux模块导出符号 EXPORT_SYMBOL_GPL EXPORT_SYMBOL

本文详细介绍了Linux内核中EXPORT_SYMBOL的功能及使用方法,包括如何通过此宏定义导出函数供其他模块使用,以及如何正确声明和加载相关模块。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一个模块mod1中定义一个函数func1;在另外一个模块mod2中定义一个函数func2,func2调用func1。
在模块mod1中,EXPORT_SYMBOL(func1);
在模块mod2中,extern int func1();
就可以在mod2中调用func1了。

=================================

EXPORT_SYMBOL只出现在2.6内核中,在2.4内核默认的非static 函数和变量都会自动导入到kernel 空间的, 都不用EXPORT_SYMBOL() 做标记的。
2.6就必须用EXPORT_SYMBOL() 来导出来(因为2.6默认不到处所有的符号)。

1、EXPORT_SYMBOL的作用是什么?
EXPORT_SYMBOL标签内定义的函数或者符号对全部内核代码公开,不用修改内核代码就可以在您的内核模块中直接调用,即使用EXPORT_SYMBOL可以将一个函数以符号的方式导出给其他模块使用。
这里要和System.map做一下对比:
System.map 中的是连接时的函数地址。连接完成以后,在2.6内核运行过程中,是不知道哪个符号在哪个地址的。
EXPORT_SYMBOL的符号,是把这些符号和对应的地址保存起来,在内核运行的过程中,可以找到这些符号对应的地址。而模块在加载过程中,其本质就是能动态连接到内核,如果在模块中引用了内核或其它模块的符号,就要EXPORT_SYMBOL这些符号,这样才能找到对应的地址连接。

2、使用方法
   第一、在模块函数定义之后使用EXPORT_SYMBOL(函数名)
   第二、在掉用该函数的模块中使用extern对之声明
   第三、首先加载定义该函数的模块,再加载调用该函数的模块

另外,在编译调用某导出函数的模块时,往往会有WARNING: "****" [**********] undefined!
使用dmesg命令后会看到相同的信息。开始我以为只要有这个错误就不能加载模块,后来上网查了一下,发现这主要是因为在编译连接的时候还没有和内核打交道,当然找不到symbol了,但是由于你生成的是一个内核模块,所以LD不提示error,而是给出一个warning,寄希望于在insmod的时候,内核能够把这个symbol连接上。

 

 

Linux内核头文件提供了一个方便的方法用来管理符号的对模块外部的可见性,因此减少了命名空间的污染(命名空间的名称可能会与内核其他地方定义的名称冲突),并且适当信息隐藏。 如果你的模块需要输出符号给其他模块使用,应当使用下面的宏定义:
EXPORT_SYMBOL(name);
EXPORT_SYMBOL_GPL(name);
这两个宏均用于将给定的符号导出到模块外. _GPL版本的宏定义只能使符号对GPL许可的模块可用。 符号必须在模块文件的全局部分导出,不能在函数中导出,这是因为上述这两个宏将被扩展成一个特殊用途的声明,而该变量必须是全局的。这个变量存储于模块的一个特殊的可执行部分(一个"ELF段" ),在装载时,内核通过这个段来寻找模块导出的变量(感兴趣的读者可以看<linux/module.h>获知更详细的信息)。

 


<think>嗯,用户问的是为什么Linux内核中要用EXPORT_SYMBOL或者EXPORT_SYMBOL_GPL导出符号。首先,我得回忆一下这两个宏的作用。它们都是用来将内核中的函数或者变量导出,使得其他模块可以使用这些符号。不过用户可能已经知道这点,所以需要更深入的解释。 首先,用户可能想知道为什么需要显式地导出符号,而不是默认所有符号都可用。这时候需要解释内核模块的动态加载机制,模块是独立的,不能自动访问内核或其他模块符号,除非显式导出。这样设计的好处是减少耦合,增强安全性,避免模块之间的意外依赖。 然后,用户可能对EXPORT_SYMBOLEXPORT_SYMBOL_GPL的区别感兴趣。需要解释GPL版本是要求使用该符号模块也必须遵循GPL协议,这是LinuxGPL兼容性的强制措施。而普通的EXPORT_SYMBOL则没有这个限制,但可能用于非GPL的代码,这可能涉及到许可证的问题。 接下来,用户可能想知道实际应用中的例子。比如内核中的某些子系统导出函数供设备驱动使用,或者硬件厂商的驱动如何利用这些导出符号。这时候需要举一些例子,比如文件系统操作、内存管理函数等,这些通常会被导出。 另外,用户可能想了解导出符号的机制,比如符号表在内核中的存储方式,模块加载时如何解析这些符号。这部分可能需要稍微深入一点,但保持简洁,避免过于技术化。 还需要提到使用这些宏的注意事项,比如不能随意导出符号,否则会导致内核接口的不稳定,或者安全风险。内核开发者需要谨慎决定哪些符号需要导出,保持最小权限原则。 用户可能还会有疑问,如果模块导出符号,是否就无法被其他模块使用?这时候需要解释静态链接和动态加载的区别,以及模块之间的依赖关系如何处理。 最后,总结一下这两个宏的作用和重要性,强调它们在Linux内核模块化设计中的关键角色,以及维护内核健康生态的意义。</think>在Linux内核开发中,`EXPORT_SYMBOL` 和 `EXPORT_SYMBOL_GPL` 是用于将函数或变量(统称为“符号”)导出给其他内核模块使用的关键机制。以下是其设计目的和用法的分步解释: --- ### 1. **内核模块化的需求** Linux内核模块化设计的,允许通过动态加载模块(`.ko`文件)扩展功能。但默认情况下: - **模块间隔离**:每个模块是独立的,无法直接访问其他模块内核内部的符号(函数、变量)。 - **符号共享需求**:某些基础功能(如内存分配`kmalloc`、设备驱动接口)需要被多个模块复用。 **解决方式**:通过显式导出符号,声明哪些符号可以被外部模块访问。 --- ### 2. **`EXPORT_SYMBOL` 的作用** - **导出符号**:将符号添加到内核的全局符号表中,允许其他模块调用它。 - **示例**: ```c // 在内核源码中定义并导出一个函数 void my_function(void) { // ... } EXPORT_SYMBOL(my_function); // 导出符号 ``` - **使用场景**:允许任何模块(无论是否开源或遵循GPL)使用该符号。 --- ### 3. **`EXPORT_SYMBOL_GPL` 的作用** - **限制导出范围**:仅允许遵循**GPL许可证**的模块使用该符号。 - **法律约束**:强制调用者遵守GPL协议,避免闭源模块滥用内核功能。 - **示例**: ```c // 导出一个仅限GPL模块使用的函数 void my_gpl_function(void) { // ... } EXPORT_SYMBOL_GPL(my_gpl_function); // 仅GPL模块可用 ``` --- ### 4. **为何需要显式导出?** - **安全性**:避免内核内部接口被滥用。未导出符号对其他模块不可见,减少攻击面。 - **稳定性**:内核开发者仅维护导出的接口,隐藏内部实现细节。 - **许可证合规**:通过`EXPORT_SYMBOL_GPL`保护GPL代码的“传染性”。 --- ### 5. **技术实现** - **符号存储**:导出符号会被记录在`/proc/kallsyms`(内核符号表)中。 - **模块加载过程**: 1. 模块加载时,通过符号表解析依赖的符号。 2. 若符号导出,加载会失败(报错`Unknown symbol`)。 --- ### 6. **实际应用场景** - **设备驱动**:导出硬件操作接口供其他驱动复用。 - **核心子系统**:如文件系统、网络栈导出通用API。 - **开源与闭源冲突**:闭源驱动无法使用`EXPORT_SYMBOL_GPL`导出符号。 --- ### 7. **总结** - **`EXPORT_SYMBOL`**:通用导出,无许可证限制。 - **`EXPORT_SYMBOL_GPL`**:仅限GPL兼容模块使用。 - **核心价值**:平衡模块扩展、代码安全性和法律合规性。 --- 通过这种机制,Linux内核既实现了灵活的模块扩展,又保护了核心代码的稳定性和开源生态的健康发展。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值