CPU上电启动到程序运行全流程详解

🔥作者简介: 一个平凡而乐于分享的小比特,中南民族大学通信工程专业研究生,研究方向无线联邦学习

🎬擅长领域:驱动开发,嵌入式软件开发,BSP开发

❄️作者主页:一个平凡而乐于分享的小比特的个人主页

✨收录专栏:Linux,本专栏目的在于,记录学习Linux操作系统的总结

欢迎大家点赞 👍 收藏 ⭐ 加关注哦!💖💖

CPU上电启动到程序运行全流程详解

一、启动概览全景图

复制代码
┌─────────────────────────────────────────────────────────────────────────────┐
│                            CPU启动全流程                                    │
├─────────────────┬─────────────┬────────────┬─────────────┬─────────────┤
│   上电瞬间      │   Boot阶段  │   UBoot阶段│  Linux内核  │  用户空间   │
├─────────────────┼─────────────┼────────────┼─────────────┼─────────────┤
│ Boot引脚配置    │ ROM Code    │ UBoot SPL  │ 内核解压    │ systemd     │
│ 启动介质选择    │ (片上固件)  │ (第一阶段) │ 驱动初始化  │ 或init      │
│ USB/自启动      │             │ UBoot      │ 挂载根文件  │ 启动服务    │
│                 │             │ (第二阶段) │ 系统        │ 用户程序    │
└─────────────────┴─────────────┴────────────┴─────────────┴─────────────┘

二、启动方式选择:Boot引脚配置

对比表格:不同CPU的启动差异

特性 ARMv7 (imx6ull) ARMv8 (imx8mp) 类比场景
ATF功能 不带ATF 直接启动Bootloader 带ATF 安全启动框架 如大楼保安系统 : ARMv7:普通门锁 ARMv8:高级安保系统+指纹识别
启动流程 简单直接 多层安全验证
USB烧录模式 BOOT_MODE[1:0]=10 类似,但通过ATF验证 如手机刷机 : 普通模式:正常开机 下载模式:刷入新系统
自启动模式 BOOT_MODE[1:0]=00 BOOT_MODE[1:0]=00

Boot引脚工作原理图

复制代码
┌─────────────────────────────────────┐
│        BOOT引脚配置电路              │
├─────────────────────────────────────┤
│  ┌─────┐  ┌─────┐  ┌─────┐        │
│  │引脚1│  │引脚2│  │引脚n│  ← PCB上的拨码开关或电阻 │
│  └─────┘  └─────┘  └─────┘        │
│     │        │        │            │
│  ┌──┴──┐  ┌──┴──┐  ┌──┴──┐        │
│  │ 高/ │  │ 高/ │  │ 高/ │        │
│  │ 低电平│  │ 低电平│  │ 低电平│        │
│  └─────┘  └─────┘  └─────┘        │
│                                    │
│   ↓ 上电时CPU读取引脚状态           │
│   ↓ 确定启动介质和模式              │
└─────────────────────────────────────┘

三、第一阶段启动流程(芯片厂商固件)

详细流程示意图

复制代码
┌─────────────────────────────────────────────────────┐
│             第一阶段:ROM Code执行                    │
├─────────────┬───────────────────────────────────────┤
│ 步骤        │ 功能说明                              │
├─────────────┼───────────────────────────────────────┤
│ 1.上电复位   │ CPU从固定地址(0x00000000)开始执行      │
│             │ ↓                                     │
│ 2.读取Boot引脚│ 确定启动介质:                       │
│             │ • SD卡    → 读取第一个扇区            │
│             │ • eMMC    → 读取boot分区              │
│             │ • NAND    → 读取前几KB                │
│             │ • USB     → 等待主机连接              │
│             │ ↓                                     │
│ 3.加载SPL    │ 从启动介质加载SPL(Secondary Program   │
│             │ Loader)到内部SRAM                     │
│             │ ↓                                     │
│ 4.设置栈指针 │ 为C语言运行准备栈空间                 │
│             │ ↓                                     │
│ 5.跳转到SPL  │ 开始执行第二阶段引导                  │
└─────────────┴───────────────────────────────────────┘

