Linux内核模块通信 | 符号导出解析
💡
原文中文,约18900字,阅读约需45分钟。
📝
内容提要
Linux内核提供了多种模块间通讯的方式,其中最便捷的方式之一是通过函数或变量符号导出,然后直接调用。内核维护着一个内核符号表,即kallsyms,其中包含了所有Linux内核中的导出符号。要启用kallsyms,需要在编译内核时将CONFIG_KALLSYMS设置为y。导出符号时,需要在模块函数定义之后使用EXPORT_SYMBOL(函数名),并在调用该函数的模块中使用extern进行声明。
🎯
关键要点
- Linux内核提供多种模块间通讯方式,最便捷的是通过导出符号直接调用。
- 模块间的全局变量默认相互独立,需通过EXPORT_SYMBOL导出才能被其他模块访问。
- EXPORT_SYMBOL()函数将函数或变量对全部内核代码公开,EXPORT_SYMBOL_GPL()仅适用于GPL许可的模块。
- 内核符号表kallsyms包含所有导出符号,需在编译时设置CONFIG_KALLSYMS为y才能启用。
- /proc/kallsyms可访问内核符号表,但在用户态下通常显示为0地址,需root权限查看真实地址。
- 模块编译时符号查找顺序为:本模块符号表 -> 内核全局符号表 -> Module.symvers文件。
- 内核符号表包含多种符号类型,如绝对符号、未初始化数据区符号、公共符号等。
- 使用EXPORT_SYMBOL导出符号时,调用模块需进行extern声明,否则编译报错。
- 模块加载顺序需先加载被调用模块,再加载调用模块,否则会出现Unknown symbol错误。
- EXPORT_SYMBOL_GPL导出的符号只能被GPL许可的模块调用,未包含GPL许可的模块会报错。
- 模块卸载需按顺序进行,先卸载调用模块,再卸载被调用模块。
- 模块的全局符号需在模块文件的全局部分输出,才能被其他模块访问。
- 内核默认不自动导出非静态全局变量和函数,需显式调用EXPORT_SYMBOL导出。
➡️