Linux内核模块通信 | 符号导出解析
内容提要
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权限才能看到真实地址。