linux模块(module_init)、子系统(subsys_initcall)入口函数

本文深入探讨了Linux内核的初始化过程,详细解析了initcalls机制如何按优先级顺序执行初始化函数,从早期初始化到设备初始化的全过程。通过分析initcall宏定义、section分配以及vmlinux.lds文件中的section内容,揭示了Linux内核如何组织和执行不同级别的初始化代码。

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

一、Include/linux/init.h中:

/* initcalls are now grouped by functionality into separate 
 * subsections. Ordering inside the subsections is determined
 * by link order. 
 * For backwards compatibility, initcall() puts the call in 
 * the device init subsection.
 *
 * The `id' arg to __define_initcall() is needed so that multiple initcalls
 * can point at the same handler without causing duplicate-symbol build errors.
 */

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

/*
 * Early initcalls run before initializing SMP.
 *
 * Only for built-in code, not modules.
 */
#define early_initcall(fn)		__define_initcall(fn, early)

/*
 * A "pure" initcall has no dependencies on anything else, and purely
 * initializes variables that couldn't be statically initialized.
 *
 * This only exists for built-in code, not for modules.
 * Keep main.c:initcall_level_names[] in sync.
 */
#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的封装。

__define_initcall中的第二个参数是调用顺序,数字越小在linux启动过程中调用的越早。

可以看到我们熟悉的模块入口函数device_initcall的优先级是6,subsys_initcall的优先级是4.

这个宏定义是将我们定义的模块初始化函数按照初始化优先级放到相应的.initcallX.init段中,如subsys_initcall定义的函数放到.initcall4.init段中。


详细解释一下这个宏的定义:

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

1,__define_initcall(fn, id)
这个宏有两个参数fn、id


2,static initcall_t _initcall##fn##id __used 
定义了一个initcall_t类型的函数指针。
initcall_t的定义为typedef int (*initcall_t)(void);,是一个参数为空,返回值为int的函数指针类型。

传递给module_init 的函数就是这个类型的static int hello_init();
这个新定义的函数指针名称是有fn和id两个参数组成的_initcall##fn##id;
__used告诉编译器无论 GCC 是否发现这个函数的调用实例,都要使用这个函数。


3,attribute((section(“.initcall” #id “.init”))) = fn;

attribute((section(“”)))是为链接器指定链接的section,这里按照id将各个初始化函数放置到不同的.initcallX.init段中。

然后将初始化函数指针赋值给了新定义的函数指针。

 

 

二、arch\arm\kernel\vmlinux.lds发现lds中定义的各个section的具体内容如下:

.init.data : {
  *(.init.data) *(.cpuinit.data) *(.meminit.data) *(.init.rodata) *(.cpuinit.rodata) *(.meminit.rodata) . = ALIGN(8);
 __clk_of_table = .; KEEP(*(__clk_of_table)) KEEP(*(__clk_of_table_end)) . = ALIGN(8); 
__clksrc_of_table = .; KEEP(*(__clksrc_of_table)) KEEP(*(__clksrc_of_table_end)) . = ALIGN(32); 
__dtb_start = .; KEEP(*(.dtb.init.rodata)) __dtb_end = .; . = ALIGN(8); 
__irqchip_begin = .; KEEP(*(__irqchip_of_table)) KEEP(*(__irqchip_of_end))  . = ALIGN(16); 
__setup_start = .; KEEP(*(.init.setup)) __setup_end = .;
__initcall_start = .; KEEP(*(.initcallearly.init)) 
__initcall0_start = .; KEEP(*(.initcall0.init)) KEEP(*(.initcall0s.init)) 
__initcall1_start = .; KEEP(*(.initcall1.init)) KEEP(*(.initcall1s.init)) 
__initcall2_start = .; KEEP(*(.initcall2.init)) KEEP(*(.initcall2s.init)) 
__initcall3_start = .; KEEP(*(.initcall3.init)) KEEP(*(.initcall3s.init)) 
__initcall4_start = .; KEEP(*(.initcall4.init)) KEEP(*(.initcall4s.init)) 
__initcall5_start = .; KEEP(*(.initcall5.init)) KEEP(*(.initcall5s.init)) 
__initcallrootfs_start = .; KEEP(*(.initcallrootfs.init)) KEEP(*(.initcallrootfss.init)) 
__initcall6_start = .; KEEP(*(.initcall6.init)) KEEP(*(.initcall6s.init)) 
__initcall7_start = .; KEEP(*(.initcall7.init)) KEEP(*(.initcall7s.init)) 
__initcall_end = .;
  __con_initcall_start = .; KEEP(*(.con_initcall.init)) __con_initcall_end = .;
  __security_initcall_start = .; KEEP(*(.security_initcall.init)) _security_initcall_end = .;
 }

三、Init/main.c中:

asmlinkage void __init start_kernel(void)
{
    static noinline void __init_refok rest_init(void)
    {
        kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
        static int __ref kernel_init(void *unused)
        {
            static noinline void __init kernel_init_freeable(void)
            {
                static void __init do_basic_setup(void)
                {
                    extern initcall_t __initcall_start[];
                    extern initcall_t __initcall0_start[];
                    extern initcall_t __initcall1_start[];
                    extern initcall_t __initcall2_start[];
                    extern initcall_t __initcall3_start[];
                    extern initcall_t __initcall4_start[];
                    extern initcall_t __initcall5_start[];
                    extern initcall_t __initcall6_start[];
                    extern initcall_t __initcall7_start[];
                    extern initcall_t __initcall_end[];

                    static initcall_t *initcall_levels[] __initdata = {
	                    __initcall0_start,
	                    __initcall1_start,
	                    __initcall2_start,
	                    __initcall3_start,
	                    __initcall4_start,
	                    __initcall5_start,
	                    __initcall6_start,
	                    __initcall7_start,
	                    __initcall_end,
                    };
                    static void __init do_initcalls(void)
                    {
	                    int level;

	                    for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
		                do_initcall_level(level);
                        static void __init do_initcall_level(int level)
                        {
	                        extern const struct kernel_param __start___param[], __stop___param[];
                            initcall_t *fn;

	                        strcpy(static_command_line, saved_command_line);
	                        parse_args(initcall_level_names[level],
		                    static_command_line, __start___param,
		                    __stop___param - __start___param,
		                    level, level,
		                    &repair_env_string);

	                        for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
		                        do_one_initcall(*fn);
                                int __init_or_module do_one_initcall(initcall_t fn)
                                {
		                            int count = preempt_count();
		                            int ret;

		                            if (initcall_debug)
			                            ret = do_one_initcall_debug(fn);
                                        {
                                            ......
                                            ret = fn();
                                            ......
                                        }
		                            else
			                            ret = fn();

		                            msgbuf[0] = 0;

		                            if (preempt_count() != count) {
			                            sprintf(msgbuf, "preemption imbalance ");
			                            preempt_count() = count;
		                            }
		                            if (irqs_disabled()) {
			                            strlcat(msgbuf, "disabled interrupts ",	sizeof(msgbuf));
			                            local_irq_enable();
		                            }
		                            WARN(msgbuf[0], "initcall %pF returned with %s\n", fn, msgbuf);

		                            return ret;
                                }
                            }
                        }
                    }
                }
            }
        }
}

参考:https://blog.youkuaiyun.com/asklw/article/details/79698422

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值