今天继续沿着 一份靠谱的BSP开发学习路线 梳理 BSP 开发的关键知识点—— Linux 内核启动流程。以下分析基于爱芯 AX615 armv7 系列 4.19.125 版本的 内核源码,完整覆盖从 BootROM 到用户态 init 进程的各个阶段。
一、启动总览(AX615 架构)
AX615 采用 armv7 架构,内核启动遵循 BootROM -> U-Boot -> 内核自解压 -> 汇编初始化 -> C 语言核心初始化 -> 用户态切换 的标准流程,各阶段严格衔接,完成从硬件到系统的初始化。
核心流程如下:
- 硬件上电 → BootROM(片上 ROM) → 加载 U-Boot
- U-Boot → 加载内核镜像(
zImage/Image)+ DTB → 跳转内核入口
- 内核自解压(压缩镜像) → 执行汇编入口
stext
- 汇编初始化 → 建立页表 / MMU → 跳转
start_kernel(C 入口)
start_kernel → 子系统初始化 → rest_init → 内核线程 kernel_init
- 挂载根文件系统 → 启动用户态
init 进程 → 系统就绪
二、阶段 1:BootROM 与 U-Boot 引导(内核启动前序)
1. BootROM 阶段(AX615 片上 ROM,不可修改)
- 功能:上电复位后 CPU 从固定地址执行,初始化时钟、DDR 控制器、存储接口(eMMC/SD/SPI-NOR),从指定介质加载 U-Boot 到 DDR。
- 关键路径:AX615 专用 BootROM 代码(厂商提供,非内核源码)。
- 参数传递:加载 U-Boot 后,将启动介质、DDR 配置等信息传递给 U-Boot。
2. U-Boot 阶段(AX615 定制 U-Boot,内核启动“桥梁”)
核心任务:
- 完整初始化 DDR(AX615 专用 DDR 驱动),建立内存运行环境。
- 从存储介质读取内核镜像(
zImage/Image)与设备树(DTB,xxx.dtb)到 DDR 指定地址。
- 解析启动参数(
bootargs),设置寄存器:r0=0、r1=机器ID、r2=DTB物理地址。
- 执行
bootm 命令,跳转至内核镜像入口,将控制权完全交给内核。
三、阶段 2:内核自解压(压缩镜像必备)
1. 入口与核心文件
- 入口地址:U-Boot 跳转的内核镜像起始地址(物理地址,MMU 关闭)。
- 核心文件路径(内核源码):
arch/arm/boot/compressed/head.S
- 链接脚本:
arch/arm/boot/compressed/vmlinux.lds.S(定义解压代码布局)
2. 自解压流程(head.S 执行)
- 保存 U-Boot 传递的寄存器参数(
r0/r1/r2)
- 初始化解压栈,关闭中断,配置 CPU 缓存(D-cache/I-cache)
- 调用
decompress_kernel()(arch/arm/boot/compressed/misc.c),解压内核镜像到 DDR 高端地址
- 解压完成后打印:
Uncompressing Linux... done, booting the kernel.
- 清理解压环境,跳转至内核真正入口
stext(虚拟地址 0xffff000008000000,armv7 标准)
四、阶段 3:汇编初始化(stext,内核第一行执行代码)
1. 核心文件与入口
- 入口符号:
stext(内核链接脚本 ENTRY(stext) 指定)
- 文件路径:
arch/arm/kernel/head.S(armv7 架构核心汇编文件)
- 链接脚本:
arch/arm/kernel/vmlinux.lds.S(编译生成 vmlinux.lds,定义内核内存布局)
2. 汇编初始化关键步骤(head.S)
(1)CPU 与架构校验
- 检查 CPU ID 是否匹配 AX615,不匹配则启动失败。
- 验证机器类型与 DTB 兼容性,确保硬件与内核配置一致。
(2)内存与 MMU 初始化(最核心)
- 建立内核页表(
swapper_pg_dir,arch/arm/mm/proc.S),完成物理地址→虚拟地址映射。
- 使能 MMU 与缓存(D-cache/I-cache),切换到虚拟地址空间,为 C 代码运行奠定基础。
- 初始化内核栈(
init_thread_union),设置栈指针 sp。
(3)BSS 段清零与参数处理
- 清零
.bss 段(未初始化全局变量),避免随机值干扰。
- 解析 U-Boot 传递的 DTB 地址(
r2),保存至全局变量 __fdt_pointer。
- 关闭所有中断,设置异常向量表(
vectors,arch/arm/kernel/entry.S)。
(4)跳转 C 语言入口
- 执行
bl start_kernel,从汇编世界进入 C 语言世界,内核核心初始化开始。
五、阶段 4:C 语言核心初始化(start_kernel,内核“总控中心”)
1. 核心文件与入口
- 函数定义:
init/main.c(内核通用 C 入口,所有架构共享)
- 函数原型:
asmlinkage __visible void __init start_kernel(void)
2. start_kernel 关键初始化流程(按执行顺序)
(1)基础环境初始化
sched_clock_init():初始化调度时钟,为时间管理奠定基础。
setup_arch(&command_line):AX615 架构核心初始化(arch/arm/kernel/setup.c)
- 解析 DTB,获取硬件信息(内存、中断、时钟、外设)
- 初始化内存管理(
memblock),标记可用 / 保留内存区域
- 注册平台设备与驱动
parse_early_param():解析内核启动参数(bootargs)
setup_log_buf():初始化日志缓冲区,启动内核打印
(2)子系统初始化(核心模块依次启动)

