initcall 驱动初始化机制与实现详解
源码路径:
rk3588/kernel-6.1/init/main.c、include/linux/init.h
内核版本:Linux 6.1(RK3588 / ARM64)
文档目录:linuxDoc/init/
目录
一、原理
1.1 设计目标
内核与子系统在编译期通过 __define_initcall(level, fn) 把初始化函数放入链接器段;启动时 按级别顺序 调用,无需中央注册表。执行完毕后,__init 代码随 free_initmem() 回收。
1.2 与 module_init 的关系
可加载模块的 module_init() 在 insmod 时执行;内置驱动等价逻辑通常落在 device 或 late 级 initcall。
二、实现方式
2.1 调用链
1 | kernel_init_freeable() |
2.2 七级划分
| Level | 名称 | 段边界 |
|---|---|---|
| 0 | pure | __initcall0_start |
| 1 | core | __initcall1_start |
| … | … | … |
| 7 | late | __initcall7_start → __initcall_end |
do_initcall_level() 每级:
- 用该级名解析 级别相关 内核参数(
parse_args+level匹配) - 遍历该级段内所有 entry,调用
do_one_initcall()
2.3 do_one_initcall()
1 | int __init_or_module do_one_initcall(initcall_t fn) |
initcall_blacklist=:跳过指定符号(需CONFIG_KALLSYMS)initcall_debug:打印每个函数耗时(tracepoint 或 printk)
2.4 异步 initcall(CONFIG_INITCALL_ASYNC)
- 启动参数
initcall_nr_threads=控制 worker 数(默认约2 * num_online_cpus) - 每级内部分 initcall 在
init/%dkthread worker 上并行 - 段
__initcallNs_start与__initcallN_start区分 sync/async 边界 - 级末
kthread_flush_worker保证该级完成后再进下一级
三、关键数据结构与宏
| 宏(init.h) | 级别 |
|---|---|
pure_initcall(fn) |
0 |
core_initcall(fn) |
1 |
postcore_initcall(fn) |
2 |
arch_initcall(fn) |
3 |
subsys_initcall(fn) |
4 |
fs_initcall(fn) |
5 |
device_initcall(fn) |
6 |
late_initcall(fn) |
7 |
rootfs 相关:rootfs_initcall(在 initramfs populate 等中使用,与上述七级并列概念,见 05)。
四、RK3588 平台说明
RK3588 上 eMMC/UFS/PCIe/DRM/VPU 等驱动 probe 主要在 subsys / fs / device 级完成。prepare_namespace() 之前的 wait_for_device_probe()(在 do_mounts.c)会等待异步 probe,因此 initcall 慢的驱动 会直接拉长 挂根时间(见 04)。
Thunder Boot 路径上,部分内存初始化被推迟,不改变 initcall 级别顺序,但可能改变 内存分配压力 的时间分布。
五、调试与观测
1 | # 内核命令行 |
| 工具 | 用途 |
|---|---|
dmesg 中 calling ... / returned |
定位慢 initcall |
cat /proc/interrupts 之后 |
确认 device 级 IRQ 注册 |
附录:源码索引
| 符号 | 文件 |
|---|---|
do_initcalls |
init/main.c ~1574 |
do_initcall_level |
~1552 |
do_one_initcall |
~1304 |
initcall_levels[] |
~1345 |
正在加载留言…