ATF与非ATF对比

复制代码
不带ATF的ARMv7 (imx6ull):
┌─────────┐     ┌─────────┐     ┌─────────┐
│  ROM    │────▶│  SPL    │────▶│  UBoot  │
│ Code    │     │ (DDR初始化)│     │         │
└─────────┘     └─────────┘     └─────────┘

带ATF的ARMv8 (imx8mp):
┌─────────┐     ┌─────────┐     ┌─────────┐     ┌─────────┐
│  ROM    │────▶│   ATF   │────▶│  SPL    │────▶│  UBoot  │
│ Code    │     │(安全监控)│     │         │     │         │
└─────────┘     └─────────┘     └─────────┘     └─────────┘
      安全检查层          DDR初始化层         完整引导层

四、第二阶段:UBoot引导

UBoot两阶段架构图

复制代码
┌─────────────────────────────────────────────────────────┐
│                  UBoot两阶段架构                         │
├──────────────┬──────────────────────────────────────────┤
│  第一阶段(SPL) │ 第二阶段(完整UBoot)                     │
├──────────────┼──────────────────────────────────────────┤
│ 位置:内部SRAM │ 位置:外部DDR                          │
│ 大小:~64KB   │ 大小:几百KB~几MB                       │
│ 功能:        │ 功能:                                  │
│ 1.初始化时钟   │ 1.初始化更多外设                        │
│ 2.初始化DDR    │ 2.检测内存映射                          │
│ 3.初始化串口   │ 3.加载内核到内存                        │
│ 4.加载第二阶段 │ 4.设置启动参数                          │
│ 代码到DDR     │ 5.启动内核                              │
│ 5.跳转        │                                        │
└──────────────┴──────────────────────────────────────────┘

第二阶段详细任务表

步骤 具体操作 类比场景
① 初始化硬件 • 初始化网络(Ethernet) • 初始化存储设备(SD/eMMC) • 初始化显示设备(可选) 如装修房子:先通水电网络,再搬家具
② 检测内存映射 扫描DDR控制器,确定: • 内存起始地址 • 内存大小 • 内存区域划分 如仓库管理员:清点仓库分区和容量
③ 加载内核 从存储设备读取: uImage(压缩内核)或Image 到DDR的特定地址(如0x80800000) 如搬家:从仓库(Flash)把家具(内核)搬到新家(DDR)
④ 设置启动参数 准备bootargs: • 控制台参数console= • 根文件系统root= • 文件系统类型rootfstype= 如旅行指南:告诉司机(内核)目的地和路线
⑤ 调用内核 跳转到内核入口地址,并传递: • 机器ID • ATAGs或DTB地址 • bootargs参数 如交接工作:把任务清单交给下一任负责人

五、内核启动参数详解

bootargs参数配置示例表

参数 示例值 说明
console console=ttymxc0,115200 指定控制台串口和波特率
root root=/dev/mmcblk1p2 root=/dev/nfs 根文件系统位置: • MMC第1设备第2分区 • NFS网络文件系统
rootfstype rootfstype=ext4 rootfstype=squashfs 文件系统类型
ip ip=192.168.1.100::::eth0:dhcp 网络配置(网络根文件系统时)
init init=/linuxrc init=/sbin/init 指定init程序
其他 rw earlyprintk mem=512M 读写模式、早期打印、内存大小

两种常见启动方式对比

复制代码
从本地存储启动:
┌─────────┐     ┌────────────┐     ┌────────────┐
│ UBoot   │────▶│ Linux内核   │────▶│ 根文件系统  │
│         │     │            │     │ (eMMC/SD)  │
└─────────┘     └────────────┘     └────────────┘
      传递bootargs:                 mount /
      root=/dev/mmcblk1p2
      rootfstype=ext4

