当前位置:首页>Linux>【深度分析】: Linux内核启动流程

【深度分析】: Linux内核启动流程

  • 2026-10-10 16:39:28
【深度分析】: Linux内核启动流程
Hello,大家好,我是程序媛MM。

本文约2600字,今天继续沿着《一份靠谱的BSP开发学习路线》来学习BSP开发所需的知识--Linux内核启动流程。本文根据爱芯AX615 armv7系列4.19.125版本的内核源码来梳理内核启动流程。

关注公众号, 即可获得与Linux相关的电子书籍以及常用开发工具,文末有文档清单。


一 启动总览(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)

>>CPU 与架构校验

检查 CPU ID 是否匹配 AX615,不匹配则启动失败

验证机器类型与 DTB 兼容性,确保硬件与内核配置一致

>>内存与 MMU 初始化(最核心)

建立内核页表(swapper_pg_dir,arch/arm/mm/proc.S),完成物理地址→虚拟地址映射

使能 MMU 与缓存(D-cache/I-cache),切换到虚拟地址空间,为 C 代码运行奠基

初始化内核栈(init_thread_union),设置栈指针sp

>>BSS 段清零与参数处理

清零.bss段(未初始化全局变量),避免随机值干扰

解析 U-Boot 传递的 DTB 地址(r2),保存至全局变量__fdt_pointer

关闭所有中断,设置异常向量表(vectors,arch/arm/kernel/entry.S)

>>跳转 C 语言入口

执行bl start_kernel,从汇编世界进入 C 语言世界,内核核心初始化开始

五 阶段 4:C 语言核心初始化(start_kernel,内核 “总控中心”)


[1]. 核心文件与入口

函数定义:init/main.c(内核通用 C 入口,所有架构共享)

函数原型:asmlinkage __visible void __init start_kernel(void)

[2]. start_kernel关键初始化流程(按执行顺序)

>>基础环境初始化

sched_clock_init():初始化调度时钟,为时间管理奠基

setup_arch(&command_line):AX615 架构核心初始化(arch/arm/kernel/setup.c)

解析 DTB,获取 硬件信息(内存、中断、时钟、外设)

初始化内存管理(memblock),标记可用 / 保留内存区域

注册平台设备与驱动

parse_early_param():解析内核启动参数(bootargs)

setup_log_buf():初始化日志缓冲区,启动内核打印

>>子系统初始化(核心模块依次启动)

>>后续初始化与线程启动

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)

>>内核线程初始化

kernel_init_freeable():完成剩余内核初始化(驱动、网络、文件系统)

do_basic_setup():

driver_init():设备驱动模型初始化(总线、设备、驱动注册)

initcalls:执行所有__init标记的初始化函数(AX615 外设驱动:I2C、SPI、GPIO、以太网等)

>>挂载根文件系统(关键)

prepare_namespace():解析root=参数,挂载根文件系统

mount_root():实际挂载根分区,完成从临时文件系统到真实根文件系统的切换

>>启动用户态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

八 启动流程关键节点总结


[1].BootROM:硬件上电->初始化基础外设->加载 U-Boot

[2].U-Boot:DDR 初始化->加载内核 + DTB->跳转内核入口

[3].自解压:arch/arm/boot/compressed/head.S->解压内核->跳转stext

[4].汇编初始化:arch/arm/kernel/head.S->MMU/页表/栈初始化->bl start_kernel

[5].C 核心初始化:init/main.c->start_kernel->子系统初始化->rest_init

[6].用户态切换:kernel_init->挂载根文件系统->启动/sbin/init->系统就绪

九 调试与排查建议


[1].启动卡死在解压阶段:检查 U-Boot 加载内核地址、DDR 初始化是否正常、内核镜像是否损坏

[2].卡死在 stext汇编阶段:核对 DTB 与内核兼容性、MMU 页表配置、AX615 CPU ID 校验

[3].卡死在start_kernel后:开启内核调试打印(bootargs添加debug),定位子系统初始化失败点

[4].根文件系统挂载失败:检查root=参数、存储驱动(eMMC/SD)是否初始化、文件系统格式是否正确

以上为全文内容。

往期文章(欢迎订阅技术分享栏目全部文章):

【从零开始撸内核驱动源码】:以ttyserial(串口驱动)为例,串联字符设备驱动基础知识点的学习计划
Linux内核源码顶层 Makefile分析并单独编译调试内核自带的驱动
【从零开始撸内核驱动源码】:ttynull驱动
Linux内核驱动安装失败问题调试及解决方法
Linux内核驱动源码走读之编译内核及外部驱动实操指南

“谢谢你看到这里”

这里是女程序员的笔记本

 15年+嵌入式软件工程师兼二胎宝妈

分享读书心得、工作经验,自我成长和生活方式。

希望我的文字能对你有所帮助

最新文章

随机文章