设备树根文件系统
335.3.8 设备树与根文件系统
/* 文件一:topeet_emmc_hdmi.dts(视频实际查看的 HDMI 版本入口文件) */
#include "imx6ull-14x14-evk-emmc.dts"
// ↑ 引入 eMMC 版开发板的设备树;本文件再补充 HDMI 相关配置。
/* 文件二:imx6ull-14x14-evk-emmc.dts(视频实际沿 include 继续追踪的文件) */
#include "imx6ull-14x14-evk.dts"
// ↑ 引入通用的 i.MX6ULL 14×14 EVK 板级设备树,再叠加 eMMC 配置。
/* 文件三:imx6ull-14x14-evk.dts(视频实际查看的板级文件) */
#include <dt-bindings/input/input.h>
// ↑ 引入输入子系统会用到的设备树常量定义。
#include "imx6ull.dtsi"
// ↑ 引入 i.MX6ULL SoC 的公共硬件描述;板级文件在此基础上补充或覆盖属性。
/* 文件四:imx6ull.dtsi(视频实际分析的 I²C1 控制器节点) */
i2c1: i2c@021a0000 {
// ↑ 定义标签 i2c1;节点名中的 021a0000 是控制器寄存器起始物理地址。
#address-cells = <1>;
// ↑ I²C 子设备的 reg 地址使用一个 32 位 cell,通常用于保存从设备地址。
#size-cells = <0>;
// ↑ I²C 子设备没有内存映射长度,所以其 reg 不需要 size cell。
compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
// ↑ 给出由具体到通用的兼容字符串,供内核选择可支持该控制器的驱动。
reg = <0x021a0000 0x4000>;
// ↑ 寄存器窗口从 0x021A0000 开始,长度为 0x4000 字节,即 16 KiB。
interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
// ↑ 描述该控制器连接到 GIC 的 SPI 36 号中断,并采用高电平触发。
clocks = <&clks IMX6UL_CLK_I2C1>;
// ↑ 引用时钟控制器中的 I²C1 时钟资源;它不是 100 kHz 的总线速率设置。
status = "disabled";
// ↑ SoC 公共层默认关闭控制器,具体开发板确认接线后再启用。
};
// ↑ I²C1 控制器的 SoC 级基础描述结束。
/* imx6ull-14x14-evk.dts 中对既有 I²C1 节点的补充 */
&i2c1 {
// ↑ 通过标签引用前面已经定义的 i2c1 节点,不是重新创建第二个控制器。
clock-frequency = <100000>;
// ↑ 把 I²C SCL 目标频率设为 100000 Hz,即 100 kHz 标准模式。
pinctrl-names = "default";
// ↑ 声明本设备使用名为 default 的默认引脚状态。
pinctrl-0 = <&pinctrl_i2c1>;
// ↑ default 状态引用 pinctrl_i2c1 引脚组。
status = "okay";
// ↑ 板级文件确认 I²C1 可用,覆盖 SoC 层的 disabled。
};
// ↑ 本节摘录的 I²C1 板级补充结束;视频未在此处编写新的 C 驱动代码。
/* imx6ull-14x14-evk.dts 中视频实际查看的 I²C1 引脚组 */
pinctrl_i2c1: i2c1grp {
// ↑ 定义可由 pinctrl-0 引用的 I²C1 引脚复用配置组。
fsl,pins = <
// ↑ 下列条目同时指定焊盘复用功能和该平台使用的电气配置值。
MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
// ↑ 把 UART4_TX_DATA 焊盘复用成 I²C1 的 SCL 时钟线。
MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
// ↑ 把 UART4_RX_DATA 焊盘复用成 I²C1 的 SDA 数据线。
>;
// ↑ 引脚条目列表结束;0x4001b8b0 是平台相关配置值,不是外设地址。
};
// ↑ I²C1 引脚组定义结束。
顶部代码只收录了本节视频实际定位、讲解的设备树片段,并按初学者视角逐行增加注释。它们分别属于四个文件,不能直接拼成一个可编译的 .dts;视频没有新写 C 语言驱动或应用程序,因此不能提前补入后续 MPU6050、OLED 的驱动代码。
本节建立两条互相配合、但不能混为一谈的启动信息链:
DTS/DTSI → 预处理与 DTC → DTB → 引导程序传给内核 → 内核据此创建设备并尝试匹配驱动
存储设备上的根文件系统 → 内核挂载到 / → 执行 init → 启动服务与用户应用
设备树回答“这块板上有什么硬件、资源如何连接”;根文件系统提供“系统启动后要运行的用户空间”。设备树不是驱动,根文件系统也不是设备树源码所在目录。
一、设备树和根文件系统
1.1 课前补充与设备树基础
视频开头先补充了 Source Insight 工程的打开方法。此前从 Linux 虚拟机复制到 Windows D 盘的内核源码已经建立为 kernel_class 工程;进入工程目录后,双击带 Source Insight 图标的 kernel_class.siproj 即可重新打开工程。它是工程描述文件,不只是普通的“快捷方式”,也不包含另一份内核源码;移动源码目录后若路径失效,需要在工程中重新同步文件。
1.1.1 设备树的功能与使用
设备树(Devicetree,DT)是一种描述硬件的数据结构。Linux 官方文档把它概括为“操作系统可读取的硬件描述”,其目的之一是避免把每块板子的硬件细节都硬编码进内核。它通常描述:
- SoC 内部控制器:I²C、UART、GPIO、PWM、ADC、LCDIF 等;
- 板级连接:某控制器用了哪些引脚、挂接了哪些外设、外设地址和中断是什么;
- 启动配置:可用内存、串口控制台、部分保留内存等;
- 设备状态及资源:
status、寄存器范围、中断、时钟、复位、DMA、引脚复用等。
“设备树包含硬件信息”不等于“设备树能替代原理图或芯片手册”。设备树只描述软件需要知道且符合绑定规范的信息;供电、电平、真实焊接、上拉电阻和机械连接仍要查原理图与器件资料。Linux 对设备树的三类主要用途是平台识别、运行期配置和设备创建,可参阅 Linux 官方设备树使用模型。
1.1.2 驱动加载流程
原笔记“系统驱动匹配设备树,成功后加载驱动”的说法过于简化。更准确的流程是:
- 引导程序把与当前板卡匹配的 DTB 地址传给内核。
- 内核解析 DTB,根据有效节点创建相应的设备对象;I²C 子节点还依赖其 I²C 控制器先成功注册。
- 总线或驱动核心比较设备信息和驱动支持表。对设备树设备,
compatible通常会与驱动的 OF 匹配表比较。 - 匹配后调用驱动的
probe();只有probe()成功,设备与驱动才真正绑定。 - 驱动若编进内核可直接参与匹配;若编成模块,还要保证
.ko已安装、版本兼容,并由用户空间按 modalias 自动加载或手动加载。 - 驱动建立设备接口后,应用程序才能通过
/dev、sysfs、IIO、input、framebuffer/DRM 等对应接口使用硬件。
所以,修改设备树只能告诉内核“硬件怎样描述并是否允许使用”,不能凭空产生驱动,也不能保证模块一定存在、硬件一定接对或 probe() 一定成功。驱动绑定的通用过程可参考 Linux 官方 Driver Binding 文档。
视频把 MPU6050 和 OLED 作为后续例子:先补充设备树节点,再让对应驱动匹配,最后由应用读取运动数据或显示内容。本节只提出“驱动怎样读取设备树属性”这一问题,实际 API 和驱动代码留到后续课程,不能在这里自行补写。
1.1.3 设备树相关概念
| 名称 | 准确含义 | 本节中的作用 |
|---|---|---|
| DTS | Devicetree Source,设备树的文本源表示;常以 .dts 作为最终板级入口 | 描述当前板卡并包含公共文件 |
| DTSI | 常用的设备树包含文件,扩展名通常为 .dtsi | 复用 SoC、芯片系列或公共板级描述 |
| DTC | Devicetree Compiler,设备树编译器 | 在 DTS、DTB 等表示之间转换并执行检查 |
| DTB | Devicetree Blob,扁平化的二进制设备树 | 启动时由引导程序提供给内核 |
需要纠正两个常见误区:
- DTS 中的字母
S是 Source/Syntax 语境,不是转录中的 “sauce”;DTB 的B指 Blob,不宜简单写成 binary。 - “DTS 是板级、DTSI 是 SoC 级”只是本课程源码的常见组织方式,不是语法强制规则。
.dtsi的本质是可复用的包含文件,也可以承载多个板卡共享的板级片段。
Devicetree Specification给出的术语定义中,DTS 是 DTC 消费的文本表示,DTB 是紧凑二进制表示,DTC 是生成 DTB 的开源工具。
1.1.4 设备树源码查看
本课程 Linux 4.1.15 厂商内核的 32 位 ARM 设备树目录是:
arch/arm/boot/dts/
视频用 Windows 映射目录并右键通过 VS Code 打开该文件夹。也可在 Linux 终端使用 less、vim 或 VS Code Remote 打开;工具不同不改变源码含义。
.dtb 是二进制文件,不适合当普通文本直接编辑。真正修改应从对应的 .dts/.dtsi 源文件开始,重新编译并部署新的 DTB。目录里同时出现 DTS、DTSI 和已构建 DTB,是因为课程源码树保留了部分构建产物,并不表示 DTC 本身是一个同目录的 .dtc 文件。
1.2 设备树文件的分层(原笔记误写为“根文件系统”)
视频从烧录时实际选择的 HDMI 版 topeet_emmc_hdmi.dtb 反查源文件,得到以下包含链:
topeet_emmc_hdmi.dts
└── imx6ull-14x14-evk-emmc.dts
└── imx6ull-14x14-evk.dts
└── imx6ull.dtsi
topeet_emmc_hdmi.dts:课程板卡的 HDMI 显示入口,增加 SII902x、LCDIF 和 HDMI 引脚等配置。imx6ull-14x14-evk-emmc.dts:eMMC 版本的板级增量配置。imx6ull-14x14-evk.dts:通用 EVK 板级描述,补充内存、外设启用和引脚复用等信息。imx6ull.dtsi:i.MX6ULL SoC 的公共控制器、地址、中断和时钟等基础描述。
这条链是源码的包含关系,不是“根文件系统的层级”。/ { ... }; 是设备树根节点,与 Linux 挂载在 / 的根文件系统只是都使用斜杠符号,概念完全不同。
课程文件使用 #include,说明内核构建流程先经过 C 预处理器,再交给 DTC;设备树规范本身也定义了 /include/ "FILE" 形式。无论是哪一种写法,包含文件都能继续包含其他文件,详见 DTS 源格式说明。
1.3 设备树功能与 I²C1 实例
1.3.1 用 VS Code 打开设备树文件夹
视频的正确操作顺序是:
- 定位内核源码的
arch/arm/boot/dts。 - 用 VS Code 打开整个
dts文件夹,以便在左侧文件树中追踪包含关系。 - 从烧录使用的
topeet_emmc_hdmi.dtb找到同名源入口topeet_emmc_hdmi.dts。
“烧写过多个 DTB”不表示它们会同时生效。引导程序一次启动通常只把选中的一个最终 DTB 交给内核;必须确认启动配置选择的文件与屏幕类型、存储介质和板卡版本一致。
1.3.2 找到 topeet_emmc_hdmi.dts
原笔记中的 “top EMS HDMI” 是识别错误,视频文件名清楚显示为:
topeet_emmc_hdmi.dts
其第一行包含 imx6ull-14x14-evk-emmc.dts。与其靠滚动列表从字母 t 开始寻找,更可靠的方法是在 VS Code 中按 Ctrl+P 输入完整文件名,或在终端执行:
find arch/arm/boot/dts -name 'topeet_emmc_hdmi.dts'
1.3.3 沿包含关系查找设备树
课程依次打开了四个文件。越接近入口的文件越偏向具体板卡差异;越接近 imx6ull.dtsi,越偏向 SoC 公共资源。最终树不是简单地用文本顺序“拼接四个文件”:预处理器会展开包含和宏,DTC 还会解析标签引用、合并对同一节点的补充,并生成 phandle 等二进制关系。
视频在 imx6ull-14x14-evk.dts 中看到约 1100 行以及 ADC、UART、PWM、LCDIF、pinctrl 等节点。这些控制器概念与单片机知识相通,但 Linux 设备树重点是描述资源与连接,不是在节点内编写寄存器驱动流程。
1.3.4 根节点解析
设备树根节点写作:
/ {
/* 根节点的属性与子节点 */
};
每个节点除根节点外都只有一个父节点,形成树状层级。根节点可包含 model、compatible、chosen、memory 等内容。它并不代表“最开始的物理地址”,也不能称为根文件系统;它只是设备树数据结构的顶层节点。
1.3.5 SoC 公共文件解析
视频把 imx6ull.dtsi 说成“最顶层文件”,容易产生反向理解。就文件复用层次看,它是包含链中较底层、较通用的 SoC 描述;就展开后的设备树结构看,里面仍然通过 / 根节点向最终树添加 SoC 总线和控制器节点。
imx6ull.dtsi 中的 I²C、UART、ADC 等节点给出 SoC 固有资源,而板级 DTS 再决定哪些控制器真正连接到板上接口、使用哪些引脚以及是否启用。这种“公共定义默认关闭,板级按需开启”的模式有利于复用同一 SoC 描述。
1.3.6 查阅 i.MX6ULL 参考手册
视频用 I²C1 演示如何把设备树与芯片手册对照。正确节点名与核心属性是:
i2c1: i2c@021a0000 {
reg = <0x021a0000 0x4000>;
};
查阅的是 NXP/Freescale 的 i.MX 6ULL Applications Processor Reference Manual。设备树不能代替参考手册:节点只给软件描述,手册才解释寄存器布局、时钟、复位和功能细节。官方资料入口见 NXP i.MX 6ULL 产品文档页。
1.3.7 在参考手册中核对地址
视频先在 PDF 中搜索完整的 021a0000,未直接命中,随后缩短为 021a,在 memory map 表中找到 I²C1。搜索失败不代表地址不存在,常见原因包括 PDF 字体编码、表格被拆分、是否带 0x、大小写或前导零不同。
正确核对结果是:
- I²C1 起始物理地址:
0x021A0000; - I²C1 地址范围:
0x021A0000~0x021A3FFF; - 下一控制器 I²C2 从
0x021A4000开始。
原笔记把 I²C 识别为 FMC/FNC,把 0x021A0000 写成 0x021A4000,均已纠正。
1.3.8 reg 属性解析
在当前父总线规定的地址、长度 cell 数量下:
reg = <0x021a0000 0x4000>;
可读作“起始地址 + 地址空间长度”:
0x021A0000是 I²C1 控制器寄存器窗口的基地址;0x4000是窗口长度,不是偏移地址,也不是 I²C 从设备地址;0x4000 = 16384字节,即 16 KiB(课程口头简称 16 KB)。
由长度可得最后一个字节地址为 0x021A0000 + 0x4000 - 1 = 0x021A3FFF,恰好与参考手册的地址范围一致。Devicetree Specification 的标准属性说明规定,reg 的编码要由父节点的 #address-cells 和 #size-cells 解释,不能脱离父节点机械地把每个数字都当地址。
1.3.9 计算器换算
视频在 Windows 程序员计算器中输入的是十六进制 4000,界面显示十进制 16384,再除以 1024 得到 16。原转录及原笔记中的 0x430、0x43000 都与画面不符。
换算过程:
0x4000 = 4 × 16³ = 16384 B
16384 B ÷ 1024 B/KiB = 16 KiB
1.3.10 继续解析板级 DTS
SoC 公共文件先用标签 i2c1: 定义节点;板级文件再写 &i2c1 { ... };,表示对同一个节点追加或覆盖属性。&i2c1 不是面向对象意义上的“继承”,也不是再实例化一份 I²C1。
本节几个属性的作用如下:
| 属性 | 作用 | 易错点 |
|---|---|---|
clock-frequency = <100000> | 请求 I²C 总线 SCL 工作在 100 kHz | 不是控制器输入时钟,也不是 clocks 引用 |
pinctrl-names = "default" | 声明默认引脚状态名称 | 字符串要与对应索引一致 |
pinctrl-0 = <&pinctrl_i2c1> | 默认状态选用该引脚组 | 引用的是标签生成的 phandle |
status = "okay" | 板级确认该控制器可用 | 只启用节点,不保证驱动一定成功 |
在 pinctrl 组中,原本名为 UART4_TX/RX 的两个物理焊盘被复用为 I²C1_SCL/SDA。SoC 的一个焊盘往往能承担多种外设功能,但同一时刻只能按所选复用方式工作;必须同时核对原理图,避免和 UART4 或其他功能冲突。
1.3.11 I²C 接口补充
视频回顾了 I²C 的速率、起止条件、读写时序、硬件/软件 I²C 和上拉电阻。对初学者应先抓住以下边界:
- SDA 是双向数据线,SCL 是时钟线;常规 I²C 使用开漏/开集电极方式,只主动拉低,总线高电平由上拉器件形成。
- 空闲时 SDA、SCL 都为高;SCL 为高时 SDA 从高到低是 START,从低到高是 STOP。
- 一次典型写传输可概括为
START → 7 位从地址 + 写位 → ACK → 数据字节 → ACK → STOP;读传输把方向位改为读,并由接收方在最后一个字节后用 NACK 表示结束。寄存器读取还常使用重复 START,实际顺序要以器件手册为准。 - 每传完 8 位数据通常还会有第 9 个时钟供接收方返回 ACK/NACK;所以协议中的确认位、地址和起止条件都会占用总线时间。
- 常见模式上限为 Standard-mode 100 kbit/s、Fast-mode 400 kbit/s、Fast-mode Plus 1 Mbit/s、High-speed mode 3.4 Mbit/s;Ultra Fast-mode 5 Mbit/s 是单向扩展,不能简单当作普通双向 I²C 的更高速档位。
clock-frequency = <100000>的单位是 Hz。它表示 100 kHz SCL;实际有效数据吞吐还要扣除地址、读写位、ACK/NACK 等协议开销,不能直接等同于每秒传输 100 kB。- “硬件 I²C”由控制器外设产生时序;“软件 I²C”由 GPIO 程序模拟时序。两者都必须满足电气和时序要求。
- 上拉阻值不能只凭经验固定选取,要结合电源电压、总线电容、允许上升时间、器件灌电流和速率计算,并核对模块上是否已经带上拉。
精确速率和电气条件应以 NXP《I²C-bus specification and user manual》UM10204 为准。
1.4 编译与反编译
设备树正常构建过程是:源文件经过预处理和 DTC 检查,生成最终 DTB。内核构建树常用 make dtbs 构建设备树集合,也可构建指定 DTB;具体目标要看该厂商内核的 Makefile。不能把所有环境都写成不存在的通用命令 make dtb。
反编译是让 DTC 读取 DTB,再输出可读的 DTS 表示。它适合检查真正准备部署或已经部署的 DTB 中最终有什么,尤其适合验证某节点、属性或外设是否进入最终结果。
反编译不会还原原始工程:注释、宏、#include 边界、源文件名、部分标签和原始排版都可能丢失或改变。因此它不能代替维护原始 DTS/DTSI,也不能仅凭反编译文本精确追溯某一属性来自哪一个源文件。
二、设备树文件
2.1 反编译
视频在 arch/arm/boot/dts 目录实际使用的命令是:
dtc -I dtb -O dts -o test001.dts topeet_emmc_hdmi.dtb
逐项解释:
| 参数 | 含义 |
|---|---|
dtc | 启动设备树编译器 |
-I dtb | 指定输入格式为 DTB |
-O dts | 指定输出格式为 DTS 文本 |
-o test001.dts | 指定输出文件名 |
topeet_emmc_hdmi.dtb | 输入的已编译设备树 |
命令通式是 dtc -I <输入格式> -O <输出格式> -o <输出文件> <输入文件>,可由 Linux 内核文档中的 DTC 说明核对。
视频终端出现的是多条 Warning,不是原笔记所说的“好多错误”。DTC 随后返回提示符并生成了 test001.dts,说明本次转换完成;但“生成了文件”不等于警告可以长期忽略。画面中的 simple_bus_reg、spi_bus_bridge 等警告提示节点命名或总线结构与检查规则不一致,维护设备树时应定位根因,并用退出码及构建日志判断是否成功:
echo $?
test -s test001.dts && echo 'output exists and is not empty'
ls test* 只是筛选当前目录中名字以 test 开头的文件,不验证文件内容正确。更稳妥的验证还包括重新编译反编译结果、执行设备树 schema 检查(若该旧内核工程具备条件),以及在目标板读取 /proc/device-tree 或 /sys/firmware/devicetree/base 对照运行时数据。
2.2 反编译后的文件
视频生成的 test001.dts 有约 1805 行。它把入口 DTS 及其包含文件形成的最终有效树放到一个文本文件中,因而搜索 I²C1 时能同时看到 SoC 层的地址、中断、时钟资源和板级追加的 clock-frequency、pinctrl、status,也能看到最终编入的 I²C 子设备。
反编译在排障时回答的是:
- 预期节点是否确实进入目标 DTB;
- 属性最终值是什么,是否被后面的板级定义覆盖;
status是否启用;compatible、reg、中断、时钟和 pinctrl 引用是否存在;- 实际启动加载的 DTB 是否可能拿错版本。
它不能单独证明驱动已经绑定。还应在目标板结合启动日志、dmesg、/sys/bus/.../devices、驱动模块状态和硬件测量继续判断。
三、根文件系统
3.1 根文件系统是什么
根文件系统(root filesystem)是内核挂载到 / 的文件系统,是其他文件系统和用户空间目录树的起点。它不仅是 bin、dev、etc 等“很多目录”,还必须为目标系统提供启动用户空间所需的程序、库、配置、设备访问入口和挂载点。
一个简化的嵌入式 Linux 启动链是:
Boot ROM → SPL/U-Boot → Linux 内核 + DTB → 挂载根文件系统 → 执行 init → 启动服务/应用
内核镜像、DTB 和根文件系统是三个不同产物:
| 产物 | 主要内容 | 启动阶段作用 |
|---|---|---|
| 内核镜像 | 内核代码和内建驱动 | 初始化 CPU、内存、调度、驱动和文件系统等 |
| DTB | 当前板卡的硬件描述 | 向内核提供平台与设备资源信息 |
| 根文件系统 | 用户空间程序、库、配置及目录树 | 提供 init、Shell、命令和应用运行环境 |
根文件系统可以存放在 eMMC、NAND、SD 卡、网络 NFS 或 initramfs 中,也可以采用 ext4、SquashFS、UBIFS 等不同格式;“根”描述它挂载后处于 /,不是限定某一种镜像格式。
常见目录的入门理解如下:
| 目录 | 常见作用 | 必须注意 |
|---|---|---|
/bin | 基础用户命令;嵌入式 BusyBox 系统中常是指向 BusyBox applet 的链接 | 现代发行版可能合并到 /usr/bin,不能假设一定是独立分区 |
/sbin | 系统管理和启动相关程序 | 普通用户的 PATH 可能不含该目录 |
/lib | 动态链接器、共享库及部分模块入口 | 动态程序缺少对应架构的加载器/库会无法启动 |
/etc | 系统配置和启动脚本 | 内容依 init 系统和发行方案而变 |
/dev | 字符/块设备节点及伪终端等 | 常由 devtmpfs 加 mdev/udev 动态维护,不只是普通静态文件 |
/proc | 进程与内核运行信息的伪文件系统 | 数据由内核运行时生成,不应打包成普通持久文件 |
/sys | 内核对象、设备、驱动和属性的 sysfs | 常用于观察设备与驱动绑定状态 |
/tmp | 临时文件 | 常挂载为 tmpfs,重启后可能消失 |
/usr、/var | 程序/共享资源与可变数据 | 精简系统可按需求裁剪,但要满足依赖 |
3.1.1 根文件系统目录与命令执行
视频在 Ubuntu 虚拟机中执行了:
cd /
cd bin
ls
./ls
这里演示的是虚拟机自己的根文件系统,不是已经制作出的开发板根文件系统。绿色只是当前终端配色对可执行文件的显示,也不能单靠颜色判断文件格式或目标架构。
./ls 中:
.表示当前目录;/是路径分隔符;- 因此它要求当前目录下确实存在名为
ls且可执行的文件。
离开 /bin 后再输入 ./ls,Shell 会查找“新当前目录下的 ls”,不存在时就报告 No such file or directory。若要从任意目录明确执行该文件,应写绝对路径 /bin/ls;直接输入 ls 时,Shell 会按 PATH 环境变量列出的目录依次搜索,并不是“自动回到 /bin”。可用以下命令观察实际解析结果:
type -a ls
command -v ls
printf '%s\n' "$PATH"
readlink -f "$(command -v ls)"
在 BusyBox 根文件系统中,ls、cp、mv 等命令常是指向同一个 BusyBox 可执行文件的软链接,由 BusyBox 根据调用名选择 applet;而完整 Ubuntu 通常使用 coreutils 等独立程序。两种环境不能机械类比文件数量和布局。
3.1.2 根文件系统制作
视频只打开网页概览了制作流程,没有在本节实际制作或验证根文件系统。页面涉及的典型工作包括:
- 为目标架构交叉编译并安装 BusyBox,生成基础命令及链接;
- 准备匹配目标 ABI 的动态链接器和共享库,或明确选择静态链接;
- 建立
/etc、/dev、/proc、/sys、/tmp等目录与配置; - 配置 init、控制台、挂载脚本和应用启动顺序;
- 处理设备节点、权限、用户与安全设置;
- 打包成目标存储介质支持的文件系统镜像,部署后进行启动验收。
视频特别指出 /etc/init.d/rcS。在课程网页展示的 BusyBox inittab 配置中,::sysinit:/etc/init.d/rcS 让 BusyBox init 在系统初始化阶段运行该脚本。它常用于挂载 /proc、/sys、准备 /dev、设置网络和启动服务,但并非所有 Linux 系统都使用它:systemd、其他 init 方案或定制 initramfs 有不同入口;rc.local 也不是 rcS 的同义词。Buildroot 官方手册同样说明,其 BusyBox init 默认配置会启动 /etc/init.d/rcS,并提醒 BusyBox 的 inittab 语法具有自身规则。
本节应形成的最终认识是:
- 从
topeet_emmc_hdmi.dts沿包含链可以追到板级与 SoC 公共描述。 imx6ull.dtsi定义 I²C1 固有资源,板级 DTS 用&i2c1配置频率、引脚并启用它。reg = <0x021a0000 0x4000>表示 0x021A0000 起始、16 KiB 长度的寄存器窗口。- 反编译可核对最终 DTB,但不能还原原始源码结构,也不能证明驱动已经正常工作。
- 根文件系统提供用户空间;设备树描述硬件。二者与内核镜像共同构成嵌入式 Linux 启动链中的不同环节。