当前位置: 首页
编程语言
c++如何读取Linux内核生成的Device Tree二进制流【深度】

c++如何读取Linux内核生成的Device Tree二进制流【深度】

热心网友 时间:2026-05-06
转载

C++如何读取Linux内核生成的Device Tree二进制流【深度】

c++如何读取Linux内核生成的Device Tree二进制流【深度】

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈

Linux用户态如何解析内核加载的dtb文件

Linux内核在启动过程中会加载并解析dtb(设备树二进制)文件,将其转换为内部数据结构(如struct device_node)。一个关键限制是:**用户态程序无法直接访问内核内存中的这些数据**。因此,若要在用户空间获取设备树信息,必须采用替代方案。主要有两种途径:一是直接读取启动时传入的原始dtb二进制文件;二是利用内核提供的标准接口——/proc/device-tree伪文件系统。

常见的错误做法是尝试使用mmap映射/sys/firmware/devicetree/base/proc/device-tree下的文件。这并不可行,因为这些是特殊的只读文件,没有传统意义上的文件长度(使用stat()查询时st_size通常返回0)。正确的方法是使用read()系统调用来读取内容。

  • /proc/device-tree是推荐方案:它以目录树的形式直观地呈现所有设备树节点和属性。你无需手动解析二进制格式,直接进行文件操作即可,且兼容性良好(主流内核版本>=3.10均支持)。
  • 若确实需要原始dtb文件(例如用于签名验证或离线分析),则需确认启动环境中是否保留了该文件。它可能位于U-Boot环境变量fdt_addr_r指向的内存地址,也可能被打包在initramfs中(例如/dtb路径下)。
  • 尽量避免依赖/sys/firmware/devicetree/base:在某些ARM64平台上该路径可能不可见,且部分Linux发行版默认不会挂载它。

使用C++递归遍历/proc/device-tree提取节点信息

这是最轻量且最可靠的方法。其逻辑非常直观:/proc/device-tree下的每个子目录对应设备树中的一个节点(device node);而目录中的每个普通文件则对应一个属性(property),文件内容即为该属性的值(注意:内容可能包含'\0'字符,因此必须使用read(),而非fgets等字符串函数)。

实现时需注意以下几个要点:

立即学习“C++免费学习笔记(深入)”;

  • 使用opendir()readdir()遍历目录,注意跳过“.”和“..”这两个特殊条目。
  • 对每个条目调用stat()判断其类型:如果是目录(S_ISDIR()),则递归进入处理子节点;如果是普通文件(S_ISREG()),则读取其内容作为属性值。
  • 属性值的末尾不会自动附加'\0',其真实长度需通过stat()获取的st_size来确定。对于字符串类属性(如compatible),其内容本身会以'\0'结尾;但对于二进制属性(如reg),它则是原始字节流。
  • 设备树路径可能较深,为输出清晰,建议使用栈结构或递归来控制缩进显示,避免在代码中硬编码层级限制。

以下是一个简单的代码片段,演示如何读取/proc/device-tree/cpus/cpu@0/compatible属性:

int fd = open("/proc/device-tree/cpus/cpu@0/compatible", O_RDONLY);
struct stat st;
fstat(fd, &st);
std::vector buf(st.st_size);
read(fd, buf.data(), st.st_size);
// 此时,buf[0]...buf[st.st_size-1] 就是原始的属性值字节
close(fd);

使用libfdt解析原始dtb文件(需链接libfdt库)

当必须处理原始dtb二进制文件时(例如从Flash存储直接dump,或需与内核启动参数进行一致性校验),libfdt库便成为事实上的标准工具。它被dtc(设备树编译器)、u-boot等广泛使用,头文件为,所有函数均以fdt_为前缀。

典型的解析流程如下:

  • 首先,使用fdt_open_into()fdt_load()等函数,将完整的dtb文件加载到内存中。需特别注意:提供的dtb数据必须是完整且未被截断的二进制流。
  • 接着,可使用fdt_first_subnode()fdt_next_subnode()这对函数组合来遍历设备树的子节点。
  • 读取属性则使用fdt_getprop()函数。它返回一个指向dtb内部缓冲区的指针,**该指针绝对不可手动free,其生命周期完全依赖于fdt blob本身的有效性**。属性的实际长度会通过一个输出参数(lenp)返回。

