君正T31学习(7)- 启动流程
一、简介
前面几篇文章大概介绍了T31的系统分区和各个分区的作用,本章就详细介绍一下T31的启动流程,方便了解后续的应用开发。
分区 | 偏移地址 | 大小 |
uboot | 0 | 0x40000(256K) |
tag | 0x40000 | 0x58000(352K) |
kernel | 0x98000 | 0x500000(5120K = 5M) |
rootfs | 0x598000 | 0x300000(3072K = 3M) |
recovery | 0x898000 | 0x280000(2560K = 2.5M) |
system | 0xb18000 | 0x468000(4512K) |
- uboot分区是启动的 “起点”,决定后续所有阶段能否执行。
- tag分区是 U-Boot 与内核的 “通信桥梁”,确保内核获得正确的启动参数。
- kenel分区是系统的 “骨架”,提供硬件管理和进程调度能力。
- system分区是用户空间的 “基础”,存放系统运行必需的程序和配置。
- rootfs分区是数据的 “仓库”,负责持久化存储动态数据。
二、启动流程
1.硬件上电与 Boot ROM 加载 U-Boot(依赖 uboot分区
)
上电复位
当芯片接通电源后,硬件复位电路触发复位信号,CPU从复位向量地址(MIPS架构通常为0xBFC00000)开始执行第一条指令。
Boot ROM 程序执行
复位向量指向芯片内部只读Boot ROM(固化在芯片中的固件),其功能是:
1).初始化最基础的硬件(如内部SRAM、时钟振荡器)
2).检测外部启动介质(根据硬件引脚配置,如BOOT_SEL引脚电平,判断从SPI_FLASH、NANO Flash或TF卡启动)。
3).Boot ROM从uboot分区(Flash起始地址)读取u-boot镜像(含SPL“secondary program loader”阶段代码)到内部SRAM,跳转到u-boot入口。
2.U-Boot 初始化与参数准备(依赖 uboot分区
、tag分区
)
SPL 是精简的启动程序(通常存储在 Flash 起始地址),功能是为加载完整 Bootloader 做准备
U-Boot 分两个阶段执行(SPL 与 full U-Boot),核心任务是为加载内核做准备
SPL 阶段
初始化关键硬件:配置 PLL 时钟(提升 CPU 主频)、初始化 DDR 内存(使外部内存可用)。
加载 full U-Boot:从uboot分区读取完整 U-Boot 镜像到 DDR 内存,跳转到 full U-Boot 执行。
full U-Boot 阶段
扩展硬件初始化:初始化 SPI 控制器(访问 Flash)、UART(串口调试)、网口(可选)。
读取启动参数:从tag分区读取预设参数(如内核启动命令bootargs、硬件配置信息),或使用 U-Boot 环境变量(存储在uboot-env分区,非必需)。
准备内核启动参数:将bootargs(如console=ttyS0,115200 root=/dev/mtdblock3)写入DDR内存的指定地址(供内核读取),若tag分区存在,参数优先从该分区加载。
3.加载内核并启动(依赖kernel分区、tag分区)
读取内核镜像
U-Boot 根据bootcmd命令,从kernel分区读取内核镜像(如uImage),加载到 DDR 内存的指定地址。
传递启动参数
U-Boot将tag分区中的参数或环境变量bootargs打包成内核可识别的格式(如MIPS架构的ATAGS),存储在内存中(通常位于内核镜像地址附近)。
启动内核
执行bootm或bootz命令,将CPU控制权交给内核,启动linux内核
4.内核初始化与挂载根文件系统(依赖kernel分区、system分区、rootfs分区)
内核启动后,从初始化硬件到进入用户空间,依赖多个分区
内核解压与初始化
内核镜像(如uImage)自动解压,执行架构相关初始化(禁用中断、设置页表、初始化进程调度器)
解析 U-Boot 传递的bootargs参数,确定根文件系统位置
驱动初始化与分区识别
初始化 Flash 控制器驱动,识别所有 MTD 分区(包括system、rootfs分区),生成设备节点
挂载根文件系统(rootfs)
首先挂载system分区:该分区通常为只读文件系统(如 SquashFS),包含系统核心程序(/sbin/init
、库文件、配置脚本)
若存在 rootfs 分区
(可读写,如 JFFS2/UBIFS),内核会将其挂载到 /mnt/data
或通过 overlayfs
与 system 分区
合并,实现 “只读系统 + 读写数据” 的混合模式。
5.用户空间初始化(依赖system分区、rootfs分区)
内核启动用户空间第一个进程 init
(位于 system 分区
的 /sbin/init
)后,进入应用启动流程
执行启动脚本
init
进程解析 system 分区
中的 /etc/inittab
或 /etc/init.d/
脚本,启动系统服务(如日志服务、网络配置)
挂载其他分区
执行 mount -a
挂载 rootfs 分区
到指定目录(如 /data
),供应用程序存储用户数据、日志文件
启动业务程序
启动 T31 核心应用(如摄像头采集、视频编码、网络推流),程序运行中产生的动态数据写入 rootfs 分区
,确保系统重启后数据不丢失
三、应用程序启动
如果是针对应用层程序的开发,那么需要了解程序如何跑到应用层。
从第二章可以知道,在kernel中,会挂载roofs。而rootf是根文件系统,所有的应用程序也都是从这里开始启动。系统跑的第一个进程为Init进程(/sbin/init)。
/sbin/init是Linux系统启动后的第一个用户态进程(PID=1),内核启动完成后会主动查找并执行它,后续由它负责初始化系统(如挂载分区、启动服务、初始化终端等)。
由于内核本身不包含用户态程序,/sbin/init必须在系统构建阶段就提前编译好并放入根文件系统,否则内核启动后会因找不到init而报错。
那么我们就需要了解init进程干了什么。
inittab
inittab文件在“SDK/os/rootfs/<4.7.2>/camera/rootfs_camera/etc”目录下。inittab是init进程的核心配置文件,定义了init启动后需要执行的动作、运行级别以及对应的处理逻辑。其作用是告诉Init“系统启动后该做什么,不同运行级别下该启动哪些程序”。
可以看到系统启动的第一个动作就是mount -a。该指令会自动挂载/ertc/fstab中定义的分区。
然后系统指向了/etc/init.d/rcS脚本。
该脚本负责挂载分区、配置网络等基础工作。
接下来执行“console::respawn:/sbin/getty -L console 115200 vt100”
该指令设置,若串口终端退出,init会自动重启它,确保终端始终可用。
最后执行“::shutdown:/bin/umount -a -r”。关机时自动撤销挂载相关分区。
rcS
rcS是Init进程在系统启动阶段执行的主初始化脚本,通常由inittab中的sysinit动作调用。它的作用是串联执行init.d目录中的启动脚本,完成系统初始化。
如果希望某个应用在上电后就启动,可以在该文件中添加命令来自启动,例如:
# 启动核心应用程序(如 T31 的摄像头服务)
/mnt/app/camera_main &
rcS是系统初始化的“总调度员”,负责
挂载文件系统(如rootfs之外的data分区)
按顺序启动init.d中的所有服务脚本(嵌入式Linux通常只有rcS一个文件)
启动最终的业务程序(如T31的摄像头采集、编码服务)。
总结一下:
内核启动后执行/sbin/init进程,init进程读取/etc/initab,发现“::sysinit:/etc/init.d/rcS”,于是执行rcS脚本。rcS脚本挂载fstab中定义的分区,启动业务应用(如摄像头程序)。