![](http://img.inpai.com.cn/2023/0410/20230410091623448.jpg)
寫過Linux驅(qū)動(dòng)的人都知道module_init
宏,因?yàn)樗暶髁艘粋€(gè)驅(qū)動(dòng)的入口函數(shù)。
除了module_init
宏,你會(huì)發(fā)現(xiàn)在Linux內(nèi)核中有許多的驅(qū)動(dòng)并沒有使用module_init
宏來聲明入口函數(shù),而是看到了許多諸如以下的聲明:
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);
上述舉例的兩個(gè)驅(qū)動(dòng)入口分別使用了device_initcall()
和arch_initcall()
來聲明驅(qū)動(dòng)入口,這些本質(zhì)上都是對(duì)initcall
的調(diào)用,module_init
也如此。
(相關(guān)資料圖)
Linux內(nèi)核對(duì)initcall進(jìn)行了等級(jí)劃分,每一種類型的initcall都有對(duì)應(yīng)等級(jí),等級(jí)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越小等級(jí)越高,Linux會(huì)按照等級(jí)由高到低順序執(zhí)行:
/* * 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)
這么做的目的主要是根據(jù)優(yōu)先級(jí)依次對(duì)設(shè)備進(jìn)行初始化,例如會(huì)先初始化與架構(gòu)相關(guān)的,然后再初始化內(nèi)核子系統(tǒng)。
在Linux啟動(dòng)時(shí),會(huì)依次遍歷所有等級(jí)的initcall,以完成一系列的初始化。
initcall
的調(diào)用流程:
start_kernel- > kernel_init- > kernel_init_freeable- > do_basic_setup- > do_initcalls- > do_initcall_level()
在do_initcalls()
函數(shù)中,會(huì)遍歷所有等級(jí)的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); //遍歷所有等級(jí)的initcall,level變量對(duì)應(yīng)等級(jí) 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);//執(zhí)行該等級(jí)下的所有函數(shù) } kfree(command_line);}
do_initcall_level()
會(huì)執(zhí)行對(duì)應(yīng)等級(jí)下的所有函數(shù):
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
宏使用的是device_initcall
,等級(jí)為6
:
#define device_initcall(fn) __define_initcall(fn, 6)......#define __initcall(fn) device_initcall(fn)......#define module_init(x) __initcall(x);
在一些內(nèi)核驅(qū)動(dòng)中,直接使用了device_initcall()
來聲明驅(qū)動(dòng)入口,其效果與使用module_init
是一樣的。
標(biāo)簽: