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导出。

延伸问答

Linux内核模块如何进行通信?

Linux内核模块可以通过导出符号的方式进行通信,使用EXPORT_SYMBOL导出函数或变量,其他模块通过extern声明来调用。

什么是kallsyms,如何启用它?

kallsyms是Linux内核的符号表,包含所有导出符号。要启用它,需要在编译内核时将CONFIG_KALLSYMS设置为y。

EXPORT_SYMBOL和EXPORT_SYMBOL_GPL有什么区别?

EXPORT_SYMBOL将符号对所有内核代码公开,而EXPORT_SYMBOL_GPL仅允许GPL许可的模块调用这些符号。

如何解决Unknown symbol错误?

Unknown symbol错误通常是因为未正确加载依赖模块,确保先加载被调用模块,再加载调用模块。

模块的全局符号如何导出?

模块的全局符号必须在模块文件的全局部分定义,并使用EXPORT_SYMBOL导出,调用模块需进行extern声明。

如何查看内核符号表中的符号?

可以通过访问/proc/kallsyms查看内核符号表中的符号,但需要root权限才能看到真实地址。

🏷️

标签

➡️

继续阅读