从网络启动(NFS):
┌─────────┐     ┌────────────┐     ┌────────────┐
│ UBoot   │────▶│ Linux内核   │────▶│ NFS服务器   │
│         │     │            │     │ 根文件系统  │
└─────────┘     └────────────┘     └────────────┘
      传递bootargs:                 mount -t nfs
      root=/dev/nfs               192.168.1.10:/nfsroot
      ip=192.168.1.100

六、Linux内核初始化

内核启动流程图

复制代码
┌─────────────────────────────────────────────────────┐
│              Linux内核初始化阶段                      │
├──────────────┬──────────────────────────────────────┤
│ 阶段         │ 主要操作                              │
├──────────────┼──────────────────────────────────────┤
│ 1.解压内核    │ • 如果是zImage/uImage,先解压         │
│              │ • 如果是Image,直接加载                │
│              │ ↓                                     │
│ 2.早期初始化   │ • 初始化CPU和内存基本功能              │
│              │ • 建立初始内存页表                     │
│              │ • 开启MMU(内存管理单元)                │
│              │ ↓                                     │
│ 3.串口初始化   │ • 初始化控制台串口                    │
│              │ • 启用printk输出                      │
│              │ ↓                                     │
│ 4.驱动初始化   │ • 按顺序初始化所有驱动:               │
│              │   1) 平台设备驱动                      │
│              │   2) 子系统驱动                        │
│              │   3) 文件系统驱动                      │
│              │ ↓                                     │
│ 5.挂载根文件系统│ • 根据root=参数找到根设备             │
│              │ • 挂载根文件系统                       │
│              │ • 切换到根文件系统                     │
│              │ ↓                                     │
│ 6.启动init进程│ • 执行init=指定的程序                 │
│              │ • 通常是/sbin/init                    │
└──────────────┴──────────────────────────────────────┘

MMU启用前后对比

复制代码
启用MMU前:                           启用MMU后:
┌─────────────────┐                  ┌─────────────────┐
│  物理地址空间     │                  │  虚拟地址空间     │
│  (1:1映射)       │                  │  (页表映射)      │
│                  │                  │                  │
│  0x80000000 ─────┼─▶物理内存        │  0xC0000000 ─────┼─▶物理内存
│  0x90000000 ─────┼─▶外设寄存器       │  0xF0000000 ─────┼─▶外设寄存器
│                  │                  │                  │
│ 直接访问硬件       │                  │ 通过页表转换       │
│ 无内存保护        │                  │ 有内存保护         │
└─────────────────┘                  └─────────────────┘

七、用户空间初始化:init系统

SystemV init vs systemd对比

方面 SystemV init (传统) systemd (现代) 类比
配置文件位置 /etc/inittab /etc/init.d/ /etc/systemd/system/ /lib/systemd/system/ 如食谱 : 旧:纸质菜谱本 新:电子菜单系统
配置文件格式 Shell脚本 .service单元文件
启动方式 顺序串行启动 并行启动 如餐厅开业 : 旧:一个一个准备工作 新:同时准备所有事项
服务管理 service xxx start /etc/init.d/xxx start systemctl start xxx systemctl enable xxx
日志管理 分散的日志文件 journalctl统一管理

systemd服务文件示例

ini 复制代码
# /etc/systemd/system/myapp.service
[Unit]
Description=My Application
After=network.target    # 在网络服务之后启动

[Service]
Type=simple
ExecStart=/usr/bin/myapp --daemon
Restart=on-failure      # 失败时自动重启
User=appuser            # 以指定用户运行
Group=appgroup

[Install]
WantedBy=multi-user.target  # 在哪个运行级别启用

SystemV启动脚本示例

bash 复制代码
#!/bin/bash
# /etc/init.d/myapp

case "$1" in
    start)
        echo "Starting myapp..."
        /usr/bin/myapp --daemon
        ;;
    stop)
        echo "Stopping myapp..."
        killall myapp
        ;;
    restart)
        $0 stop
        sleep 1
        $0 start
        ;;
    *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
        ;;
