# Haiku:device_node 树 / 内核对象模型 / KDL / BeOS 兼容边界 Haiku 有**两套容易看错的东西**,而且**逆驱动与逆系统组件的入口完全不同**: ``` 驱动侧:device_node 树 + on-demand exploration + driver module / device module 两层 系统侧:team / thread / area / port / semaphore 这套内核对象模型 + 用户态服务 ``` **如果目标不是驱动**,别继续在 Device Manager 里找——换到内核对象模型与用户态服务那一层。 ## 一、驱动侧:device_node 树与按需发现 - 设备管理器围绕 **device_node** 构建:每个驱动发布一个或多个节点,构成一棵动态表示硬件的树 - 一个 device_node = **module + attributes + resources** + 父/子指针;**module 是唯一必需的部分** - 启动时只有根节点;主要总线注册上去;**智能总线为自己发现的每个设备创建子节点** - **按需探索**:需要某功能时系统扫描设备文件系统,按内建规则把设备路径翻译成 device node(智能总线上的节点带 PCI 式类型信息),**只查询匹配的模块目录** ``` boot 时驱动没出现、后来访问某设备时才出现 → 可能只是 on-demand exploration ``` - 有"只在系统要求时才被搜索"的按需标志 ## 二、驱动侧:两层模块(driver module / device module) **driver module**(名字必须以 `driver_v1` 结尾)实现:`supports_device`、`register_device`、`init_driver`/`uninit_driver`、`register_child_devices`、`rescan_child_devices`、`device_removed`、`suspend`/`resume`(**后两个从不被调用**) - `register_device` 注册节点并可**认领 I/O 资源**(端口或内存范围)——**同一资源只能被一个节点认领** **device module**(名字必须以 `device_v1` 结尾,经 publish 一类调用发布到 `/dev`)导出:`init_device`/`uninit_device`/`device_removed`、`open`/`close`/`free`/`read`/`write`/`io`/`control`/`select`/`deselect` **在 driver module 里找不到 `read`/`write` 很正常**——那是 device module 的接口。 - 节点属性含显示名、唯一 ID、**固定子节点**、厂商/设备/类型/子类型/接口、标志 - **固定子节点与动态子节点不能混用**,注册时机也不同(固定在前) - 较新版本给节点加了"路径/驱动名"属性,用于显示某设备由哪个驱动、走哪条 `/dev` 路径 ## 三、系统侧:内核对象模型 逆一个**非驱动**的目标时,入口在这里: | 对象 | 说明 | |---|---| | **team** | 进程等价物:地址空间与资源容器 | | **thread** | 执行单位,隶属 team | | **area** | 内存区域(可共享/映射),是"内存从哪来"的答案 | | **port** | **内核消息队列**——线程间、team 间通信的基本手段 | | **semaphore** | 同步原语 | - **信号量的行为有若干标志位影响**:可中断(即使调用方没要求,也可能被终止信号打断)、**释放全部等待者**、**只在有等待者时才递减**——后两者正是实现条件变量语义所需要的 - **调试 API 与这些对象一一对应**:调试器侧有表示 area / semaphore / image / team / thread / syscall 信息的模型类 **逆向含义**:定位"这块内存在哪、谁在等谁、谁给谁发消息",答案在 area / port / semaphore,而不是驱动框架。 ## 四、KDL 与内核调试接口 **KDL** 是内建在内核里的交互式命令调试器,进入方式包括内核 panic、显式的调试器调用、**紧急组合键**、以及硬件异常(缺页、保护违规)。 常用命令:帮助、**栈回溯**、继续执行、**线程列表**、**切换 CPU**、dump 系统日志、**查内存**、重启。 - **命令系统可扩展**:各内核子系统可以注册自己的命令 - 相关输出机制各有适用上下文(在内核调试器上下文里用的打印与常规内核打印不同) - **调试 API 的细节对分析很关键**: - 发给调试器的消息**以一个含线程与 team ID 的头结构开头**,后来又加了**调试端点端口**字段,使同步调试消息能带上调试通道 - 存在一组 **team 调试标志**:team 创建事件、线程事件、**image 事件**、停止新线程、以及**系统调用前追踪** - **系统调用参数缓冲区在 64 位平台上被扩大过**——此前在追踪时有内核内存破坏风险;调试器侧相应按目标架构区分 **注意**:**这个调试接口与 BeOS"概念相似,但源码与二进制都不兼容"**——拿 BeOS 时代的调试器接口假设去套会直接落空。 ## 五、BeOS 兼容边界(别把它当"就是 BeOS") - 目标是**对 BeOS R5(x86)大范围二进制与源码兼容,但反过来不成立**;某些情况下**刻意破坏源码兼容以保住二进制兼容** - 已知差异举例:头文件路径从 `be` 变为 `os`;部分头文件名不同;类的私有字段改名;一些未文档化的 API 未实现;**私有的设备映射 API 被磁盘设备 API 取代** - **调试接口不兼容**;**文件系统 API 也变过**(BeOS 时代的文件系统驱动不能用) - 对更早版本的兼容支持已被放弃 - **内核内部的改动也可能显式破坏兼容**——例如某次为做锁竞争测量把自旋锁改成结构体,就明确标注会破坏二进制兼容 ## 六、用户态服务边界 不是所有"Haiku 组件"都在内核里:图形服务器、应用注册服务一类是**用户态服务**,它们有自己的协议与生命周期。逆这类目标时: ``` 不要找内核驱动接口 要看用户态服务的通信端点与协议 ``` ## 决策树 ``` 拿到 Haiku 目标 ├─ 先判:驱动 / 内核子系统 / 用户态服务 │ ├─ 驱动 → device_node 树 + 两层模块 │ ├─ 内核子系统 → team/thread/area/port/semaphore + KDL │ └─ 用户态服务 → 服务端点与协议(不在内核) ├─ 驱动"不存在" │ ├─ 按需发现:只有被请求时才会搜索/加载 │ └─ 查总线类型规则与按需标志 ├─ 在 driver module 里找不到 read/write │ └─ 那是 device module(device_v1)的接口 ├─ 内存/同步/消息相关异常 │ └─ 落到 area / semaphore(注意其扩展标志位)/ port ├─ 现场调试 │ ├─ KDL 可查栈回溯、线程、内存、系统日志、切换 CPU │ └─ 调试接口与 BeOS 不兼容,别套旧接口 └─ 兼容性相关判断 ├─ R5 二进制/源码兼容是单向的 └─ 调试接口与文件系统 API 属于不兼容的部分 ``` ## 工具与验证 - 驱动:device_node 树(module/attributes/resources)、两层模块的接口集合与命名约束、资源认领唯一性 - 系统:team/thread/area/port/semaphore 的归属与关系;信号量标志位 - 调试:KDL 命令与调试 API 消息结构(头部字段、team 调试标志、syscall 追踪) - 兼容:目标是否来自 BeOS 时代(调试接口/文件系统 API 会直接落空) - 验证:能同时说清「这个目标是驱动还是系统组件 + 它在哪一层、由谁加载 + 它用的对象属于哪套模型」 ## 该平台的坑(汇总) - **把按需发现当驱动缺失**:初始只探索最小集合 - **在 driver module 里找 `read`/`write`**:那是 device module 的接口 - **忽略 `driver_v1` / `device_v1` 命名约束** - **期待 `suspend`/`resume` 被调用**:当前实现不调用 - **混用固定子节点与动态子节点** - **逆非驱动目标时仍钻进 Device Manager**:入口应是内核对象模型或用户态服务 - **忽略信号量的扩展标志**:可中断、释放全部、仅在等待时递减会改变行为 - **拿 BeOS 的调试接口或文件系统 API 假设去套**:两者都不兼容 - **以为兼容是双向的**:只保证"旧程序能跑",不保证新接口回退 - **忽略 64 位下调试 API 缓冲区结构的变化**:按旧布局解析会错