使用libfdt时,有几个容易出错的地方:

  • 在进行任何其他操作之前,必须先调用fdt_check_header()来校验dtb头的合法性,否则非法dtb文件很可能导致程序段错误(Segmentation Fault)。
  • fdt结构体本身不管理内存,因此承载dtb数据的缓冲区在整个使用周期内都必须保持有效,不能是栈上的临时变量,也不能被realloc等操作移动。
  • 若在多线程环境下使用,需自行加锁,因为libfdt本身并非线程安全,同一fdt实例不支持并发访问。

为何不推荐使用libdevicetree或自行编写解析器

或许有人会问,是否存在其他库,或者是否可以自行编写解析器?通常不建议这样做。

libdevicetree这类非主流库往往缺乏持续维护,API可能不稳定。而自行编写解析器风险极高。dtb的二进制格式包含魔数(magic number)、总长度、结构体偏移量、字符串表偏移量等多个字段,且存在版本差异(如v17/v18)、对齐要求(通常为8字节)以及复杂的字符串表引用机制。即便跳过严格校验,仅想正确识别节点和属性的边界,也极易发生数组越界读取错误。

在实际开发中,大约95%的需求通过/proc/device-tree接口即可完美解决。剩余的5%场景,如bootloader调试或固件签名校验,才是libfdt的用武之地。若非要绕过这两条成熟路径,很可能在自行实现的fdt_next_tag()循环中陷入困境,或读取到乱码数据。

归根结底,真正的挑战并非“如何读取数据”这一动作,而是明确所需数据究竟位于哪一层:是启动时传给内核的原始硬件描述,还是内核解析后生成的运行时视图,亦或是驱动实际看到的platform_device资源?这三者的语义截然不同,绝不能混为一谈。

来源:https://www.php.cn/faq/2320694.html

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
ThinkPHP如何使用ThinkOrm封装_ThinkOrm数据库封装方法【指南】

ThinkPHP如何使用ThinkOrm封装_ThinkOrm数据库封装方法【指南】

一、引入 ThinkOrm 独立包并初始化连接 如果你正在寻找一个轻量、独立且能兼容多种数据库的ORM方案,又不想为了它而引入整个ThinkPHP框架,那么ThinkOrm的封装方案正好能派上用场。它本质上是一个剥离出来的PDO抽象层,开箱即用。具体怎么操作呢?咱们一步步来看。 首先,ThinkOr

时间:2026-05-06 09:48
ThinkPHP怎样监控Session状态_Session会话状态监控【会话】

ThinkPHP怎样监控Session状态_Session会话状态监控【会话】

ThinkPHP会话状态监控:五种立即可用的实战方法 在ThinkPHP项目里,你是否遇到过这样的困惑:用户会话好像突然失效了,数据莫名其妙丢失,或者你根本不确定Session到底有没有正常启动?这背后,往往是Session中间件配置、存储驱动异常,或者客户端Cookie出了问题。别担心,下面这五种

时间:2026-05-06 09:48
ThinkPHP使用Redis缓存驱动连接失败_PHP扩展安装与连接池配置

ThinkPHP使用Redis缓存驱动连接失败_PHP扩展安装与连接池配置

根本原因是Redis扩展未启用或长连接配置不当:需确认phpinfo中Redis Support已启用、TP配置开启persistent=true并设prefix防污染,Swoole等常驻框架须改用连接池,且必须手动ping检测连接存活。 说到ThinkPHP项目里Redis连接失败,很多开发者第一

时间:2026-05-06 09:47
PHP 中 foreach 循环内正确使用 elseif 判断字符串值

PHP 中 foreach 循环内正确使用 elseif 判断字符串值

PHP 中 foreach 循环内正确使用 elseif 判断字符串值 在 PHP 的 foreach 循环中,使用 if elseif 条件语句判断 JSON 字段的字符串值时,务必将字符串字面量用单引号或双引号包裹。否则,PHP 会将其解释为未定义的常量,从而引发 Notice 级别错误,并可能

时间:2026-05-06 09:47
C#怎么使用隐式类型var C#var和显式类型的区别什么时候该用var什么时候不该用【语法】

C#怎么使用隐式类型var C#var和显式类型的区别什么时候该用var什么时候不该用【语法】

C 怎么使用隐式类型var C var和显式类型的区别什么时候该用var什么时候不该用【语法】 var是编译期语法糖,编译时推断类型生成等效IL,非动态类型;适用于类型冗长、LINQ、泛型初始化等场景,但工厂方法返回object、数值精度敏感、需明确接口语义时应显式声明类型。 var 是编译期语法糖

时间:2026-05-06 09:46
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 日榜
  • 周榜
  • 月榜
热门教程
更多
  • 游戏攻略
  • 安卓教程
  • 苹果教程
  • 电脑教程