Linux kernel __init和core_initcall分析

本文详细分析了Linux内核4.14版本中__init宏和core_initcall的工作原理。__init宏将代码置于.init.text段,初始化后可删除。core_initcall用于驱动加载,其调用顺序可通过不同_initcall函数调整。init/main.c中的initcall_blacklist()处理禁载驱动,而do_one_initcall()则用于按顺序执行初始化函数。

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

Linux kernel __initcore_initcall分析

下面内容都是以kernel 4.14版本分析;通常linux驱动定义为如下:

static int __init gpiolib_dev_init(void)
{
                                                                                                                                                                                                               
    ...
}
core_initcall(gpiolib_dev_init);

1. __init分析
__init是个宏,定义为 define __init __section(.init.text) __cold __inittrace __latent_entropy __noinitretpoline __nocfi
通常编译器把把代码放在.text段,全局初始化的变量放在.data段,全局初始化的变量放在.bss段。而使用session属性,连接器(vmlinux.lds)可以把需要的内容,放在指定的段中。
上面的__init被放在.init.text中,因此所有的_init都放入到’.init.text’中,初始化完后就可以把该内存删除掉。

2. core_initcall分析
路径为include/linux/init.h

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

#define core_initcall(fn)       __define_initcall(fn, 1)
#define core_initcall_sync(fn)      __define_initcall(fn, 1s)
#define postcore_initcall(fn)       __define_initcall(fn, 2)                                                                                                                                                 
#define postcore_initcall_sync(fn)  __define_initcall(fn, 2s)
#define arch_initcall(fn)       __define_initcall(fn, 3)
#define arch_initcall_sync(fn)      __define_initcall(fn, 3s)
#define subsys_initcall(fn)     __define_initcall(fn, 4)
#define subsys_initcall_sync(fn)    __define_initcall(fn, 4s)
#define fs_initcall(fn)         __define_initcall(fn, 5)
#define fs_initcall_sync(fn)        __define_initcall(fn, 5s)
#define rootfs_initcall(fn)     __define_initcall(fn, rootfs)
#define device_initcall(fn)     __define_initcall(fn, 6)
#define device_initcall_sync(fn)    __define_initcall(fn, 6s)
#define late_initcall(fn)       __define_initcall(fn, 7)
#define late_initcall_sync(fn)      __define_initcall(fn, 7s)

#define __initcall_name(fn, id)   __initcall_##fn##id

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

typedef int (*initcall_t)(void)声明一个函数指针

core_initcall(gpiolib_dev_init) =
__define_initcall(gpiolib_dev_init, 1) =
static initcall_t __initcall_gpiolib_dev_init_1 __used   __attribute__((__section__(.initcall.1.init))) = gpiolib_dev_init;

上面宏展开的意思为声明了一个函数__initcall_gpiolib_dev_init_1,该函数指向了gpiolib_dev_init。同时__initcall_gpiolib_dev_init_1是储存在__section__(.initcall.1.init) ,储存方式通过lds连接如下(查看out目录下的vmlinux.lds)

initcall_start = .; 
KEEP(*(.initcallearly.init)) 
__initcall0_start = .; 
KEEP(*(.initcall0.init)) KEEP(*(.initcall0s
<think>好的,用户的问题是关于Linux内核中的module_init函数initcall机制之间的关系,以及它们在内核初始化过程中的作用。我需要先回忆一下相关的知识,并整理出一个清晰的解释。 首先,用户提到了module_initinitcall,可能对内核模块的初始化过程不太清楚。根据引用中的内容,module_init对应init6段,而initcall机制是通过do_initcalls函数来调用各个初始化函数的。需要解释module_init如何将驱动初始化函数注册到initcall的特定段中,以及内核启动时如何按顺序执行这些函数。 接下来,我需要考虑用户可能的知识背景。他们可能已经了解Linux内核启动的大致流程,比如start_kernel函数的作用,但可能不清楚具体的初始化调用机制。需要从start_kernel开始,逐步说明kernel_init如何调用到do_initcalls,并在此过程中执行各个initcall段中的函数。 另外,用户可能混淆了模块加载的两种情况:编译进内核作为可加载模块。需要区分这两种情况,说明module_init在不同情况下的行为差异。例如,当模块编译进内核时,初始化函数会被放到特定的initcall段;而作为可加载模块时,函数则存放在模块的初始化段,由insmod或modprobe触发。 还需要引用提供的参考资料,特别是引用[2]提到的kernel_thread创建的内核线程执行kernel_init,最终调用do_initcalls。而引用[3]则强调了start_kernel的整体作用,但需要聚焦到module_init的执行过程。 可能存在的疑问点包括:initcall的等级划分、各个等级的执行顺序、module_init对应的等级是哪一个,以及这些等级在内核启动流程中的位置。需要明确说明initcall分为多个等级,从core到late,而module_init默认属于第六级(initcall6),即设备驱动初始化阶段。 此外,要确保使用正确的中文术语,比如“初始化函数”、“内核线程”、“可加载模块”等,避免术语混淆。同时,按照用户的格式要求,行内数学表达式用$...$,独立公式用$$...$$,但在这个问题中可能涉及较少数学内容,但如果有需要展示代码结构或函数调用层次,可能需要使用代码块。 最后,生成的相关问题需要围绕用户的问题扩展,例如询问initcall等级划分、编译进内核可加载模块的区别、如何调整初始化顺序等,帮助用户进一步理解相关知识。</think>### module_init函数与initcall机制的关系 在Linux内核初始化过程中,`module_init`函数**initcall机制**共同实现了驱动模块的初始化调用。具体关系如下: --- #### 1. **module_init的作用** `module_init`是一个宏,用于将驱动模块的初始化函数注册到内核中。根据模块是否**编译进内核**(built-in)或作为**可加载模块**(loadable module),其行为不同: - **编译进内核**时:初始化函数被放置在特定的`initcall`段(如`.initcall6.init`)中,由内核启动时统一调用。 - **作为可加载模块**时:初始化函数被存放在模块的`.init`段,在用户通过`insmod`或`modprobe`加载模块时触发[^1][^2]。 --- #### 2. **initcall机制的执行流程** initcall机制通过划分**初始化等级**,确保内核启动时各模块按依赖顺序初始化。核心流程如下: 1. **内核启动入口**:`start_kernel`函数完成基础初始化后,调用`rest_init`创建内核线程`kernel_init`[^3]。 2. **调用do_initcalls**:在`kernel_init`中,最终通过`do_initcalls`函数遍历所有initcall段(从`core_initcall`到`late_initcall`),依次执行注册的函数。 3. **模块初始化执行**:对于编译进内核的模块,其初始化函数通过`module_init`被分配到`initcall6`段(设备驱动初始化阶段),由`do_initcalls`统一调用[^1]。 --- #### 3. **关键代码结构** ```c // module_init宏定义(简化版) #define module_init(initfn) \ __define_initcall(initfn, 6) // 将函数指针存放到.initcall6.init段 // do_initcalls函数(简化逻辑) static void __init do_initcalls(void) { for (level = 0; level < ARRAY_SIZE(initcall_levels); level++) { call_initcalls(initcall_levels[level]); // 按等级顺序调用 } } ``` --- #### 4. **两种场景对比** | 场景 | 初始化函数存放位置 | 触发时机 | |---------------------|--------------------------|------------------------| | 编译进内核 | `.initcall6.init`段 | 内核启动时`do_initcalls`调用 | | 可加载模块 | 模块的`.init`段 | 用户手动加载模块时触发 | --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值