内核是不认识dtb文件的,所以在初始化时会将dtb转化成可识别的device_node
cpp
struct device_node {
const char *name; // 节点名称(如 "cpu")
const char *full_name; // 节点全路径(如 "/cpus/cpu@0")
struct property *properties; // 指向该节点的属性链表(如 reg, compatible 等)
struct device_node *parent; // 指向父节点
struct device_node *child; // 指向第一个子节点
struct device_node *sibling; // 指向兄弟节点
// ... 其他内核管理相关的字段(如 phandle, flags 等)
};
device_node 大概长这样

展开过程

1. 核心流程概览
DTB 在内存中是连续的二进制数据,而 device_node 是通过指针相互连接的结构体对象。转换过程主要分为:扫描、内存分配、填充。
阶段一:初始校验与早扫描
在正式转换前,内核会先通过 setup_machine_fdt() 验证 DTB 的合法性(Magic Number、CRC等),并扫描一些紧急信息(如内存基址、命令行参数 chosen 节点)。
阶段二:反扁平化(Unflattening)
这是最核心的一步,主要调用流程如下: start_kernel() -> setup_arch() -> unflatten_device_tree() -> __unflatten_device_tree()。
2. 关键算法与步骤
__unflatten_device_tree() 会对 FDT 字节码进行两次遍历:
第一轮遍历:计算空间
内核首先扫描整个 DTB,计算出转换后所有 device_node 结构体以及属性 property 所需的总内存大小。
-
统计节点个数。
-
统计属性个数及属性值的总长度。
第二轮遍历:解析并填充
在申请到足够的连续内存后,内核再次扫描 DTB,开始构建逻辑树:
-
创建节点 :遇到
OF_DT_BEGIN_NODE标志时,分配并初始化一个struct device_node。 -
建立父子关系 :利用栈的思想,根据 DTB 的嵌套结构,设置节点的
parent、child和sibling指针。 -
解析属性 :遇到
OF_DT_PROP标志时,解析属性名称和值,封装进struct property结构,并挂载到对应节点的properties链表上。 -
生成 Full Name :根据路径生成节点的完整路径名(如
/soc/uart@e0001000)。