首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >module_init(hello_init) 宏展开深度剖析

module_init(hello_init) 宏展开深度剖析

作者头像
一个平凡而乐于分享的小比特
发布2026-02-02 17:20:14
发布2026-02-02 17:20:14
910
举报

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

在这里插入图片描述
在这里插入图片描述

module_init(hello_init) 宏展开深度剖析

这是一个关于 Linux 内核模块初始化的深度技术问题。我来详细分析 module_init(hello_init) 如何展开为最终的复杂定义。

1. 宏观流程概览

代码语言:javascript
复制
module_init(hello_init)
↓ 宏展开(多级展开)
↓ __define_initcall(level, fn, id)
↓ ____define_initcall(fn, id, __sec)
↓ 最终:声明一个特定section的函数指针变量

2. 逐层展开分析

第一层:module_init 宏定义

include/linux/module.h 中:

代码语言:javascript
复制
#define module_init(initfn) \
    __module_init_call(initfn, __i686)  /* 针对不同架构 */

展开后变成:

代码语言:javascript
复制
__module_init_call(hello_init, __i686)
第二层:__module_init_call
代码语言:javascript
复制
#define __module_init_call(fn, secid) \
    __module_init_call_name(fn, __initcall_##fn##secid)

展开为:

代码语言:javascript
复制
__module_init_call_name(hello_init, __initcall_hello_init__i686)
第三层:__module_init_call_name
代码语言:javascript
复制
#define __module_init_call_name(fn, __name) \
    static initcall_t __name __used \
    __attribute__((__section__(".initcall" #fn ".init"))) = fn

这里开始变得复杂:

  • __name = __initcall_hello_init__i686
  • initcall_ttypedef int (*initcall_t)(void);
  • __used 确保编译器不优化掉这个变量

展开为:

代码语言:javascript
复制
static initcall_t __initcall_hello_init__i686 __used \
    __attribute__((__section__(".initcall" "hello_init" ".init"))) = hello_init
第四层:更现代的版本(当前内核)

实际当前内核中,module_init 的定义更简洁:

代码语言:javascript
复制
#define module_init(fn) \
    __initcall(fn, 6)

因为 6 对应 device_initcall 级别,这是大多数驱动模块使用的初始化级别。

第五层:__initcall 宏
代码语言:javascript
复制
#define __initcall(fn) device_initcall(fn)
#define device_initcall(fn) __define_initcall(fn, 6)

所以:

代码语言:javascript
复制
module_init(hello_init) → __define_initcall(hello_init, 6)
第六层:核心的 __define_initcall
代码语言:javascript
复制
#define __define_initcall(fn, id) \
    ____define_initcall(fn, \
        __initcall_##fn##id, \
        __initcall_##fn##id##_used, \
        id, \
        __initcall_##fn##id##_unused)

展开为:

代码语言:javascript
复制
____define_initcall(hello_init, 
    __initcall_hello_init6,          /* 变量名 */
    __initcall_hello_init6_used,     /* 用于__used */
    id,                              /* 6 */
    __initcall_hello_init6_unused)   /* 未使用参数 */
第七层:最终的 ____define_initcall
代码语言:javascript
复制
#define ____define_initcall(fn, __name, __unused, __id, __unused2) \
    static initcall_t __name __used \
    __attribute__((__section__(".initcall" #__id ".init"))) = fn

替换所有参数:

代码语言:javascript
复制
static initcall_t __initcall_hello_init6 __used \
    __attribute__((__section__(".initcall" "6" ".init"))) = hello_init
第八层:__used 的展开

__used 的定义:

代码语言:javascript
复制
#define __used __attribute__((__used__))

所以最终成为:

代码语言:javascript
复制
static initcall_t __initcall_hello_init6 __attribute__((__used__)) \
    __attribute__((__section__(".initcall" "6" ".init"))) = hello_init

3. 关键机制解析

3.1 Section 名称生成
  • .initcall6.init 是最终的 section 名称
  • 数字 6 表示初始化优先级(1-7,数字越小优先级越高)

各级初始化顺序:

代码语言:javascript
复制
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
3.2 变量命名规则
  • 变量名:__initcall_hello_init6
  • 加上 _used 后缀是旧版本机制
  • 现在的命名更统一:__initcall_<函数名><优先级>
3.3 内存布局

内核链接时,所有 .initcall6.init section 的内容会被收集到一起:

代码语言:javascript
复制
.initcall6.init section:
    __initcall_hello_init6 → 指向 hello_init 函数
    __initcall_other_driver6 → 指向其他驱动初始化函数
    ...
3.4 初始化调用流程

内核启动时,在 do_initcalls() 中:

代码语言:javascript
复制
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)();  // 执行初始化函数
        }
    }
}

4. 完整展开示例

假设我们有:

代码语言:javascript
复制
// 模块代码
static int __init hello_init(void) {
    printk(KERN_INFO "Hello world\n");
    return 0;
}
module_init(hello_init);

展开过程

代码语言:javascript
复制
第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

最终结果

代码语言:javascript
复制
static int (*initcall_t)(void) 
    __initcall_hello_init6  // 变量名
    __attribute__((__used__))  // 确保不被优化
    __attribute__((__section__(".initcall6.init")))  // 放入特定段
    = hello_init;  // 初始化为hello_init函数

5. 设计目的和优势

  1. 自动排序:通过section机制,链接器自动按优先级排序初始化函数
  2. 零运行时开销:排序在链接时完成,运行时直接遍历执行
  3. 模块化:每个模块独立,不需要中心注册表
  4. 优先级控制:不同子系统可以指定不同的初始化顺序
  5. 内存优化:初始化完成后,整个 .init section 可以被释放

6. 调试信息

查看实际的内存布局:

代码语言:javascript
复制
# 查看内核符号
readelf -s vmlinux | grep initcall | head -20

# 查看section内容
objdump -j .initcall6.init -s vmlinux

这个机制是Linux内核模块化架构的基石之一,通过巧妙的编译器和链接器特性,实现了高效、灵活的初始化系统。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-01-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • module_init(hello_init) 宏展开深度剖析
    • 1. 宏观流程概览
    • 2. 逐层展开分析
      • 第一层:module_init 宏定义
      • 第二层:__module_init_call
      • 第三层:__module_init_call_name
      • 第四层:更现代的版本(当前内核)
      • 第五层:__initcall 宏
      • 第六层:核心的 __define_initcall
      • 第七层:最终的 ____define_initcall
      • 第八层:__used 的展开
    • 3. 关键机制解析
      • 3.1 Section 名称生成
      • 3.2 变量命名规则
      • 3.3 内存布局
      • 3.4 初始化调用流程
    • 4. 完整展开示例
    • 5. 设计目的和优势
    • 6. 调试信息
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档