ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

【译】《心悟内核:先懂设计,再读代码》—24、内核模块如何互相感知:仅能通过导出符号_FINISH

【译】《心悟内核:先懂设计,再读代码》—24、内核模块如何互相感知:仅能通过导出符号_FINISH

作者:Moon Hee Lee
原文: The Kernel in the Mind

内核模块如何互相感知:仅能通过导出符号

  • 内核模块是独立编译的内核功能单元,设计为可在运行时加载。模块提供了一种扩展内核的灵活方式 —— 通常用于设备驱动、文件系统、密码学例程或协议实现,无需完整重新编译内核或重启系统。一旦插入,模块就成为运行中内核的一部分,运行在特权空间,可完整访问内核环境。

  • 尽管拥有这样的访问权限,内核模块在设计上是相互隔离的。除非内核函数或变量被显式导出,否则模块无法引用或调用它们。内核不提供自动发现、后期绑定或符号查找机制。所有交互都必须通过由EXPORT_SYMBOL或EXPORT_SYMBOL_GPL声明的预定义接口完成。

  • 该边界是有意设计的。内核不保证稳定的内部 ABI,并且仅对导出接口提供支持。任何未显式导出的符号都视为内部符号,在不同版本间可能发生变更或被移除。导出接口构成模块与内核其余部分之间唯一受支持的边界。

  • 加载模块时,模块内的未定义符号会与全局符号表进行匹配,该符号表仅包含内核选择对外暴露的标识符。如果缺少所需符号,模块加载失败。符号解析仅发生一次 —— 在加载阶段,无法动态调整。

  • 模块不能访问彼此的内部符号,除非这些符号被显式导出。无论模块是否联合开发、是否在同一配置下编译、或是存放于相关源码目录,都没有区别。没有EXPORT_SYMBOL,符号就是不可见的。从模块视角来看,未导出的符号等同于不存在。

  • 模块分层与堆叠十分常见,但必须严格显式定义。一个模块可以依赖另一个模块注册回调、提供处理程序表或暴露工具函数 —— 但只能通过导出符号和已建立的注册点。不存在运行时自动发现与连接。

  • 这并非技术限制,而是刻意的架构决策。内核强制执行这种隔离,以保留内部灵活性、版本无关性以及系统完整性。task_struct、cred、mm_struct这类核心结构体是内核行为的关键,除非通过安全接口有意对外暴露,否则永远不能直接访问。

  • 无论模块与内核功能结合得多紧密,每个模块都被当作外部代码。模块必须声明许可证,提前解析依赖,并且访问范围仅限于内核显式选择暴露的接口。

  • 内核模块只能通过导出符号感知彼此,没有例外。这不是约定,而是强制实施的设计:精确、有意,对 Linux 内核的长期稳定至关重要。

如本文对你有些许帮助,欢迎大佬支持我一下(点赞+收藏+关注、关注公众号等),您的支持是我持续创作的竭动力
支持我的方式

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进