内核中module_init()是被如何调用

本文深入解析了Linux内核模块初始化过程中的关键概念和技术细节,包括module_init宏的作用原理及其内部实现机制,阐述了如何通过这一机制指定模块的初始化函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

static int __init initialization_function(void){/*初始化代码*/}

module_init(initialization_function);

初始化函数应当声明成静态的,因为它们不会在特定文件之外可见。上述定义中的__init标志可能看起来有点陌生,它对内核来讲是一个暗示,表明该函数只是在初始化时使用。模块加载器在模块加载后会丢掉这个初始化函数,这样可将该函数占用的内存释放出来,以作他用。__init和__initdata的使用是可选的,虽然有点繁琐,但是很值得使用。注意,不要在初始化完成后还使用的函数(或者数据结构)上使用这两个标记。moudle_init的使用是强制性的,这个宏定义会在模块目标代码中增加一个特殊的段,用于说明内核模块初始化函数所在的位置。没有这个定义,你的初始化函数不会被调用。

转自嵌入式学习网!
原文地址:http://www.embedstudy.com/viewnews-8456

 

http://www.linuxidc.com/Linux/2011-03/32833.htm

在Linux底下写过driver模块的对这个宏一定不会陌生。module_init宏在MODULE宏有没有定义的情况下展开的内容是不同的,如果这个宏没有定义,基本上表明阁下的模块是要编译进内核的(obj-y)。
1.在MODULE没有定义这种情况下,module_init定义如下:

#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(x)最终展开为:
static initcall_t __initcall_##fn##id __used \
        __attribute__((__section__(".initcall" level ".init"))) = fn

更直白点,假设阁下driver所对应的模块的初始化函数为int gpio_init(void),那么module_init(gpio_init)实际上等于:
static initcall_t  __initcall_gpio_init_6 __used __attribute__((__section__(".initcall6.init"))) =gpio_init;
就是声明一类型为initcall_t(typedef int (*initcall_t)(void))函数指针类型的变量__initcall_gpio_init_6并将gpio_init赋值与它。
这里的函数指针变量声明比较特殊的地方在于,将这个变量放在了一名为".initcall6.init"节中。接下来结合vmlinux.lds中的
.initcall.init : AT(ADDR(.initcall.init) - (0xc0000000 -0x00000000)) {
  __initcall_start = .;
  *(.initcallearly.init) __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)
  __initcall_end = .;
  }
以及do_initcalls:
static void __init do_initcalls(void)
{
        initcall_t *call;

        for (call = __initcall_start; call < __initcall_end; call++)
                do_one_initcall(*call);

        /* Make sure there is no pending stuff from the initcall sequence */
        flush_scheduled_work();
}
那么就不难理解阁下模块中的module_init中的初始化函数何时被调用了:在系统启动过程中start_kernel()->rest_init()->kernel_init()->do_basic_setup()->do_initcalls()。

1.
在看linux核心代码的时候看到/init/main.c 里面的do_initcalls函数
static void __init do_initcalls(void)
{
  initcall_t *call;

  call = &__initcall_start;
  do {
   (*call)();
   call++;
  } while (call < &__initcall_end);

  /* Make sure there is no pending stuff from the initcall sequence */
  flush_scheduled_tasks();
}
当时就很诧异,在用source insight 查看的时候,没有发现__initcall_start的定义,
然后就很网站搜索,(估计是我搜索水平太菜),没有发现有相关的文章,于是在相关的linux
论坛提问,有人指引我看看/arch/i386/vmlinux.lds
lds是什么?晕菜,不清楚,查了资料得知是ld script.看来的研究ld了

2.
察看/arch/i386/vmlinux.lds,发现一段代码
 __initcall_start = .;
 .initcall.init : { *(.initcall.init) }
 __initcall_end = .;
 
跟我找的东西相关
使用info ld,察看相关资料,(最终发现太麻烦,到网上找了一个ld.pdf).发现有这么一
段介绍关于c++地联结构造器的使用,和这段用法相同
其含义时,是让__initcall_start指向代码节.initcall.init的节首,而__initcall_end
指向.initcall.init的节尾。
那么第一段代码从程序逻辑上得到了解释。

3。
因为do_initcalls所作的是系统中有关于选择的驱动部分的初始化工作,那么具体是这些
函数指针数据怎样放到了.initcall.init节。
想起来了,还没有使用grep哈哈,在
grep -rn .initcall.init *
发现在include/linux/init.h:83:有这样一个定义
#define __init_call   __attribute__ ((unused,__section__ (".initcall.init")))
娃哈哈哈
终于让我发现了
然后又发现了
#define __initcall(fn) \
static initcall_t __initcall_##fn __init_call = fn

4。
问题是什么是__attribute__??,查找man gcc,找到关于__attribute__的定义
`section ("section-name")'
   Normally, the compiler places the code it generates in the `text'
   section. Sometimes, however, you need additional sections, or you
   need certain particular functions to appear in special sections.
   The `section' attribute specifies that a function lives in a
   particular section. For example, the declaration:

     extern void foobar (void) __attribute__ ((section ("bar")));

   puts the function `foobar' in the `bar' section.

   Some file formats do not support arbitrary sections so the
   `section' attribute is not available on all platforms. If you
   need to map the entire contents of a module to a particular
   section, consider using the facilities of the linker instead.
   
他的意思就是使它建造一个在.initcall.init节的指向初始函数的指针

5。
问题是##是什么意思?
查阅gcc的man page得知,她是用在可变参数使用宏定义的时候的
在这里也就是建立一个变量名称为所指向的函数的名称,并且前面加上
__initcall_.

6.
然后看看成果
在/include/linux/init.c中有发现
#define module_init(x) __initcall(x);
看看很多驱动中都有类似
module_init(usb_init);
module_exit(usb_exit);
的代码

<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); ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值