esac

八、完整启动时间线图

复制代码
时间轴:0ms → 100ms → 200ms → 500ms → 1s → 3s → 完成
        │        │        │        │      │      │
┌───────┼────────┼────────┼────────┼──────┼──────┼─────────────┐
│阶段   │ROM Code│  SPL   │ UBoot  │内核  │init  │ 用户程序     │
│       │ 执行   │ 执行   │ 执行   │初始化│系统  │              │
├───────┼────────┼────────┼────────┼──────┼──────┼─────────────┤
│关键   │读取Boot│初始化  │加载    │挂载  │启动  │应用服务      │
│操作   │引脚    │DDR     │内核    │根文件│服务  │运行          │
│       │加载SPL │跳转到  │传递参数│系统  │      │              │
│       │        │UBoot   │        │      │      │              │
└───────┴────────┴────────┴────────┴──────┴──────┴─────────────┘
实际时间: 1-10ms   10-50ms  50-200ms  200-500ms 500ms-2s  2s+

九、常见启动问题排查表

现象 可能原因 排查方法
无任何输出 1. Boot引脚配置错误 2. 电源问题 3. 晶振不起振 1. 检查Boot引脚电压 2. 测量各路电源 3. 示波器测时钟
停在ROM Code 1. 启动介质无有效程序 2. 存储设备通信失败 1. 检查SPL是否烧写 2. 测量SD卡/eMMC时钟和数据线
UBoot启动失败 1. DDR初始化失败 2. SPL加载UBoot出错 1. 检查DDR配置参数 2. 验证UBoot镜像CRC
内核panic 1. bootargs错误 2. 根文件系统错误 3. 驱动问题 1. 检查console参数 2. 验证根文件系统 3. 查看panic信息
无法挂载根文件系统 1. root=参数错误 2. 文件系统损坏 3. 驱动缺失 1. 检查设备节点 2. fsck检查文件系统 3. 确认内核包含对应驱动

十、总结:从硬件到软件的完整旅程

整个启动过程就像建造和启动一座现代化城市

  1. 奠基阶段(Boot引脚+ROM Code):选址规划,确定城市类型和基础
  2. 基础设施建设(SPL):修建道路、铺设水电网络(DDR初始化)
  3. 城市框架搭建(UBoot):建立政府机构,制定法规(bootargs)
  4. 核心系统启动(Linux内核):启动警察、消防、医疗等核心服务(驱动)
  5. 民生服务启动(init):开设学校、商店、公交等公共服务(服务进程)
  6. 市民生活开始(用户程序):市民开始工作生活,城市正常运转

每个阶段都依赖前一阶段的正确完成,任何环节出错都会导致启动失败。理解这个完整流程,有助于在开发调试时快速定位问题所在。

相关推荐
不像程序员的程序媛18 小时前
Linux开机自启动systemd配置
linux·运维·服务器
GREGGXU18 小时前
Could not load the Qt platform plugin “xcb“ in ““ even though it was found.
linux·qt
optimistic_chen18 小时前
【Redis 系列】持久化特性
linux·数据库·redis·分布式·中间件·持久化
食咗未18 小时前
Linux tcpdump工具的使用
linux·服务器·网络·驱动开发·tcp/ip·测试工具·tcpdump
YJlio18 小时前
WinObj 学习笔记(15.7):看懂内核对象管理器与命名空间的“地图”
linux·服务器·网络·windows·笔记·学习·微信
xingzhemengyou118 小时前
Linux lastlog查询和显示所有用户最近一次登录信息
linux·服务器
我的golang之路果然有问题19 小时前
linux 个人笔记导出之网络,防火墙,定时,权限,后台
linux·运维·服务器·网络·笔记·个人笔记
Sapphire~19 小时前
odoo-087 安装 npm (node ok npm not)
linux·运维·npm
唐装鼠19 小时前
linux vscode解压版 AI账号无法登陆问题(浏览器无法打开vscode)
linux·运维·vscode