EXPORT_SYMBOL,extern关键字在linux中的使用

本文介绍了Linux内核中的符号表/proc/kallsyms的作用及使用方法,并通过实例展示了如何利用EXPORT_SYMBOL宏导出模块内的函数供其他模块调用,同时解释了extern关键字在跨模块函数调用中的作用。

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

 

linux的“/proc/kallsyms”文件对应着内核符号表,记录了符号以及符号所在的内存地址。

模块可以使用如下宏导出符号到内核符号表:

  1. EXPORT_SYMBOL(符号名);  
  2. EXPORT_SYMBOL_GPL(符号名)  

      导出的符号可以被其他模块使用,不过使用之前一定要声明一下。EXPORT_SYMBOL_GPL()只适用于包含GPL许可权的模块。

代码演示:

//hello.c文件,定义2个函数,用于导出  
#include <linux/init.h>  
#include <linux/module.h>  
MODULE_LICENSE("Dual BSD/GPL");  
int add_integar(int a,int b)  
{  
    return a + b;  
}  
int sub_integar(int a,int b)  
{  
    return a - b;  
}  
EXPORT_SYMBOL(add_integar);  
EXPORT_SYMBOL(sub_integar);  
//test.c 用于调用hello模块导出的函数  
#include <linux/init.h>  
#include <linux/module.h>  
MODULE_LICENSE("Dual BSD/GPL");  
extern int add_integar(int ,int); //声明要调用的函数  
extern int sub_integar(int ,int); //声明要调用的函数  
int result(void)  
{  
    int a,b;  
    a = add_integar(1,1);  
    b = sub_integar(1,1);  
      
    printk("%d/n",a);  
    printk("%d/n",b);  
      
    return 0;  

make后,先加在hello模块,再加载test模块。

然后cat /proc/kallsyms | grep integer

显示:

[root@localhost test]# cat /proc/kallsyms |grep integar  
e053d000 u add_integar  [test]  
e053d004 u sub_integar  [test]  
e053d02c r __ksymtab_sub_integar        [hello]  
e053d03c r __kstrtab_sub_integar        [hello]  
e053d034 r __ksymtab_add_integar        [hello]  
e053d048 r __kstrtab_add_integar        [hello]  
e053d000 T add_integar  [hello]  
e053d004 T sub_integar  [hello]  

转载于:http://blog.youkuaiyun.com/lisan04/article/details/4076013#comments

extern关键字用法:

在C语言中,修饰符extern用在变量或者函数的声明前,用来说明“此变量/函数是在别处定义的,要在此处引用”。

1. extern修饰变量的声明。

      举例来说,如果文件a.c需要引用b.c中变量int v,就可以在a.c中声明extern int v,然后就可以引用变量v。这里需要注意的是,被引用的变量v的链接属性必须是外链接(external)的,也就是说a.c要引用到v,不只是取决于在a.c中声明extern int v,还取决于变量v本身是能够被引用到的。这涉及到c语言的另外一个话题--变量的作用域。能够被其他模块以extern修饰符引用到的变量通常是全局变量。还有很重要的一点是,extern int v可以放在a.c中的任何地方,比如你可以在a.c中的函数fun定义的开头处声明extern int v,然后就可以引用到变量v了,只不过这样只能在函数fun作用域中引用v罢了,这还是变量作用域的问题。对于这一点来说,很多人使用的时候都心存顾虑。好像extern声明只能用于文件作用域似的。

2. extern修饰函数声明。

       从本质上来讲,变量和函数没有区别。函数名是指向函数二进制块开头处的指针。如果文件a.c需要引用b.c中的函数,比如在b.c中原型是int fun(int mu),那么就可以在a.c中声明extern int fun(int mu),然后就能使用fun来做任何事情。就像变量的声明一样,extern int fun(int mu)可以放在a.c中任何地方,而不一定非要放在a.c的文件作用域的范围中。对其他模块中函数的引用,最常用的方法是包含这些函数声明的头文件。使用extern和包含头文件来引用函数有什么区别呢?extern的引用方式比包含头文件要简洁得多!extern的使用方法是直接了当的,想引用哪个函数就用extern声明哪个函数。这大概是KISS原则的一种体现吧!这样做的一个明显的好处是,会加速程序的编译(确切的说是预处理)的过程,节省时间。在大型C程序编译过程中,这种差异是非常明显的。

3. 此外,extern修饰符可用于指示C或者C++函数的调用规范。

       比如在C++中调用C库函数,就需要在C++程序中用extern “C”声明要引用的函数。这是给链接器用的,告诉链接器在链接的时候用C函数规范来链接。主要原因是C++和C程序编译完成后在目标代码中命名规则不同。

相关链接:C语言中extern关键字的使用 http://www.linuxidc.com/Linux/2012-10/73003.htm

<think>好的,我现在需要解决用户关于使用EXPORT_SYMBOL_GPL导致Unknown symbol的问题。首先,我需要回顾一下EXPORT_SYMBOLEXPORT_SYMBOL_GPL的基本作用。根据引用[1]和[3],EXPORT_SYMBOL用于将内核符号导出,使得其他模块可以使用这些符号,而EXPORT_SYMBOL_GPL则限制只有遵循GPL许可的模块才能访问这些符号。 接下来,用户的问题可能是由于模块的许可证声明不正确导致的。根据引用[3],如果使用EXPORT_SYMBOL_GPL导出的符号,调用它的模块必须声明为GPL兼容的许可证,比如MODULE_LICENSE("GPL")。如果用户模块没有正确声明,就会导致找不到符号的错误。这时候需要检查模块的许可证声明是否正确。 另一个可能的原因是符号导出后未被正确链接。根据引用[4],可能存在符号表不统一的问题,需要确保导出的符号在目标模块的符号表中存在。使用命令如`nm`或查看`/proc/kallsyms`可以验证符号是否正确导出。如果符号没有出现在这些列表中,可能需要重新编译内核或模块,确保导出语句被正确处理。 还有可能是内核版本或配置问题。某些情况下,内核配置选项可能影响符号的导出,比如某些功能被编译为内置而非模块,或者配置选项关闭了相关功能。需要检查内核的.config文件,确认相关配置是否开启。 此外,模块加载顺序也可能导致问题。如果依赖的模块在目标模块之前未加载,符号可能不可用。使用modprobe或调整加载顺序可能解决此问题。 最后,如果所有检查都正确,但问题仍然存在,可能需要查阅内核文档或社区支持,确认是否存在已知问题或特定版本的限制。例如,某些内核版本可能对GPL符号的导出有更严格的检查,需要相应的补丁或更新。 总结下来,解决步骤应包括检查许可证声明、验证符号导出、确认内核配置、调整模块加载顺序,以及查阅文档或社区资源。通过这些步骤,应该能定位并解决Unknown symbol的问题。</think>### EXPORT_SYMBOL_GPL 导致 Unknown symbol 错误的原因及解决方法 #### **错误原因** 1. **模块许可证不兼容** - 使用 `EXPORT_SYMBOL_GPL` 导出的符号仅允许遵循 **GPL 兼容许可证**(如 `GPL`、`MIT` 等)的模块访问。如果调用该符号的模块未声明为 `GPL` 许可证(例如使用 `MODULE_LICENSE("Proprietary")`),会导致符号不可见[^3]。 2. **符号未正确导出** - 导出符号的代码可能未正确编译到内核或模块中,例如: - 导出语句未放置在内核符号表的有效位置(如函数定义之后)。 - 内核配置(`.config`)未启用 `CONFIG_MODULES` 或相关依赖项[^4]。 3. **模块加载顺序问题** - 如果导出符号的模块(如驱动A)尚未加载,而依赖该符号的模块(如驱动B)已加载,会导致符号解析失败[^4]。 --- #### **解决方法** 1. **检查模块许可证声明** - 在调用 `EXPORT_SYMBOL_GPL` 符号的模块中,确保声明了 GPL 兼容许可证: ```c MODULE_LICENSE("GPL"); ``` 2. **验证符号是否成功导出** - 使用 `nm` 命令或查看 `/proc/kallsyms` 文件,确认符号已存在于内核符号表: ```bash nm <模块文件>.ko | grep <符号名> grep <符号名> /proc/kallsyms ``` 3. **检查内核配置与编译** - 确保内核配置启用了模块支持(`CONFIG_MODULES=y`)。 - 重新编译内核或模块,确保导出语句未被优化或遗漏。 4. **调整模块加载顺序** - 先加载导出符号的模块,再加载依赖它的模块: ```bash sudo insmod driverA.ko # 导出符号的模块 sudo insmod driverB.ko # 使用符号的模块 ``` 5. **使用 EXPORT_SYMBOL 替代(仅限非GPL场景)** - 如果符号无需 GPL 限制,改用 `EXPORT_SYMBOL` 导出[^2][^3]。 --- #### **示例代码对比** ```c // 导出符号的模块(driverA.c) void my_function(void) { /* ... */ } EXPORT_SYMBOL_GPL(my_function); // 仅允许GPL模块使用 // 使用符号的模块(driverB.c) MODULE_LICENSE("GPL"); // 必须声明为GPL extern void my_function(void); ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kevin@1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值