module_init的内核调用顺序

本文深入剖析了Linux内核中的initcall机制,详细解释了从module_init宏定义到实际函数调用的过程,以及不同级别的initcall如何组织和执行。

现在以module_init为例分析initcall在内核中的调用顺序

在头文件init.h中,有如下定义:

#define module_init(x)     __initcall(x);

#define __initcall(fn) device_initcall(fn)

继续看:

#define device_initcall(fn)              __define_initcall("6",fn,6)

#define __define_initcall(level,fn,id) \

       static initcall_t __initcall_##fn##id __used \

       __attribute__((__section__(".initcall" level ".init"))) = fn

最终我们看到的是module_init的真身:__define_initcall(level,fn,id),仔细推敲这个真身,知道这是个宏,它把传给module_init的函数名组装成以__initcall为前缀的、以6为后缀的函数名,并把这个函数定义到代码段.initcall6.init里面。

在代码段.initcall6.init里面?这函数躲在这里干嘛,啥时候才轮得到它出头啊!找到有此字符串的文件vmlinux.lds.h,相关代码如下所示:

#define INITCALLS                                           \

       *(.initcallearly.init)                                        \

       VMLINUX_SYMBOL(__early_initcall_end) = .;                  \

     *(.initcall0.init)                                       \

     *(.initcall0s.init)                                     \

     *(.initcall1.init)                                       \

     *(.initcall1s.init)                                     \

     *(.initcall2.init)                                       \

     *(.initcall2s.init)                                     \

     *(.initcall3.init)                                       \

     *(.initcall3s.init)                                     \

     *(.initcall4.init)                                       \

     *(.initcall4s.init)                                     \

     *(.initcall5.init)                                       \

     *(.initcall5s.init)                                     \

       *(.initcallrootfs.init)                                      \

     *(.initcall6.init)                                       \

     *(.initcall6s.init)                                     \

     *(.initcall7.init)                                       \

     *(.initcall7s.init)

在vmlinux.lds.S里面

__initcall_start = .;

                     INITCALLS

              __initcall_end = .;

              __con_initcall_start = .;

                     *(.con_initcall.init)

              __con_initcall_end = .;

              __security_initcall_start = .;

                     *(.security_initcall.init)

              __security_initcall_end = .;

言归正传,INITCALLS在__initcall_start = .与__initcall_end = .之间,表示INITCALLS宏内涵的相关段代码顺序存放在这里,module_init所代表段的镶嵌其中

#define pure_initcall(fn)          __define_initcall("0",fn,0)

 

#define core_initcall(fn)          __define_initcall("1",fn,1)

#define core_initcall_sync(fn)        __define_initcall("1s",fn,1s)

#define postcore_initcall(fn)           __define_initcall("2",fn,2)

#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)

#define arch_initcall(fn)          __define_initcall("3",fn,3)

#define arch_initcall_sync(fn)        __define_initcall("3s",fn,3s)

#define subsys_initcall(fn)              __define_initcall("4",fn,4)

#define subsys_initcall_sync(fn)    __define_initcall("4s",fn,4s)

#define fs_initcall(fn)                     __define_initcall("5",fn,5)

#define fs_initcall_sync(fn)            __define_initcall("5s",fn,5s)

#define rootfs_initcall(fn)        __define_initcall("rootfs",fn,rootfs)

#define device_initcall(fn)              __define_initcall("6",fn,6)

#define device_initcall_sync(fn)    __define_initcall("6s",fn,6s)

#define late_initcall(fn)           __define_initcall("7",fn,7)

#define late_initcall_sync(fn)         __define_initcall("7s",fn,7s)

至此我们应该明白了各个initcall是如何来的以及在代码存储空间上是怎么组织的了吧,下面来看看内核是什么时候调用它的。

内核启动流程如下所示

Main()àlinux_main()àstart_uml()àstart_kernel_proc()àstart_kernelàrest_inità kernel_inità do_basic_setupà do_initcalls

do_initcalls的代码如下所示

static void __init do_initcalls(void)

{

       initcall_t *call;

 

       for (call = __early_initcall_end; call < __initcall_end; call++)

              do_one_initcall(*call);

 

       /* Make sure there is no pending stuff from the initcall sequence */

       flush_scheduled_work();

}

上面的代码中,__early_initcall_end在INITCALLS内定义,__initcall_end在文件vmlinux.lds.S中定义,他们代表的是一些初始化函数的指针数组起始与结束地址,执行函数do_initcalls时,包含在这各指针数组里面的函数顺序的被调用以执行一些必要的初始化工作。至此,明白的各initcall的执行时刻了吧。

