60 秒回答模板

module_init 不是运行时魔法函数调用,而是一个宏。对于可加载模块,它会把你写的初始化函数声明成模块入口,最终对应 init_module 路径;insmod/modprobe 把 .ko 交给内核后,内核模块加载器会检查 ELF、license、vermagic,分配模块内存,加载代码和数据段,解析依赖符号,做重定位,建立 sysfs/module 状态,然后调用该 init 函数。如果 init 返回 0,模块进入 live 状态;返回错误,加载失败并释放已分配资源。对于内建进内核的代码,module_init 会把函数指针放到相应 initcall 段,启动过程中按 initcall 等级调用。驱动开发里 module_init 里通常注册 bus driver、platform_driver、i2c_driver、字符设备等,真正设备初始化往往在后续 probe 或 open 中发生。

考点 加载流程
难度 真实面经题
回答目标 证明自己理解内核模块入口、加载器流程和驱动注册/探测的区别。

深入解析

01

宏定义连接入口

开发者写 module_init(my_init),编译后会生成模块加载器能识别的入口关系。它不是驱动设备已经初始化,只是模块代码被加载后要执行的第一段初始化逻辑。

02

可加载模块先完成装载

内核加载 .ko 时要验证版本、分配内存、处理 section、解析导入符号、应用重定位并建立模块对象。只有这些成功后才调用 init。

03

返回值决定模块状态

init 返回 0 表示模块加载成功,后续可被使用和卸载;返回负错误码表示失败,模块加载器会撤销模块内存和状态,调用者看到加载失败。

04

内建代码走 initcall

同样的宏在 built-in 场景下会把函数放到 initcall 段,随内核启动阶段被调用,而不是通过 insmod 触发。

05

驱动注册和设备探测是两步

module_init 常做的是注册驱动到某个总线。只有总线发现匹配设备后,probe 才执行设备级初始化。

易错点

  • 把 module_init 当成每个设备的 probe。
  • 不知道可加载模块需要符号解析和重定位。
  • init 失败路径不清理已注册资源。
  • 不区分模块编译成 ko 和内建进内核时的调用路径。

面试官追问

module_init 里能直接访问设备寄存器吗?

通常不应该,除非资源已明确可用。多数驱动应在 probe 中根据具体设备资源映射寄存器并初始化硬件。

init 函数返回错误后 module_exit 会调用吗?

一般不会按正常卸载路径调用 module_exit,因此 init 失败路径要自己清理已经申请的资源。

如果面试官让你现场排查,你会先看哪些证据?

我会先看 dmesg 和返回码,再查 /sys 下设备、驱动和资源状态,必要时加 trace/ftrace 或最小日志,先证明问题出在设备生成、匹配、资源获取、回调路径还是用户态交互。