一、嵌入式设备启动流程

1、运行厂家固化在IROM里面的代码

a.基本硬件初始化(需要的时钟和设备)

b. 判断对应的启动方式(emmc;sd卡;usb)

c. 把bootloader第一阶段的代码加载到IRAM(芯片内运行内存)

2.在IRAM里运行对应的bootloader第一阶段的代码

SPL(Secondary Program Loader(二级程序加载器)):u-boot-spl.bin

a.初始化整个系统的时钟;

b.初始化内存

c.把完整的bootloader搬移到内存中

3.在内存里运行bootloader第二个阶段的代码

a.初始化了一些硬件设备(根据开发板去定义(网卡;串口;USB))

 b.加载对应的操作系统(内存)

4.运行操作系统(没有图形界面的)

5.挂载文件系统(EXT(2,3,4))

6.启动应用程序

不在IROM里初始化内存的原因:因为芯片厂家不知道开发板采用什么样的内存,没有办法进行初始化
为啥bootloader分两个阶段?
运行芯片内部存储空间有限,如果内部空间做大,成本提高

二、U-boot和Linux 内核之间的参数传递

        内核自身上电不会主动扫描、探测全部硬件资源,依赖 U-Boot 传递基础信息才能正常初始化系统。根文件系统路径、串口控制台、网卡 IP、NFS 挂载配置这类运行参数,完全依靠 U-Boot 的bootargs传递给内核之后才能正确启动硬件系统。

数据流向:

U-Boot(setenv bootargs) → bootm启动内核时,把bootargs放入内存固定区域 → Linux内核
启动解析该字符串

参数传递方式(通过寄存器)

寄存器
r0 规定为0,不传递有效信息
r1 开发板的ID
r2 其他参数在内存中开始地址

r1 : 存放机器 ID(Machine ID),内核根据该 ID 匹配内核中注册的单板平台代码,区分不同开发板硬件平台

r2 : 存放内存这片区域存放内存大小、告诉Linux内核启动后,挂载文件系统的方式、bootargs 命令行、串口、时钟等全部系统参数

        U-boot只是告诉了数据在什么位置,没有告诉内核可以访问的内存大小,数据什么格式,什么时候结束,Linux内核如何知道它访问的参数结束的地方在哪里?

        u_boot和Linux之间在传递参数的时候,是按照双方约定的格式传递,格式包含:1.struct parm__struct结构体,2.tag列表,3.设备树.

bootargs

内核刚启动阶段只会初始化最基础的 CPU、MMU,不会自己自动扫描挂载根文件系统、匹配串口输出、不会配置网卡 IP,全部依靠 bootargs 告知内核该怎么工作。

root=/dev/nfs nfsroot=192.168.2.128:/home/hqyj/fs4412/rootfs,proto=tcp,nfsvers=3 rw 
console=ttySAC2,115200 init=/linuxrc ip=192.168.2.234
参数片段 具体作用 补充说明
root=/dev/nfs 指定根文件系统的介质类型为 NFS 网络文件系统 告知内核去哪里寻找系统最顶层的根目录,是系统能否启动的核心
nfsroot=IP:目录路径 填写 PC 主机 IP + Ubuntu 上 rootfs 的绝对路径 内核通过网线挂载电脑里的根文件系统,开发调试常用
proto=tcp,nfsvers=3 NFS 传输使用 TCP 协议、协议版本为 3 提升传输稳定性,适配 Ubuntu 默认 NFS 服务
rw 根文件系统以读写模式挂载 不加该参数默认只读,板子无法创建设备节点、保存文件,会卡死启动
console=ttySAC2,115200 指定调试串口:设备名 ttySAC2,波特率 115200 内核打印日志、命令行交互的串口,配置错误则串口无打印
ip=192.168.2.234 开发板开机自动配置静态 IP 地址 和电脑 IP 处于同一网段,保证 NFS 网络通信正常
init=/linuxrc 指定用户空间第一个运行的程序 内核启动后执行的第一个用户态程序是 /linuxrc(通常是 BusyBox 提供的初始化脚本或软链接到 /sbin/init)

bootcmd

bootcmd 是 U-Boot 的环境变量,存放一串 U-Boot 可执行命令.

作用:开发板上电倒计时结束后,U-Boot 自动执行这组命令,完成加载内核、dtb、启动 Linux 系统整套流程

bootcmd=tftp 41000000 uImage;tftp 42000000 exynos4412-fs4412.dtb;
tftp 43000000 ramdisk.img;bootm 41000000 43000000 42000000

uImage:是 U-Boot 引导加载程序专用的、经过特殊格式封装的 Linux 内核镜像文件。

exynos4412-fs4412.dtb:设备树文件。

ramdisk.img:自己的系统映象文件。

tftp 0x41000000 uImage

通过 TFTP 从电脑下载内核 uImage,存到板子内存地址 0x41000000

tftp 0x42000000 exynos4412-fs4412.dtb
下载设备树 dtb 文件,存入内存 0x42000000

bootm 41000000 43000000 42000000

正式启动 Linux 内核
格式:bootm 内核地址 ramdisk地址 dtb地址

三、Kconfig Makefile .config menuconfig之间的关系

总关系Kconfig 定义选项规则 → menuconfig 让你可视化选择 → 选择结果存成 .config → Makefile 读 .config 决定编译哪些 .c 文件 → 最终生成 uImage。

1.Kconfig--"菜单的菜谱"

相当于配置对应的宏 ,将用户选择的功能配置项通过宏的形式确定下来。比如配置为Y代表将对应功能直接编译进内核,M代表编译进模块,n表示不编译。

2.menuconfig--"点菜界面"