(3)后续初始化与线程启动
vfs_caches_init():虚拟文件系统缓存初始化
buffer_init()/pagecache_init():页缓存、缓冲区初始化
fork_init():进程管理初始化(最大 PID、进程表)
rest_init():启动内核线程,进入用户态准备(init/main.c)
六、阶段 5:rest_init 与用户态切换(内核 → 用户空间)
1. rest_init 核心逻辑(init/main.c)
- 创建内核线程 1(PID=1):执行
kernel_init 函数(内核第一个线程)
- 创建
kthreadd 线程(PID=2):管理所有内核线程,负责内核线程创建 / 销毁
- 0 号进程(
init_task)进入 cpu_idle() 循环,成为空闲进程(系统无任务时执行)
2. kernel_init 流程(init/main.c,PID=1)
(1)内核线程初始化
kernel_init_freeable():完成剩余内核初始化(驱动、网络、文件系统)
do_basic_setup():
driver_init():设备驱动模型初始化(总线、设备、驱动注册)
initcalls:执行所有 __init 标记的初始化函数(AX615 外设驱动:I2C、SPI、GPIO、以太网等)
(2)挂载根文件系统(关键)
prepare_namespace():解析 root= 参数,准备挂载根文件系统
mount_root():实际挂载根分区,完成从临时文件系统到真实根文件系统的切换
(3)启动用户态 init 进程
init_post():
- 关闭内核控制台,释放启动资源
- 执行
run_init_process(),尝试启动用户态 init 程序(顺序:/sbin/init → /etc/init → /bin/init → /bin/sh)
- 若成功,内核态 PID=1 消失,用户态
init 进程(PID=1)接管系统,进入用户空间
七、专用关键文件汇总
架构通用核心文件:
- 内核入口汇编:
arch/arm/kernel/head.S
- 自解压入口:
arch/arm/boot/compressed/head.S
- C 语言总入口:
init/main.c(start_kernel/rest_init/kernel_init)
- 架构初始化:
arch/arm/kernel/setup.c
- 内存管理:
arch/arm/mm/mmu.c、mm/mm_init.c
- 中断处理:
arch/arm/kernel/irq.c、kernel/irq/irqdesc.c
八、启动流程关键节点总结
- BootROM:硬件上电 -> 初始化基础外设 -> 加载 U-Boot
- U-Boot:DDR 初始化 -> 加载内核 + DTB -> 跳转内核入口
- 自解压:
arch/arm/boot/compressed/head.S -> 解压内核 -> 跳转 stext
- 汇编初始化:
arch/arm/kernel/head.S -> MMU/页表/栈初始化 -> bl start_kernel
- C 核心初始化:
init/main.c -> start_kernel -> 子系统初始化 -> rest_init
- 用户态切换:
kernel_init -> 挂载根文件系统 -> 启动 /sbin/init -> 系统就绪
九、调试与排查建议
- 启动卡死在解压阶段:检查 U-Boot 加载内核地址、DDR 初始化是否正常、内核镜像是否损坏。
- 卡死在
stext 汇编阶段:核对 DTB 与内核兼容性、MMU 页表配置、AX615 CPU ID 校验。
- 卡死在
start_kernel 之后:开启内核调试打印(bootargs 添加 debug),定位子系统初始化失败点。
- 根文件系统挂载失败:检查
root= 参数、存储驱动(eMMC/SD)是否初始化、文件系统格式是否正确。
以上为全文内容。
|