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. 市民生活开始(用户程序):市民开始工作生活,城市正常运转

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

相关推荐
Yana.nice1 小时前
openssl将证书从p7b转换为crt格式
java·linux
AI逐月1 小时前
tmux 常用命令总结:从入门到稳定使用的一篇实战博客
linux·服务器·ssh·php
小白跃升坊2 小时前
基于1Panel的AI运维
linux·运维·人工智能·ai大模型·教学·ai agent
跃渊Yuey2 小时前
【Linux】线程同步与互斥
linux·笔记
舰长1152 小时前
linux 实现文件共享的实现方式比较
linux·服务器·网络
zmjjdank1ng2 小时前
Linux 输出重定向
linux·运维
路由侠内网穿透.2 小时前
本地部署智能家居集成解决方案 ESPHome 并实现外部访问( Linux 版本)
linux·运维·服务器·网络协议·智能家居
VekiSon3 小时前
Linux内核驱动——基础概念与开发环境搭建
linux·运维·服务器·c语言·arm开发
zl_dfq3 小时前
Linux 之 【进程信号】(signal、kill、raise、abort、alarm、Core Dump核心转储机制)
linux
Ankie Wan3 小时前
cgroup(Control Group)是 Linux 内核提供的一种机制,用来“控制、限制、隔离、统计”进程对系统资源的使用。
linux·容器·cgroup·lxc