Linux内核学习笔记:initcall

2023-06-01 15:25:39 来源:嵌入式Linux充电站

写过Linux驱动的人都知道module_init宏,因为它声明了一个驱动的入口函数。

除了module_init宏,你会发现在Linux内核中有许多的驱动并没有使用module_init宏来声明入口函数,而是看到了许多诸如以下的声明:

static int __init qcom_iommu_init(void){ int ret; ret = platform_driver_register(&qcom_iommu_ctx_driver); if (ret)  return ret; ret = platform_driver_register(&qcom_iommu_driver); if (ret)  platform_driver_unregister(&qcom_iommu_ctx_driver); return ret;}device_initcall(qcom_iommu_init);
static int __init ebsa110_init(void){ arm_pm_idle = ebsa110_idle; return platform_add_devices(ebsa110_devices, ARRAY_SIZE(ebsa110_devices));}arch_initcall(ebsa110_init);

上述举例的两个驱动入口分别使用了device_initcall()arch_initcall()来声明驱动入口,这些本质上都是对initcall的调用,module_init也如此。


(相关资料图)

initcall等级

Linux内核对initcall进行了等级划分,每一种类型的initcall都有对应等级,等级0-7。

路径:include/init/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 * canpoint 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; \\ LTO_REFERENCE_INITCALL(__initcall_##fn##id)

id越小等级越高,Linux会按照等级由高到低顺序执行:

/* * 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(fn) device_initcall(fn)

这么做的目的主要是根据优先级依次对设备进行初始化,例如会先初始化与架构相关的,然后再初始化内核子系统。

Linux对initcall的调用

在Linux启动时,会依次遍历所有等级的initcall,以完成一系列的初始化。

initcall的调用流程:

start_kernel- >   kernel_init- >      kernel_init_freeable- >         do_basic_setup- >            do_initcalls- >               do_initcall_level()

do_initcalls()函数中,会遍历所有等级的initcall,完成初始化。

static void __init do_initcalls(void){ int level; size_t len = strlen(saved_command_line) + 1; char *command_line; command_line = kzalloc(len, GFP_KERNEL); if (!command_line)  panic("%s: Failed to allocate %zu bytes\\n", __func__, len);    //遍历所有等级的initcall,level变量对应等级 for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++) {  /* Parser modifies command_line, restore it each time */  strcpy(command_line, saved_command_line);  do_initcall_level(level, command_line);//执行该等级下的所有函数 } kfree(command_line);}

do_initcall_level()会执行对应等级下的所有函数:

static void __init do_initcall_level(int level, char *command_line){ initcall_entry_t *fn; parse_args(initcall_level_names[level],     command_line, __start___param,     __stop___param - __start___param,     level, level,     NULL, ignore_unknown_bootoption); trace_initcall_level(initcall_level_names[level]); for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)  do_one_initcall(initcall_from_entry(fn));}

module_init等级

module_init宏使用的是device_initcall,等级为6

#define device_initcall(fn)  __define_initcall(fn, 6)......#define __initcall(fn) device_initcall(fn)......#define module_init(x) __initcall(x);

在一些内核驱动中,直接使用了device_initcall()来声明驱动入口,其效果与使用module_init是一样的。

标签:

上一篇:世界热议:Linux内核学习笔记:printk调试
下一篇:最后一页