<think>我们正在讨论Linux内核中`module_init`的详细调用过程和实现细节。根据用户要求,我们需要结合之前的对话历史和提供的参考资料,深入解析`module_init`从宏展开到实际执行的完整流程,并注意使用LaTeX格式和引用标注。关键参考资料回顾:-引用[1]:描述了`SYSCALL_DEFINE3(init_module,...)`到`do_one_initcall()`的调用链,说明模块初始化函数是如何被调用的。-引用[2][3]:涉及structmodule结构体和模块内存布局,其中特别指出模块的初始化函数指针(.init)和内存划分(init段和core段)。-引用[4]:解释了__init属性的作用——将初始化函数放在特定section,并在初始化后释放内存。我们现在的任务是详细解释`module_init`的调用过程:1.从宏定义开始,展示如何将初始化函数挂载到内核的初始化调用链。2.说明模块加载时(通过insmod/modprobe)如何触发系统调用,并最终执行模块的初始化函数。3.结合structmodule结构,解释初始化函数的执行环境和内存管理细节(如init段的释放)。同时,我们需要遵循格式要求:数学表达式使用`$...$`或`$$...$$`,在引用内容后添加`[^数字]`标识,最后生成相关问题。思考步骤:1.解释`module_init`宏的本质——它通过链接器脚本将函数地址放入特定section(如.initcall)。2.描述模块加载的系统调用路径:从用户空间insmod到内核空间执行初始化函数的过程链。3.说明初始化函数执行的具体机制(通过`do_one_initcall()`调用模块的init函数)。4.讨论初始化函数的内存管理:__init函数在初始化完成后会被释放的原理。实现细节代码参考:-`module_init`宏定义(通常位于include/linux/init.h):```c#definemodule_init(initfn)\staticinlineinitcall_t__maybe_unused__inittest(void)\{returninitfn;}\intinit_module(void)__attribute__((alias(#initfn)));//简化版逻辑```更精确的实现是将函数指针放入`.initcall`段。-系统调用链(如引用[1]):```SYSCALL_DEFINE3(init_module,...)→load_module()→do_init_module()→do_one_initcall(mod->init)```-模块内存布局(引用[2][3]):structmodule中的module_initinit_size字段管理初始化段。最后,我们生成3-5个相关问题,围绕模块初始化机制、内存管理、调试技巧等。</think> ### `module_init` 调用过程详解 Linux 内核模块的初始化流程可分为以下四个阶段,核心实现基于**链接器脚本+函数指针链表+内存管理**的协作机制: --- ### 一、宏展开阶段:链接器脚本注入 `module_init()` 宏的本质是将函数指针存入 **`.initcall`段**: ```c // include/linux/init.h #define module_init(fn) __initcall(fn); #define __initcall(fn) device_initcall(fn) #define device_initcall(fn) __define_initcall(fn, 6) ``` **关键操作**: `__define_initcall(fn, id)` 通过链接器脚本将函数指针 `fn` 放入名为 `.initcall6.init` 的 ELF 段(数字 6 表示初始化优先级)[^1]。生成的链接脚本片段: ``` .initcall6.init : AT(ADDR(.initcall6.init) - LOAD_OFFSET) { *(.initcall6.init) // 此处注入模块的 init 函数地址 } ``` --- ### 二、加载触发阶段:系统调用链 用户空间 `insmod/modprobe` 触发的内核调用链: $$ \text{insmod} \xrightarrow{\text{syscall}} \text{SYSCALL\_DEFINE3(init\_module)} \xrightarrow{\text{load\_module}} \text{do\_init\_module} \xrightarrow{\text{do\_one\_initcall}} \text{模块 init 函数} $$ 1. **SYSCALL_DEFINE3**(系统调用入口): ```c // kernel/module.c SYSCALL_DEFINE3(init_module, ...) { return load_module(uargs, ...); // 调用模块加载器 [^1] } ``` 2. **load_module()**: - 解析 ELF 文件,构建 `struct module`(包含模块的 `.init.text` 地址) - 调用 `do_init_module()` 3. **do_init_module()**: ```c static int do_init_module(struct module *mod) { do_one_initcall(mod->init); // 执行模块初始化函数 mod->state = MODULE_STATE_LIVE; // 标记模块为活跃状态 free_init_mem(mod); // 释放 .init 段内存 [^4] } ``` --- ### 三、函数执行阶段:`do_one_initcall()` 此函数遍历并执行 `.initcall` 段中注册的函数: ```c // init/main.c int __init do_one_initcall(initcall_t fn) { return fn(); // 直接调用函数指针 } ``` **内存管理机制**: - `__init` 宏修饰的函数(如 `int __init foo(void)`)被链接到 `.init.text` 段 - 函数执行后,内核调用 `free_init_mem()` 释放整个 `.init` 段内存(包含 `.init.text` 和 `.init.data`)[^4] ```mermaid classDiagram struct module { +void *module_init // 指向 .init.text 起始地址 +void *module_core // 指向 .core.text 起始地址 +init_size : u32 // .init 段总大小 +init_text_size : u32 // .init.text 代码段大小 +core_size : u32 // .core 段大小 +state : enum // 模块状态 (MODULE_STATE_LIVE等) } note "module_init内存释放:do_init_module() → free_module_init(mod) → module_memfree(module_init)" ``` --- ### 四、资源清理阶段:自动释放 `.init` 段 `__init` 函数的生命周期约束: | 属性 | 内存区域 | 生命周期 | |------|----------|----------| | `__init` | `.init.text` | 执行后自动释放 | | 未标记 | `.core.text` | 常驻内存直至模块卸载 | **释放原理**: ```c // kernel/module.c static void free_init_mem(struct module *mod) { vfree(mod->module_init); // 释放虚拟地址空间 [^3][^4] mod->module_init = NULL; } ``` 验证工具: ```bash # 查看模块内存区域 cat /sys/module/<模块名>/sections/.init.text ``` --- ### 典型问题分析:为何某些模块不释放内存? 若驱动未正确处理 `__init`: ```c static int init_func(void) { // 错误:未使用 __init platform_driver_register(...); } module_init(init_func); ``` 此时函数驻留在 `.core.text` 段,导致: 1. 内存泄漏(持续占用内核空间) 2. 模块卸载后无法重新加载(需重启内核) **解决方案**: ```c static int __init init_func(void) { // 正确添加 __init platform_driver_register(...); } module_init(init_func); ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值