LINUX内核中的xx_initcall初始化标号

本文详细解析了Linux内核中的初始化标号机制,包括不同级别的初始化宏定义及其作用,如core_initcall和device_initcall等,并解释了这些标号如何在内核启动过程中按顺序执行。

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

LINUX内核中的xx_initcall初始化标号

田海立@优快云 2011-07-02

LINUX内核中有很多的初始化指示标志postcore_initcall(), arch_initcall(), subsys_initcall(), device_initcall(), etc. 这些起什么作用呢?查阅源代码(android goldfish-2.6.29)并搜索网上相关文章,对此做一总结。

  1. 初始化标号

先看这些宏的定义(定义在文件include/linux/init.h中)

#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)

  1. __define_initcall

这些宏都用到了__define_initcall(),再看看它的定义(同样定义在文件include/linux/init.h中)

#define __define_initcall(level,fn,id) \ static initcall_t __initcall_##fn##id __used \ __attribute__((__section__(".initcall" level ".init"))) = fn

这其中initcall_t是函数指针,原型如下,

typedef int (*initcall_t)(void);

而属性 __attribute__((__section__())) 则表示把对象放在一个这个由括号中的名称所指代的section中。

所以__define_initcall的含义是:

1) 声明一个名称为__initcall_##fn的函数指针;

2) 将这个函数指针初始化为fn;

3) 编译的时候需要把这个函数指针变量放置到名称为 ".initcall" level ".init"的section中。


3. 放置.initcall.init SECTION

明确了__define_initcall的含义,就知道了是分别将这些初始化标号修饰的函数指针放到各自的section中的。

SECTION“.initcall”level”.init”被放入INITCALLS(include/asm-generic/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)

__initcall_start和__initcall_end以及INITCALLS中定义的SECTION都是在arch/xxx/kernel/vmlinux.lds.S中放在.init段的。

SECTIONS { .init : { __initcall_start = .; INITCALLS __initcall_end = .; } }

4. 初始化.initcallxx.init里的函数

而这些SECTION里的函数在初始化时被顺序执行(init内核线程->do_basic_setup()[main.c#778]->do_initcalls())。

程序(init/main.c文件do_initcalls()函数)如下,do_initcalls()把.initcallXX.init中的函数按顺序都执行一遍。

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

*************************** 本文完 *****************************

<think>嗯,用户问的是Linux中的subsys_initcall函数能不能用于初始化模块。我得先回忆一下Linux内核模块初始化的相关知识。记得内核有不同的初始化级别,比如arch_initcall、subsys_initcall、device_initcall等等,这些宏用来指定模块初始化函数的执行顺序。 首先,subsys_initcall属于比较早期的初始化阶段,通常用于子系统级别的初始化。那模块初始化的话,一般用的是module_init宏,但module_init其实会根据内核配置决定属于哪个级别。比如在编译进内核时,module_init可能对应device_initcall,而如果是可加载模块,可能对应更晚的阶段。 用户想知道subsys_initcall是否可以用于模块。这里需要区分两种情况:模块是编译进内核还是作为可加载模块(.ko)。如果是编译进内核的话,subsys_initcall确实可以用来指定初始化函数,这样在内核启动时就会按顺序执行。但如果是动态加载的模块,可能有问题,因为subsys_initcall的优先级较高,可能在模块加载时已经过了那个阶段,导致函数不被执行。 另外,用户可能混淆了内核中的不同初始化机制。模块通常用module_init,而subsys_initcall更多用于内核子系统的初始化,这些可能在内核启动阶段就完成了。如果用户尝试在可加载模块中使用subsys_initcall,可能导致初始化函数不被调用,从而模块无法正确加载。 还需要考虑模块加载的机制。动态加载的模块在insmod的时候,其初始化函数是由module_init注册的,而subsys_initcall内核启动的早期阶段就已经执行完毕了。这时候即使用subsys_initcall声明,函数也不会被再次调用,导致模块初始化失败。 所以,总结的话,subsys_initcall主要适用于内核编译时内置的子系统的初始化,而动态加载的模块应该使用module_init。如果用户错误地使用subsys_initcall,可能导致模块初始化函数不执行,因此需要根据具体情况建议正确的宏。 另外,用户可能实际需求是想让模块的初始化函数早点执行,或者在内核启动阶段就初始化,这时候可能需要将模块编译进内核,并选择合适的初始化级别。需要向用户解释清楚不同场景下的正确用法,避免误用导致的问题。</think>Linux内核中的`subsys_initcall`确实可用于模块初始化,但其具体行为需结合模块类型(内置模块/可加载模块)和内核启动阶段来分析。以下是逐步解析: --- ### 1. **`subsys_initcall`的本质** - **作用**: 属于Linux内核定义的**初始化级别宏**,用于指定函数在内核启动阶段的执行顺序。 - **优先级**: 位于`arch_initcall`之后、`fs_initcall`之前(具体顺序见`include/linux/init.h`)。 - **典型场景**: 内核子系统的初始化(如内存管理、进程调度等)。 --- ### 2. **模块类型与初始化方式** #### (1) **内置模块(Built-in)** - **编译方式**: 直接编译进内核镜像(vmlinux)。 - **适用性**: ✅ 可使用`subsys_initcall`。 ```c static int __init my_init(void) { /* 初始化代码 */ } subsys_initcall(my_init); // 在内核启动时自动执行 ``` #### (2) **可加载模块(Loadable Module, .ko)** - **编译方式**: 独立编译为`.ko`文件,通过`insmod`加载。 - **问题**: ❌ 不可直接使用`subsys_initcall`。 - **原因**: `subsys_initcall`对应的初始化阶段在内核启动时已完成,动态加载模块时不会再次触发该阶段的函数。 --- ### 3. **可加载模块的正确初始化方法** - **标准做法**: 使用`module_init()`宏。 ```c static int __init my_init(void) { /* 初始化代码 */ } module_init(my_init); // 通过insmod或modprobe触发 ``` - **内核行为**: - 编译为可加载模块时,`module_init`会将函数注册到`.ko`的初始化段。 - 模块加载时,内核调用`do_init_module()`执行该函数。 --- ### 4. **潜在问题与验证方法** #### 错误用法示例 ```c // 错误:在可加载模块中使用subsys_initcall subsys_initcall(my_init); // 加载时不会执行! ``` - **后果**: 模块加载后,初始化函数未被调用,导致模块失效。 #### 验证方式 - **查看Syslog**: ```bash dmesg | grep my_init ``` - 若未输出初始化函数的日志,则说明函数未执行。 --- ### 5. **特殊情况:强制匹配初始化级别** - **理论可能**: 修改模块代码,将`subsys_initcall`与`module_init`指向同一初始化级别。 - **风险**: ⚠️ 破坏内核启动流程,可能导致系统不稳定。 --- ### 总结 | 模块类型 | 适用宏 | 执行时机 | |----------------|-------------------|------------------------| | 内置模块 | `subsys_initcall` | 内核启动时自动执行 | | 可加载模块 | `module_init` | `insmod`/`modprobe`时 | **建议**: 始终为可加载模块使用`module_init()`,除非明确需要修改内核子系统初始化顺序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值