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

描述

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

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

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

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

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

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

当驱动为内置模块(#ifndef MODULE

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

Linux

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

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

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

当驱动为可加载模块

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

Linux 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》

审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分