3. Linux内核启动
Android 系统启动的第一段真正意义上的“软件旅程”,是从 BootROM 把控制权交给引导程序、引导程序再把 Linux 内核装载进内存开始的。内核启动阶段决定了后续 Android 能否顺利挂载根文件系统、能否找到硬件、能否拉起第一个用户态进程。理解这一段,等于拿到了整条启动链路的“地基图纸”。本节围绕内核加载、设备树传递、init 进程诞生以及内核启动日志分析四个关键点展开。
一、核心原理:从引导程序到内核入口
在 ARM64 平台上,典型的启动顺序是:BootROM → Preloader/SPL → U-Boot/LK → Linux Kernel → init。引导程序完成 DRAM 初始化、存储介质读取后,会把内核镜像(如 Image 或 Image.gz)和设备树二进制文件(dtb)加载到内存指定地址,然后跳转到内核入口。内核入口会依次完成解压(若为压缩镜像)、建立临时页表、初始化内存管理、解析设备树、初始化驱动子系统,最后启动第一个用户态进程。
内核启动的本质是“从无到有地建立运行环境”:先让 CPU 能访问内存,再让内核能识别硬件,最后让用户态能运行。这三步分别对应内核解压与内存初始化、设备树解析与驱动探测、以及 init 进程的创建。
二、设备树:内核与硬件的“契约”
在 ARM 架构中,硬件信息不再像 x86 那样大量依赖 BIOS 探测,而是通过设备树(Device Tree)以数据形式描述。设备树由 .dts 源文件编译为 .dtb,由引导程序传递给内核。内核解析设备树后,才能知道串口寄存器地址、中断控制器、GPIO、I2C 总线、存储控制器等硬件信息。
设备树的核心结构包括根节点、CPU 节点、内存节点、总线节点和外设节点。每个节点通过 compatible 属性与内核驱动匹配,通过 reg 属性描述寄存器地址,通过 interrupts 描述中断号。若设备树与硬件不匹配,常见现象是串口无输出、存储无法识别或系统卡在早期启动。
/* 简化示例:一个 I2C 设备节点 */
&i2c1 {
status = "okay";
clock-frequency = <400000>;
touchscreen@38 {
compatible = "vendor,ts-ft5x06";
reg = <0x38>;
interrupt-parent = <&gpio1>;
interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
};
};
三、init 进程的诞生:内核态到用户态的交接
内核完成初始化后,会尝试挂载根文件系统并执行第一个用户态程序。在 Android 中,这个程序通常是 /init。内核通过 init= 启动参数或默认路径查找 init,若成功执行,内核态启动阶段结束,用户态启动阶段开始。
init 进程的诞生标志着内核已经具备“运行用户程序”的能力。它会继续解析 init.rc、挂载分区、启动服务、设置属性,最终拉起 Zygote 和 SystemServer。因此,内核启动日志中是否出现 init 相关输出,是判断内核是否成功交接的重要标志。
关键交接点:内核启动的最后一步是 run_init_process(),它尝试执行 /init。若该步骤失败,内核会打印 Failed to execute /init 并触发 panic,系统无法进入 Android 用户态。
四、内核启动日志分析:定位问题的第一现场
内核启动日志通常通过串口输出,是排查启动问题的第一手资料。日志按时间顺序记录了内核版本、命令行参数、内存信息、设备树解析、驱动探测、根文件系统挂载以及 init 启动结果。分析日志时,应重点关注以下节点:
- 内核命令行:确认
console=、init=、root=等参数是否正确。 - 设备树解析:查看是否有
OF: fdt相关输出,确认 dtb 是否被正确识别。 - 驱动探测:关注
probe失败、timeout或failed to request等关键字。 - 根文件系统:确认
VFS: Mounted root是否出现,以及挂载类型是否为ext4、f2fs或ramdisk。 - init 启动:查看是否出现
Run /init as init process,这是内核成功交接的标志。
下面是一段典型的内核启动日志片段,可用于对照分析:
[ 0.000000] Booting Linux on physical CPU 0x0
[ 0.000000] Linux version 5.10.66 (build@host)
[ 0.000000] Machine model: Vendor SoC Board
[ 0.000000] earlycon: uart0 at MMIO 0x1c28000
[ 0.000000] Memory: 2048MB available
[ 0.000000] OF: fdt: Machine model: Vendor SoC Board
[ 0.210000] i2c i2c-1: IMX I2C adapter registered
[ 0.350000] mmc0: SDHCI controller on 0x1c0f000
[ 0.520000] VFS: Mounted root (ext4 filesystem)
[ 0.530000] Run /init as init process
五、案例与场景:一次“卡在串口无输出”的复盘
某项目在更换 DDR 颗粒后,系统上电后串口无任何输出。排查思路如下:首先确认 BootROM 是否正常,若 BootROM 有输出但内核无输出,说明问题在引导程序到内核之间;其次检查内核镜像加载地址与设备树地址是否冲突;最后核对设备树中的 chosen 节点是否包含正确的 stdout-path。最终发现是设备树中串口节点被误设为 disabled,导致内核无法输出日志。修复后日志恢复正常。
提示:内核启动日志的“第一行”往往包含 CPU 和内核版本信息,若这一行都没有,说明问题在引导程序或硬件层面,而不是内核内部。
六、常见误区与检查清单
| 误区 | 现象 | 正确做法 |
|---|---|---|
| 设备树与内核版本不匹配 | 驱动 probe 失败或硬件不识别 | 确保 dtb 与内核源码同步编译 |
| init 路径错误 | 内核 panic,提示无法执行 init | 检查 init= 参数和根文件系统 |
| 根文件系统挂载失败 | 卡在 VFS: Cannot open root device | 核对 root= 与分区表 |
| 串口未配置 | 无日志输出,误判为死机 | 检查设备树 stdout-path |
| 忽略早期日志 | 只关注 Android 层日志 | 从内核第一行开始逐段分析 |
内核启动阶段是 Android 启动链路中最接近硬件的一环,也是问题最容易“沉默”的一环。掌握内核加载流程、设备树传递机制、init 进程诞生条件以及日志分析方法,就能在系统无法启动时快速缩小范围,判断问题出在引导程序、内核、设备树还是根文件系统。后续章节将在此基础上,进入 init 进程与 Android 用户态启动的详细分析。