module_init和module_exit

本文深入探讨了Linux内核中初始化函数的工作原理。介绍了如何通过使用特定宏定义将初始化函数加入到初始化列表中,并且详细解释了连接器脚本中如何定义初始化列表的起始和结束地址,最终使得内核能在启动过程中按需调用这些函数。

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

/*http://blog.youkuaiyun.com/citytramper/archive/2006/02/16/600708.aspx*/

一直以来写linux驱动,都是按照固定格式,定义一个初始化和推出函数,书上告诉我这两个函数会被调用,至于为什么会被调用,在哪调用,一直不清楚。

偶然的一个机会,看到blob里面的代码,里面有一个初始化函数列表。按照一般的编程想法,各部分的初始化函数会在一个固定的函数里调用比如:

void init(void)

{

    init_a();

    init_b();

}

 如果再加入一个初始化函数呢,那么再init_b()后面再加一行:

init_c();

这样确实能完成我们的功能,但这样有一定的问题,就是不能独立的添加初始化函数,每次添加一个新的函数都要修改init函数,blob中的初始化函数就是完全独立的,只要用一个宏来修饰一下:

void init_a(void)

{

}

__initlist(init_a, 1);

它是通过这个宏来实现初始化函数列表的呢?

先来看__initlist的定义:

#define __init __attribute__((unused, __section__(".initlist")))

#define __initlist(fn, lvl) /
static initlist_t __init_##fn __init = { /
 magic:    INIT_MAGIC, /
 callback: fn, /
 level:   lvl }

看来就是定义了一个结构体,存了初始化函数的指针,没什么特别的。请注意:__section__(".initlist")

这个属性起什么作用呢?它告诉连接器这个变量存放在.initlist区段,如果所有的初始化函数都是用这个宏,那么每个函数会有对应的一个initlist_t结构体变量存放在.initlist区段,也就是说我们可以在.initlist区段找到所有初始化函数的指针。怎么找到.initlist区段的地址呢?

extern u32 __initlist_start;
extern u32 __initlist_end;

这两个变量起作用了,__initlist_start是.initlist区段的开始,__initlist_end是结束,通过这两个变量我们就可以访问到所有的初始化函数了。

 这两个变量在那定义的呢?

在一个连接器脚本文件里

 . = ALIGN(4);
 .initlist : {
  __initlist_start = .;
  *(.initlist)
  __initlist_end = .;
 }
这两个变量的值正好定义在.initlist区段的开始和结束地址,所以我们能通过这两个变量访问到所有的初始化函数。

 

 

<think>好的,用户想了解Linux内核编程module_init__init的关系及用法。首先,我需要回忆这两个宏的具体作用它们在内核模块加载过程中的角色。 首先,module_init是一个宏,用于指定模块的初始化函数。当模块被加载时,内核会调用这个函数。而__init是一个属性宏,用于标记函数或变量仅在初始化阶段使用,之后内存会被释放。这可能涉及到内核的内存管理机制,比如将这部分代码或数据放在特定的段中,初始化完成后回收资源。 接下来,我需要确认两者的使用场景。module_init用于定义模块的入口点,无论是编译进内核还是作为可加载模块,都需要用它来声明初始化函数。而__init则用于优化内存使用,标记那些只在初始化阶段需要的函数变量,避免长期占用内存。 用户可能还会混淆这两个宏的具体用法,比如是否必须同时使用。例如,模块的初始化函数是否需要用__init修饰。根据经验,是的,通常module_init声明的函数会同时使用__init,这样在模块内置入内核时,初始化完成后可以释放内存。但如果是可加载模块,可能不需要,因为模块卸载时内存会被释放。不过需要进一步确认内核的处理机制。 另外,用户可能想知道如果不使用__init会有什么后果。可能会导致内存浪费,因为初始化代码在模块加载后不再需要,但未被释放。而module_init如果不使用,模块将无法正确注册初始化函数,导致加载失败。 需要查阅相关文档或代码示例来验证这些信息。例如,Linux内核源码中的模块通常会看到如下结构: static int __init my_module_init(void) { // 初始化代码 } module_init(my_module_init); 这里,__init修饰了初始化函数,而module_init将其注册为入口点。当模块编译进内核时,__init标记的部分会在初始化后被释放;如果是可加载模块,则在卸载时释放。 还要注意,__initdata用于初始化阶段的数据,而__exit__exitdata用于退出时的清理函数数据,类似机制。 可能用户的问题还涉及到模块的生命周期管理,以及内核如何优化资源使用。需要解释清楚这两个宏如何协作,确保模块正确初始化资源释放。 最后,生成相关问题时,需要考虑用户可能进一步想了解的内容,比如其他类似的宏(如__exit),或者模块加载卸载的具体过程,以及如何调试模块初始化的问题等。</think>在Linux内核编程中,`module_init``__init`是密切相关的宏,用于管理内核模块的初始化资源优化。以下是它们的详细关系用法: --- ### 1. `module_init` 的作用 `module_init`宏用于**指定模块的初始化函数**,该函数在模块加载时由内核自动调用。它的主要特点包括: - 当模块编译为可动态加载(`.ko`文件)时,初始化函数在`insmod`或`modprobe`时执行 - 当模块直接编译进内核时,初始化函数在内核启动时执行 - 初始化函数需要返回`int`类型值,成功返回`0`,失败返回负数错误码 示例: ```c static int __init my_module_init(void) { printk(KERN_INFO "Module initialized\n"); return 0; } module_init(my_module_init); // 注册初始化函数 ``` --- ### 2. `__init` 的作用 `__init`宏是**内存优化标记**,用于标识只在初始化阶段使用的函数或数据: - 被`__init`修饰的函数会被放入`.init.text`段 - 被`__init`修饰的变量会被放入`.init.data`段 - 内核在完成初始化后,会释放这些内存区域以节省空间[^1] 示例: ```c static int __initdata init_count = 0; // __initdata用于初始化阶段变量 static int __init my_init(void) { init_count++; return 0; } ``` --- ### 3. 两者的协作关系 | 特性 | `module_init` | `__init` | |---------------------|----------------------------|--------------------------| | 主要目的 | 注册模块入口点 | 标记初始化专用资源 | | 作用阶段 | 模块加载/内核启动时 | 编译阶段划分内存区域 | | 内存管理 | 不直接管理内存 | 优化内核内存使用 | | 典型组合 | 常与`__init`联合使用 | 常修饰`module_init`函数 | **典型代码模式**: ```c static int __init my_module_init(void) { // 用__init修饰初始化函数 /* 初始化逻辑 */ } module_init(my_module_init); // 注册为模块入口 ``` --- ### 4. 关键区别 - **编译处理差异**: - 若模块编译进内核(built-in),`__init`标记的函数/数据会在初始化后**被内存管理器自动释放** - 若模块为动态加载(loadable),`__init`标记的内容会在模块卸载时释放 - **必要性差异**: - `module_init`是**强制要求**的模块入口声明 - `__init`是**可选优化**,但强烈推荐使用 --- ### 5. 扩展应用 - `__exit``module_exit`:与初始化对应的清理机制 ```c static void __exit my_module_exit(void) { printk(KERN_INFO "Module removed\n"); } module_exit(my_module_exit); ``` - `__initdata`:专门用于初始化阶段的数据标记 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值