🔥作者简介: 一个平凡而乐于分享的小比特,中南民族大学通信工程专业研究生,研究方向无线联邦学习 🎬擅长领域:驱动开发,嵌入式软件开发,BSP开发 ❄️作者主页:一个平凡而乐于分享的小比特的个人主页 ✨收录专栏:Linux,本专栏目的在于,记录学习Linux操作系统的总结 欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖

module_init(hello_init) 宏展开深度剖析这是一个关于 Linux 内核模块初始化的深度技术问题。我来详细分析 module_init(hello_init) 如何展开为最终的复杂定义。
module_init(hello_init)
↓ 宏展开(多级展开)
↓ __define_initcall(level, fn, id)
↓ ____define_initcall(fn, id, __sec)
↓ 最终:声明一个特定section的函数指针变量在 include/linux/module.h 中:
#define module_init(initfn) \
__module_init_call(initfn, __i686) /* 针对不同架构 */展开后变成:
__module_init_call(hello_init, __i686)#define __module_init_call(fn, secid) \
__module_init_call_name(fn, __initcall_##fn##secid)展开为:
__module_init_call_name(hello_init, __initcall_hello_init__i686)#define __module_init_call_name(fn, __name) \
static initcall_t __name __used \
__attribute__((__section__(".initcall" #fn ".init"))) = fn这里开始变得复杂:
__name = __initcall_hello_init__i686initcall_t 是 typedef int (*initcall_t)(void);__used 确保编译器不优化掉这个变量展开为:
static initcall_t __initcall_hello_init__i686 __used \
__attribute__((__section__(".initcall" "hello_init" ".init"))) = hello_init实际当前内核中,module_init 的定义更简洁:
#define module_init(fn) \
__initcall(fn, 6)因为 6 对应 device_initcall 级别,这是大多数驱动模块使用的初始化级别。
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall(fn, 6)所以:
module_init(hello_init) → __define_initcall(hello_init, 6)#define __define_initcall(fn, id) \
____define_initcall(fn, \
__initcall_##fn##id, \
__initcall_##fn##id##_used, \
id, \
__initcall_##fn##id##_unused)展开为:
____define_initcall(hello_init,
__initcall_hello_init6, /* 变量名 */
__initcall_hello_init6_used, /* 用于__used */
id, /* 6 */
__initcall_hello_init6_unused) /* 未使用参数 */#define ____define_initcall(fn, __name, __unused, __id, __unused2) \
static initcall_t __name __used \
__attribute__((__section__(".initcall" #__id ".init"))) = fn替换所有参数:
static initcall_t __initcall_hello_init6 __used \
__attribute__((__section__(".initcall" "6" ".init"))) = hello_init__used 的定义:
#define __used __attribute__((__used__))所以最终成为:
static initcall_t __initcall_hello_init6 __attribute__((__used__)) \
__attribute__((__section__(".initcall" "6" ".init"))) = hello_init.initcall6.init 是最终的 section 名称6 表示初始化优先级(1-7,数字越小优先级越高)各级初始化顺序:
1: .initcall1.init - pure_early_initcall
2: .initcall2.init - core_early_initcall
3: .initcall3.init - postcore_early_initcall
4: .initcall4.init - arch_initcall
5: .initcall5.init - subsys_initcall
6: .initcall6.init - device_initcall(大部分驱动)
7: .initcall7.init - late_initcall__initcall_hello_init6_used 后缀是旧版本机制__initcall_<函数名><优先级>内核链接时,所有 .initcall6.init section 的内容会被收集到一起:
.initcall6.init section:
__initcall_hello_init6 → 指向 hello_init 函数
__initcall_other_driver6 → 指向其他驱动初始化函数
...内核启动时,在 do_initcalls() 中:
static void __init do_initcalls(void)
{
initcall_t *fn;
// 遍历所有优先级
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) {
// 遍历该优先级的所有初始化函数
for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++) {
(*fn)(); // 执行初始化函数
}
}
}假设我们有:
// 模块代码
static int __init hello_init(void) {
printk(KERN_INFO "Hello world\n");
return 0;
}
module_init(hello_init);展开过程:
第1步:module_init(hello_init)
第2步:__initcall(hello_init, 6)
第3步:__define_initcall(hello_init, 6)
第4步:____define_initcall(hello_init, __initcall_hello_init6, ...)
第5步:static initcall_t __initcall_hello_init6 __used \
__attribute__((__section__(".initcall6.init"))) = hello_init最终结果:
static int (*initcall_t)(void)
__initcall_hello_init6 // 变量名
__attribute__((__used__)) // 确保不被优化
__attribute__((__section__(".initcall6.init"))) // 放入特定段
= hello_init; // 初始化为hello_init函数.init section 可以被释放查看实际的内存布局:
# 查看内核符号
readelf -s vmlinux | grep initcall | head -20
# 查看section内容
objdump -j .initcall6.init -s vmlinux这个机制是Linux内核模块化架构的基石之一,通过巧妙的编译器和链接器特性,实现了高效、灵活的初始化系统。