真实面经题目 · 原创解析
Linux 内核模块的 module_init 底层原理是什么?
这题考内核模块初始化机制,回答要说明 module_init 宏把初始化函数注册到特定入口,加载模块时内核完成 ELF 装载、符号解析、重定位后调用它。
真实面经题目 · 原创解析
这题考内核模块初始化机制,回答要说明 module_init 宏把初始化函数注册到特定入口,加载模块时内核完成 ELF 装载、符号解析、重定位后调用它。
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 中发生。
开发者写 module_init(my_init),编译后会生成模块加载器能识别的入口关系。它不是驱动设备已经初始化,只是模块代码被加载后要执行的第一段初始化逻辑。
内核加载 .ko 时要验证版本、分配内存、处理 section、解析导入符号、应用重定位并建立模块对象。只有这些成功后才调用 init。
init 返回 0 表示模块加载成功,后续可被使用和卸载;返回负错误码表示失败,模块加载器会撤销模块内存和状态,调用者看到加载失败。
同样的宏在 built-in 场景下会把函数放到 initcall 段,随内核启动阶段被调用,而不是通过 insmod 触发。
module_init 常做的是注册驱动到某个总线。只有总线发现匹配设备后,probe 才执行设备级初始化。
通常不应该,除非资源已明确可用。多数驱动应在 probe 中根据具体设备资源映射寄存器并初始化硬件。
一般不会按正常卸载路径调用 module_exit,因此 init 失败路径要自己清理已经申请的资源。
我会先看 dmesg 和返回码,再查 /sys 下设备、驱动和资源状态,必要时加 trace/ftrace 或最小日志,先证明问题出在设备生成、匹配、资源获取、回调路径还是用户态交互。