uboot,linux内核,dts设备树,rootfs区别和联系
1,关系表
组件 | 作用 | 文件类型 | 运行阶段 |
---|---|---|---|
U-Boot | 引导加载程序,初始化硬件、加载内核和设备树到内存,并启动内核 | 二进制可执行文件(如 u-boot.bin ) | 系统上电 → 内核启动前 |
Linux 内核 | 操作系统核心,管理硬件资源(CPU、内存、外设)、进程调度、驱动等 | 压缩的二进制文件(如 Image 、zImage ) | 内核启动 → 用户空间启动前 |
设备树(DTS) | 描述硬件配置(CPU、外设、中断、寄存器地址等),供内核识别硬件 | 文本文件(.dts ) → 编译为二进制(.dtb ) | 内核启动阶段解析 |
rootfs | 根文件系统,包含用户空间程序(如 /bin 、/lib )、配置文件、应用程序等 | 文件系统镜像(如 ext4 、squashfs 、initramfs ) | 内核启动后挂载,用户空间运行 |
2, 协作关系(启动流程)
1 | 系统上电 → U-Boot → 加载内核和设备树 → 内核启动 → 解析设备树 → 挂载 rootfs → 启动用户空间(如 systemd) |
3,关键步骤详解
- U-Boot 阶段
- 初始化 CPU、内存、存储设备(如 eMMC、SD 卡)、串口等基础硬件。
- 从存储设备(或网络)加载 内核镜像(Image) 和 设备树二进制(.dtb) 到内存。
- 通过
bootm
或bootz
命令启动内核,并传递设备树的内存地址。
- Linux 内核阶段
- 解析设备树(
.dtb
),识别 CPU 架构、外设地址、中断号等硬件信息。 - 初始化驱动(如网卡、存储控制器),挂载 rootfs(从硬盘、网络或内存加载)。
- 启动第一个用户空间进程(如
init
或systemd
)。
- rootfs 阶段
- 提供用户空间程序运行环境(如
bash
、systemd
、应用程序)。 - 包含动态链接库(
/lib
)、配置文件(/etc
)、设备节点(/dev
)等。
rootfs生成脚本(mmdebstrap,chroot)
1 | mmdebstrap --architectures=riscv64 \ |
问题
1 | 这部分代码: |
1. 为什么不报错?—— 内核接口的架构无关性
虚拟文件系统的本质
/proc
、/sys
、dev
等目录是 Linux 内核暴露给用户空间的 虚拟文件系统(Virtual File Systems):/proc
: 提供进程、系统状态等运行时信息(如cpuinfo
)。/sys
: 暴露硬件设备、驱动、内核参数等层级化配置。/dev
: 设备节点文件(如/dev/sda
)。
- 这些文件系统的内容由内核动态生成,与用户空间的 CPU 架构无关。例如:
cat /proc/cpuinfo
在 x86 和 RISC-V 上都会返回当前运行内核的 CPU 信息。/sys/devices
展示的是内核管理的设备树,与用户空间程序架构无关。
- 架构无感知性*
- 挂载操作只是映射内核接口,不涉及任何二进制代码执行。
- 即使宿主架构(x86)和目标架构(RISC-V)不同,内核仍能正确生成这些虚拟文件系统的内容,因为这些内容由宿主机的内核管理。
2. 为什么需要挂载?—— Chroot 环境的完整性
构建根文件系统的需求
在 chroot
环境中执行操作(如安装软件、配置服务)时,需要:
- 设备访问:如挂载
/dev
以便在 chroot 内创建设备节点。 - 系统信息查询:如通过
/proc
获取进程信息。 - 依赖内核功能:如通过
/sys
配置硬件参数。
跨架构构建的特殊性
虽然宿主架构和目标架构不同,但 所有用户空间操作(如
apt install
)实际由宿主机的内核通过二进制翻译(如 QEMU)执行:1
2
3# 宿主机的内核通过 binfmt_misc 识别 RISC-V 二进制
# 并调用 qemu-riscv64-static 解释执行
$ sudo update-binfmts --enable qemu-riscv64挂载宿主机的
/proc
、/sys
到 chroot 环境后:- 用户空间程序(RISC-V)通过 QEMU 翻译执行。
- 内核接口(如 sysfs)仍由宿主机内核提供,但用户空间程序会按 RISC-V 的预期解析这些接口。
3. 潜在问题与规避
架构相关路径的冲突
极少数情况下,内核接口的路径可能因架构不同产生差异,例如:
- 设备树路径:RISC-V 设备树路径
/sys/firmware/devicetree/base
可能与 x86 不同。 - CPU 拓扑信息:
/sys/devices/system/cpu
的结构可能因架构而异。
解决方案
- 动态生成配置:在 chroot 环境内通过脚本动态生成架构相关配置。
- 避免硬编码路径:使用抽象接口(如
lscpu
替代直接读取/proc/cpuinfo
)。 - 隔离关键目录:在构建完成后卸载宿主机的虚拟文件系统,确保目标系统首次启动时重新挂载自身的虚拟文件系统。
4. 操作的目的总结
挂载操作 | 目的 | ||
---|---|---|---|
mount -t proc /proc ... | 允许 chroot 内程序查询进程和系统信息(如 ps 命令) | ||
mount --bind /sys ... | 提供硬件设备配置接口(如安装驱动时访问 /sys/class ) | ||
mount --bind /dev ... | 创建设备节点(如 /dev/null , /dev/console ) | ||
mount --bind /dev/pts ... | 支持伪终端操作(如 SSH 登录需要 /dev/pts/0 ) | ||
### 5. 完整工作流程示例 | |||
| |||
### 结论 | |||
* 安全且必要:跨架构挂载虚拟文件系统是安全的,因为这些接口由宿主机的内核统一管理,与用户空间架构无关。 | |||
* 依赖 QEMU:实际执行 RISC-V 二进制需要 QEMU 的二进制翻译支持,但挂载操作本身不直接涉及指令集转换。 | |||
* 最佳实践:这种方法是构建跨架构根文件系统的标准操作,广泛用于嵌入式 Linux 开发和容器化构建(如 Docker 多架构构建)。 | |||
## uboot常用命令 | |||
### 1、信息查询命令 | |||
常用的信息查询有关的命令有三个:bdinfo、printenv、version。 |
命令 | 描述 |
---|---|
bdinfo | 查看板子信息 |
printenv | 输出环境变量信息 |
version | 查看uboot的版本号 |
2、环境变量操作命令
环境变量的操作涉及到两个命令:setenv和saveenv。
setenv命令用于创建、修改环境变量的值,也可以用于删除环境变量;
saveenv命令用于保存修改后的环境变量。
一般环境变量是存放在外部flash中的,uboot启动的时候会将环境变量从flash读取到DRAM中,所以使用命令setenv修改的是DRAM中的环境变量值,修改后需使用saveenv命令将环境变量保存到flash中。
场景:误修改 bootcmd 导致无法启动,需恢复默认环境。
1 | # 进入 U-Boot 命令行 |
3、内存操作命令
内存操作命令就是用于直接对 DRAM 进行读写操作的,常用的内存操作命令有 md、 nm、mm、 mw、 cp 和 cmp。
4、网络操作命令
uboot是支持网口的,在移植uboot的时候都要调通网络的功能,因为在移植linux内核的时候需要用到uboot的网络功能做调试。uboot支持的网络相关的命令有:dhcp、ping、nfs、ftfpboot。
使用网络调试前需先设置好下列几个环境变量。
环境变量 | 描述 | ||
---|---|---|---|
ipaddr | 开发板的IP地址,可以通过dhcp命令从路由器获取IP | ||
ethaddr | 开发板的MAC地址,一定要设置 | ||
gatewayip | 网关地址 | ||
netmask | 子网掩码 | ||
serverip | 服务器IP地址,也就是ubuntu主机的IP地址,用于调试 | ||
| |||
![]() |
5、EMMC和SD卡操作命令
uboot支持EMMC和SD卡,提供了EMMC和SD卡的操作命令。一般认为EMMC和SD卡就是同一个东西,所以没有特殊说明,统一使用MMC来代指EMMC和SD卡。uboot中常用于操作MMC设备的命令为:mmc。
mmc是一系列的命令,后面可以跟不同的参数,输入“? mmc”即可查看mmc有关的命令。如下图所示:
mmc后面跟不同的参数可以实现不同的功能。
命令 | 描述 |
---|---|
mmc info | 输出MMC设备信息 |
mmc read | 读取MMC中的数据 |
mmc write | 向MMC设备写入数据 |
mmc rescan | 扫描MMC设备 |
mmc part | 列出MMC设备的分区 |
mmc dev | 切换MMC设备 |
mmc list | 列出当前有效的所有MMC设备 |
mmc hwpartition | 设置MMC设备的分区 |
mmc bootbus … | 设置指定MMC设备的BOOT_BUS_WIDTH域的值 |
mmc bootpart … | 设置指定MMC设备的boot和RPMB分区的大小 |
mmc partconf … | 设置指定MMC设备的PARTITION_CONFG域的值 |
mmc rst | 复位MMC设备 |
mmc setdsr | 设置DSR寄存器的值 |
mmc rescan 命令
mmc rescan 命令用于扫描当前开发板上所有的 MMC 设备,包括 EMMC 和 SD 卡,输入“mmc rescan”即可。
mmc list 命令
mmc list 命令用于来查看当前开发板一共有几个 MMC 设备,输入“mmc list”
可以看出当前开发板有两个 MMC 设备: FSL_SDHC:0 (SD)和 FSL_SDHC:1 (eMMC),这是因为我现在用的是 EMMC 版本的核心板,加上 SD 卡一共有两个 MMC 设备, FSL_SDHC:0 是 SD卡, FSL_SDHC:1(eMMC)是 EMMC,。默认会将 EMMC 设置为当前 MMC 设备,要想查看 EMMC信息,就要使用命令“mmc dev”来将 EMMC卡设置为当前的 MMC 设备
mmc dev 命令
mmc dev 命令用于切换当前 MMC 设备,使用如下命令切换到 EMMC:
mmc info 命令
mmc info 命令用于输出当前选中的 mmc info 设备的信息,输入命令“mmc info”即可,如下图所示:
从上图可以看出,当前选中的 MMC设备是 SD卡,版本为 3.0,容量为 14.8GiB(EMMC为 4GB),速度为 50000000Hz=50MHz, 4 位宽的总线。还有一个与 mmc info 命令相同功能的命令: mmcinfo,“mmc”和“info”之间没有空格。
mmc part 命令
有时候 SD 卡或者 EMMC 会有多个分区,可以使用命令“mmc part”来查看其分区,比如查看 EMMC 的分区情况,输入如下命令,结果如下图所示:
从上图中可以看出,此时 EMMC 有两个分区,扇区 20480~ 262144 为第一个分区,扇区 282644~14987264 为第二个分区。如果 EMMC 里面烧写了 Linux 系统的话, EMMC 是有3 个分区的,第 0 个分区存放 uboot,第 1 个分区存放 Linux 镜像文件和设备树,第 2 个分区存放根文件系统。但是在图中只有两个分区,那是因为第 0 个分区没有格式化,所以识别不出来,实际上第 0 个分区是存在的。一个新的 SD 卡默认只有一个分区,那就是分区 0.
参考:
u-boot常用命令:https://blog.csdn.net/helaisun/article/details/128166820
史上最全的Uboot常用命令汇总(超全面!超详细!)收藏这一篇就够了「建议收藏」:https://cloud.tencent.cn/developer/article/2102295
U-Boot命令使用:https://blog.csdn.net/cike626/article/details/128430824
U-Boot命令之EMMC和SD卡操作命令:https://blog.csdn.net/weixin_45309916/article/details/109178989
uboot环境变量解析
样例信息
1 | > printenv |
4大部分
1 | 1. 硬件初始化 |
1. 系统基础信息
变量 | 值 | 说明 |
---|---|---|
arch | riscv | 系统架构为 RISC-V |
board | eic7700_d314 | 硬件板型号(与设备树文件 eic7700-d314.dtb 匹配) |
vendor | eswin | 硬件厂商标识 |
cpu | eic770x | CPU 型号(可能为定制 SoC) |
ram_size | 16 | 系统内存容量为 16GB |
2. 启动流程控制
变量 | 值 | 说明 |
---|---|---|
bootcmd | sysboot mmc ${emmc_dev}:1 any $boot_conf_addr_r $boot_conf_file; | 默认启动命令: - 从 eMMC 的第 1 分区加载 extlinux 配置 ( /extlinux/extlinux.conf )- 使用 sysboot 执行配置的启动项 |
bootdelay | 2 | 启动等待时间为 2 秒(用户可按任意键中断自动启动) |
boot_conf_file | /extlinux/extlinux.conf | U-Boot 启动菜单配置文件路径 |
bootargs | root=/dev/nfs ... console-ttyS0 | 内核启动参数: - 通过 NFS 挂载根文件系统 ( 10.10.192.205:/srv/nfs_server )- 使用串口 ttyS0 作为控制台 |
3. 存储与分区
变量 | 值 | 说明 |
---|---|---|
partitions | name=boot,start=1MiB,size=512MiB... | GPT 分区定义: - boot 分区:512MB,EFI 类型 (FAT32)- swap 分区:4GB- root 分区:剩余空间(ext4) |
gpt_partition | gpt write mmc ${emmc_dev} $partitions | 命令:将 partitions 变量定义的分区表写入 eMMC |
emmc_dev | 0 | 当前操作的 eMMC 设备编号(对应硬件上的存储控制器) |
uuid_boot | 44b7cb94-f58c-4ba6-bfa4-7d2dce09a3a5 | Boot 分区的 UUID(与 fstab 中的配置匹配) |
4. 网络配置
变量 | 值 | 说明 |
---|---|---|
ethact | ethernet@50400000 | 当前激活的以太网设备(硬件寄存器地址 0x50400000 ) |
ethaddr | 8c:1f:64:13:c0:9c | MAC 地址(需确保与网络环境无冲突) |
serverip | 10.10.192.205 | TFTP/NFS 服务器 IP(用于网络启动或文件传输) |
nfsboot | run nfsargs; run loadimage | NFS 启动命令: 1. 设置 NFS 参数 ( nfsargs )2. 加载内核镜像 ( loadimage ) |
5. 内核与设备树加载 |
变量 | 值 | 说明 |
---|---|---|
kernel_addr_r | 0x84000000 | 内核镜像 (fitImage ) 加载到内存的地址 |
fdt_addr_r | 0x88000000 | 设备树文件 (eic7700-d314.dtb ) 加载地址 |
fdtfile | eswin/eic7700-d314.dtb | 设备树文件路径(需与硬件板型号匹配) |
loadimage | dhcp 0x90000000 fitImage | 从 TFTP 服务器下载内核镜像到内存 0x90000000 |
6. 高级功能
变量 | 值 | 说明 |
---|---|---|
sdupdate | ext4load mmc 1:1 0x90000000 sdupdate.scr; source 0x90000000 | SD 卡更新脚本: - 从 SD 卡加载 sdupdate.scr 脚本并执行 |
usbupdate | ext4load usb 0 0x90000000 usbupdate.scr; source 0x90000000 | USB 更新脚本: - 从 USB 设备加载 usbupdate.scr 并执行 |
preboot | sata init; nvme scan | 启动前初始化 SATA/NVMe 设备(扩展存储支持) |