读取所有 Kconfig 文件,生成一个图形化的菜单界面。在里面用空格键勾选/取消选项。它本身不修改 Kconfig,只是可以让使用者方便地选择。

3.config - -"点好的菜单"

.config 是人可读的纯文本,记录了用户最终选定的内核功能。

4.Makefile--"后厨按菜单做菜"

管理Linux内核的编译过程,根据.config里面的宏配置决定编译哪些代码

四、U-boot启动过程

ARM核的设置

1.设置CPU 为SVC模式   2.设置异常向量表   3.关闭Cache和MMU(内存管理单元:A.内存访问权限设置 B.物理地址和虚地址的映射);

SOC芯片的设置

4.处理唤醒的条件   5.判断是否在内存中运行   6.如果不在内存中运行,则初始化系统时钟和内存控制器(第一阶段) 如果在内存中运行,则不需要初始化系统时钟和内存控制器(第二阶段)   7.初始化uart   8.设置sp; 

 9.调用board_init_f

第一阶段代码: board_init_f 功能将u_boot从存储器搬移到内存,跳到内存中运行

第二阶段代码: board_init_f (1)执行了初始化序列表 (板子相关的初始化操作)   (2)为u_boot重定向预留内存

10.对u_boot重定向 (从低端内存搬移到高端内存)   

11.清bss段 12.board_init_r

[1]外围硬件初始化 (emmc 、网卡)   [2]让串口作为标准输入输出设备   [3]main_loop;

探测自动启动是否被打断,如果没有被打断,则执行bootcmd指定的命令 如果被打断了,则进入u_boot交互界面

五、Linux启动过程

1.CPU 合法性校验
        读取 CPU 标识,内核检索自身配置,判断是否支持当前 CPU;不支持则直接打印错误并终止启动。
2.单板机器 ID 合法性校验
        读取 U‑Boot 通过r1寄存器传递的板子 Machine ID,和内核内置支持的单板列表比对;ID 不匹配打印报错,大概率是内核配置与开发板不匹配。
3.内存管理基础搭建
        校验全部通过后,调用函数创建一级页表 → 开启 MMU 虚拟内存机制,内核正式进入虚拟地址寻址模式。
4.基础环境初始化
        清空 BSS 段、设置栈指针、保存 CPU 编号、单板 ID、ATAGS 参数基地址等全局数据。
5.汇编跳转 C 代码
        使用汇编指令跳转至start_kernel(),从汇编环境进入 C 语言的内核主体运行。

    六、busybox init启动过程

    1. 启动 init 进程

    Linux 内核完成根文件系统挂载后,执行 init 进程(进程 ID 为 1)。在 Busybox 中,这个 init 就是 /sbin/init,由 Busybox 提供。

    2. 读取并解析 /etc/inittab

    init 启动后,首先读取并解析 /etc/inittab 文件,然后按照该文件的配置去启动相应的应用程序和脚本。

    ::sysinit:/etc/init.d/rcS    # 系统初始化最先执行rcS脚本(最关键)
    ::askfirst:-/bin/sh          # 串口控制台拉起一个shell交互终端
    ::restart:/sbin/init        # init异常重启的处理
    ::ctrlaltdel:/sbin/reboot   # 按键重启系统

    3.执行 /etc/init.d/rcS 脚本

    #!/bin/sh
    /bin/mount -a                  # 根据 /etc/fstab 挂载所有文件系统
    mkdir /dev/pts                 # 创建伪终端设备目录
    mount -t devpts devpts /dev/pts  # 挂载 devpts 文件系统(支持 SSH 等需要终端的程序)
    echo /sbin/mdev>/proc/sys/kernel/hotplug  # 告诉内核:有热插拔事件时调用 mdev
    mdev -s                        # 扫描 /sys 创建所有设备节点(/dev/ttySAC2 等)
    

    4.根据 /etc/fstab 批量挂载各类文件系统

    device:要挂载的设备;mount-point:挂载点;type:文件系统类型;options:挂载参数;dump程序根据这个字段来决定这个文件系统是否需要备份;fsck order:fsck(file system check)程序根据这个字段来决定是否对磁盘做检查。

    /proc:内核运行信息虚拟文件系统
    /sys:硬件设备树、驱动信息文件系统
    /tmp:临时目录(内存文件系统)
    /dev:设备节点目录

    5.运行 shell 程序

    硬件、文件系统全部就绪后,依据 inittab 配置启动/bin/sh,也就是在串口看到的命令交互终端。

    6. 执行 /etc/profile 脚本

    #!/bin/sh
    export HOSTNAME=farsight        # 设置主机名
    export USER=root
    export HOME=root                # root 用户的家目录
    export PS1="[${USER}@${HOSTNAME} \W]\# "  # 命令提示符格式:[root@farsight ~]#
    PATH=/bin:/sbin:/usr/bin:/usr/sbin       # 命令搜索路径
    LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH  # 库搜索路径
    export PATH LD_LIBRARY_PATH
    

    四个文件之间的关系

    Linux 开机自启程序的四种方案

    方案 启动时机 进程自愈 运行环境 适用场景
    bootargs 指定 init 最早(根文件挂载完立刻运行) 残缺,无系统服务 极简专用固件
    /etc/inittab respawn 系统初始化完成后 崩溃自动重启 完整系统环境 必须永久常驻的后台服务
    rcS 内部调用 系统初始化中期 基础环境就绪 需要提前启动的硬件检测程序
    /etc/profile 调用 所有系统服务、shell 环境就绪 环境最全,变量全部生效 绝大多数普通业务程序(首选)
    Logo

    openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

    更多推荐