关于__init、__initdata和__exit、__exitdata的学习笔记

对于__init、__initdata和__exit、__exitdata的定义位于<linux/init.h>,这些宏定义的作用是告诉编译器将这些函数或者数据放入相应的section中,而在模块加载的阶段,.ko文件中的代码和数据的加载区域是根据section来加载的。

比如:如果函数的定义中带有__init,那么这个函数的所有代码会被放入.init.text的section中。
      如果函数的定义中带有__initdata,那么这个函数的所有代码会被放入.init.data的section中。

    之所以要使用这个宏定义,其中一个原因是标记为初始化的函数和数据,表明该函数和数据仅在初始化期间使用。在模块装载之后,模块装载就会将初始化函数扔掉。这样可以将该函数占用的内存释放出来。

这种释放根据是否编译进内核是有区别的:
(1)模块编译进内核:所有的初始化数据和函数都是在系统启动的最后阶段,在所有模块都初始化完成以后被内核统一释放的。所有你一般可以在内核启动信息的后面看到:

  1. PHY: 0:01 - Link is Up - 100/Full
  2. VFS: Mounted root (nfs filesystem) on device 0:14.
  3. devtmpfs: mounted
  4. Freeing init memory: 196K
  5. INIT: version 2.86 booting
(2)独立的模块:模块是通过module-init-tool中的insmod的程序利用系统调用来挂载的,而所有的初始化数据和函数都是被这个系统调用所使用的,在模块挂载完成并初始化过后,由系统调用来完成对初始化数据和函数所占空间的释放。

所以对于将内核驱动代码中的函数和数据定义为“初始化”时需要注意:不要将驱动定义的文件方法(如 open、read、write、close)或者驱动在实际工作中需要使用的函数和数据定义为“初始化”属性,因为在驱动初始化后这些东东就已经被释放了,如果使用了就会Oops。

有些网上的文章中写到:
  1. __init宏使内建模块中的init函数在执行完成后释放掉,不过可装载的模块不受影响。
这个是错误的,这些“初始化”宏同样影响可装载模块。从模块装载的系统调用源码中你可以找到释放的地方,他释放的是整个“初始化”section。具体的情况请参考《深入Linux内核构架》的《第七章 模块》

从代码上可以证明“初始化”宏同样影响可装载模块,从实验中同样可以,实验步骤:
(1)在一个简单的字符驱动中定义一个“初始化”字符串,并在模块初始化时打印出来。在驱动的其他方法中也试图打印这个字符串,如果这个方法被系统调用执行了,那么你就得到了一个Oops。
(2)去掉这个字符串定义时的“初始化”宏,再做一边实验,字符串依然可以被打印出来。
<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、付费专栏及课程。

余额充值