嵌入式分享#57:为什么 Kernel 内置驱动能 “上电自启”?

伍华  2026-02-24 11:52:11  阅读 7 次

欢迎关注“嵌入式分享”,每周更新! 正文

从接触 Linux 系统开始,我们就知道内核内置(built-in)驱动会在系统上电启动时自动加载,而编译为.ko文件的驱动则需要手动通过insmod加载。

这看似顺理成章的差异,背后实则是内核对驱动初始化机制的精巧设计 —— 核心在于module_init和module_exit这两个宏在不同编译模式下的实现逻辑差异。

要理解这一点,首先需要明确一个前提:无论是内置驱动还是可加载模块,其初始化和卸载的核心逻辑都是通过module_init(入口)和module_exit(出口)定义的。两者的差异并非 “是否执行这些函数”,而是 “何时、如何触发这些函数的执行”。

有了以上思路,问题就好办了。

先从module_init/module_exit 入手,阅读源码(源码路径:include/linux/module.h)知道,这两个宏根据驱动是否编译为模块,会展开为不同的代码,从而决定初始化函数的调用时机。

当驱动为内置模块(#ifndefMODULE

此时MODULE宏未定义,module_init被定义为__initcall(x):

嵌入式分享#57:为什么 Kernel 内置驱动能 “上电自启”?

__initcall(x):将函数x注册到内核的初始化调用队列中。内核启动时,会按优先级依次执行所有__initcall标记的函数(从early_initcall到late_initcall)。

编译时,这些初始化函数会被归类到内核的初始化段(如.init.text),内核启动时会按顺序执行这些函数。

因此,内置驱动的初始化函数会在系统启动阶段自动执行,无需手动干预。

当驱动为可加载模块

此时MODULE宏被定义(编译时通过-DMODULE指定),module_init和module_exit的定义完全不同。

module_init(initfn):将用户定义的initfn函数别名为init_module(内核模块加载器约定的初始化入口)。 module_exit(exitfn):将用户定义的exitfn函数别名为cleanup_module(模块卸载时的入口)。

当通过insmod加载.ko文件时,内核会调用init_module函数;通过rmmod卸载时,会调用cleanup_module函数。这两个函数与用户定义的initfn/exitfn是同一个函数(通过alias属性关联)。

总结

内置驱动与可加载模块的加载差异,本质是module_init宏在不同编译模式下的实现分流:

可加载模块通过 “函数别名” 将初始化逻辑绑定到insmod触发的标准入口,属于 “用户态手动触发”;

内置驱动通过 “初始化调用链” 将初始化逻辑注册到内核启动流程,属于 “内核态自动执行”。

这种设计既保证了系统关键驱动的自动初始化(确保启动流程顺畅),又提供了非关键驱动的动态加载能力(提升灵活性、节省内存),是 Linux 内核设备模型 “模块化” 与 “启动可靠性” 的精妙平衡。

(完)

本人专注 Linux 驱动 & Linux/Android BSP 开发调试,可接外包项目/技术支持/问题定位。有需求可加微信:【Chen_WeChat2026】。

更多原创技术文章:《README 2026》

审核编辑 黄宇

本文地址:https://www.shazhe.cn/huagu1221.html
免责声明:本文为原创文章,版权归 伍华 所有,欢迎分享本文,转载请保留出处!

评论已关闭!