组件开发基本概念
文档说明
本文介绍 openUBMC 组件开发的基本概念与核心机制。本文面向初次接触 openUBMC 组件开发的工程师。
本文重点回答以下问题:
- openUBMC 采用微组件架构的原因。
- “组件”“Skynet 服务”“进程”“MDS 类”“D-Bus 对象”等概念的含义。
- MDS、
mdb_interface、资源协作接口、CSR/PSR、Skynet、错误引擎、BMC Studio CLI、Conan/CMake、DT、hica、manifest分别解决的问题。 - 模型、接口、业务代码和硬件配置之间的关系。
- 一个组件从模型定义到运行、测试、构建和产品集成的大致阶段。
- 部件开发者和整机开发者在同一套组件架构中的不同关注点。
说明
本文中的“组件开发基本概念”既包括业务组件本身,也包括组件开发所依赖的模型、运行框架、公共组件和工程机制。它们并不属于同一种软件实体,需要结合各自职责分别理解。
1. openUBMC 微组件架构概述
1.1 组件化的价值
openUBMC 是算力设备管理软件。与将大量业务集中在单个程序中的方式不同,openUBMC 将系统功能拆分为多个相对独立、可以自行演进的组件,再由多个组件组合形成完整系统。
组件化带来的核心价值包括:
- 独立开发:开发者可以围绕单一业务边界修改或新增组件,而不需要理解整个系统的全部实现。
- 独立测试:组件在开发阶段即可进行单元测试和集成测试,尽早发现问题。
- 独立构建和发布:组件通过 Conan 包进行管理,可以单独构建、打包和发布。
- 按需组合和裁剪:产品可以根据机型和业务诉求选择组件组合,更适合差异化和定制化场景。
- 接口解耦:组件之间通过统一的资源协作接口交互,而不是直接依赖对方内部代码。
因此,openUBMC 的组件化不仅是“把代码拆成多个仓”,还包括对模型、接口、运行、测试、构建和发布边界的拆分。
1.2 微组件定义
从开发视角看,一个 openUBMC 组件可以理解为一个相对独立的业务功能单元。它通常拥有自己的:
- MDS 模型;
- 业务代码;
- 对外资源协作接口实现;
- 对其他组件资源协作接口的依赖声明;
- 测试代码;
- 构建和打包配置;
- 组件版本与 Conan 包。
“微”并不表示组件必须非常小,而是强调功能边界清晰、职责相对集中、可以独立演进。
多数业务组件只需要一个 Skynet 服务即可完成主要业务,但一个组件也可以根据业务需要创建多个 Skynet 服务。
1.3 组件、服务和进程的区别
组件、Skynet 服务和操作系统进程分别属于不同层次。
| 概念 | 含义 | 典型作用 |
|---|---|---|
| 组件(Component) | 功能开发、模型、测试、构建和发布的基本边界 | 承载一类相对独立的业务能力 |
| Skynet 服务(Service) | Skynet 中的独立运行单元,拥有独立 Lua 虚拟机 | 执行组件运行态业务逻辑 |
| 进程(Process) | 操作系统进程,可承载一个或多个 Skynet 服务 | 提供运行资源和故障隔离边界 |
| 工作线程(Worker Thread) | Skynet 进程内部用于调度服务的线程 | 支持多个服务并发运行 |
| 协程(Coroutine) | 服务内部的轻量级协作式执行单元 | 处理异步 RPC、数据库、网络和周期任务等 |
可以将其关系简化理解为:
产品
└── 多个进程
└── 一个或多个 Skynet 服务
└── 对应组件业务
└── 一个或多个协程 / Task一个组件通常对应一个主要 Skynet 服务,但并不是强制的一一对应关系;多个组件也可以根据运行编排进入同一个进程。
1.4 组件间协作方式
组件之间原则上不直接调用彼此的内部函数,而是通过资源协作接口进行协作。
资源协作接口采用 D-Bus 协议实现,通过“服务、对象路径、接口、属性/方法”等信息描述和访问资源。
一次典型的跨组件调用可以概括为:
组件 A
│
│ 声明并使用资源协作接口
▼
资源协作接口 / D-Bus
│
│ 属性访问 / RPC 方法调用
▼
组件 B
│
└── 执行组件 B 内部业务逻辑这种方式把“能力的定义”和“能力的内部实现”隔离开。调用方只依赖统一接口契约,不需要了解被调用组件内部的代码结构。
1.5 组件开发的分层理解
openUBMC 组件开发涉及多个机制,如果只记忆 MDS、CSR、Skynet、Conan 等名称,很容易把它们看成彼此独立的工具。
从整体上可以将相关机制理解为四个相互衔接的层次:
┌─────────────────────────────────────────────┐
│ 产品与集成层 │
│ manifest / hica / 产品组件组合 / 运行编排 │
├─────────────────────────────────────────────┤
│ 工程交付层 │
│ DT / bingo / CMake / Conan / 组件包 │
├─────────────────────────────────────────────┤
│ 运行与协作层 │
│ 业务组件 / Skynet / D-Bus / 资源协作接口 │
│ 错误引擎 │
├─────────────────────────────────────────────┤
│ 模型与硬件描述层 │
│ MDS / mdb_interface / CSR / PSR │
└─────────────────────────────────────────────┘四层共同构成组件从定义到进入产品的完整体系:
- 模型与硬件描述层定义组件管理什么、接口契约是什么以及实际硬件有什么。
- 运行与协作层使组件业务运行起来,并通过统一接口与其他组件交互。
- 工程交付层保证组件能够进行代码生成、测试、构建和独立发布。
- 产品与集成层负责将多个组件组合为具体产品,并完成运行编排和制品集成。
2. 微组件架构中的核心组成及作用
2.1 业务组件——功能的实际提供者
业务组件是 openUBMC 功能实现的主体。
从开发视角看,一个业务组件通常需要回答四个问题:
- 组件管理什么数据和对象? —— 由 MDS 描述。
- 组件向系统提供什么能力? —— 由资源协作接口定义,组件负责实现。
- 组件依赖其他组件的什么能力? —— 通过运行时资源依赖进行声明。
- 组件业务逻辑如何运行? —— 由 Skynet 服务以及 Lua/C/C++ 等业务代码承载。
因此,业务组件并不是孤立的一组源码,而是模型、接口、业务实现、运行和工程交付共同形成的功能单元。
2.2 MDS——组件的软件模型
MDS(Module Description Source)是微组件框架的核心模型源,用于描述组件本身以及组件管理的数据模型。
MDS 主要描述:
- 组件名称、版本、类型等基本信息;
- 组件构建和测试依赖;
- 运行时需要访问的资源协作接口;
- 组件管理的类、属性和对象路径;
- 组件实现的资源协作接口;
- 私有属性、持久化、权限等模型信息;
- IPMI 命令模型;
- 结构体、枚举等复杂数据类型。
常见模型文件包括:
| 文件 | 主要作用 |
|---|---|
service.json | 描述组件服务模型、版本、构建/测试依赖以及运行时资源依赖 |
model.json | 描述类、属性、对象路径、接口和持久化等数据模型 |
ipmi.json | 描述组件 IPMI 命令模型 |
types.json | 描述结构体、枚举等复杂数据类型 |
schema.json | 根据模型生成,用于模型字段格式校验 |
MDS 的关键价值在于模型驱动开发:开发者先通过模型定义组件结构和资源边界,再由工程框架根据模型生成对象管理、服务初始化、RPC 桩函数和客户端访问等基础代码。
2.3 mdb_interface——公共接口与错误定义
mdb_interface 是组件开发中的公共组件仓,主要承担两类职责。
2.3.1 统一定义资源协作接口
资源协作接口的公共契约在 mdb_interface 中统一维护,包括:
- 对象路径;
- 接口名称;
- 属性;
- RPC 方法;
- 方法请求和响应数据模型等。
可以将其与业务组件之间的关系理解为:
mdb_interface
│
│ 定义公共接口契约
▼
业务组件
│
│ 实现具体业务行为
▼
资源协作接口实例因此:
说明
mdb_interface 定义“接口是什么”,业务组件决定“接口具体做什么”。
2.3.2 统一管理公共错误定义
公共错误定义同样由 mdb_interface 管理。
业务组件可以使用统一生成的错误对象处理异常,中间组件在跨组件 RPC 调用过程中也可以识别、转换和继续传递错误,从而保持跨组件错误语义的一致性。
2.4 资源协作接口——组件之间的标准边界
资源协作接口采用 D-Bus 协议实现,是组件之间交换数据和调用能力的统一通道。
一个运行态资源通常通过以下信息进行定位:
服务(service)
+ 对象路径(object path)
+ 接口(interface)
+ 属性 / 方法(property / method)组件可以通过资源协作接口:
- 对外提供属性;
- 对外提供 RPC 方法;
- 查询其他组件资源;
- 调用其他组件 RPC;
- 订阅资源属性变化。
因此,资源协作接口解决的是:
说明
不同组件之间如何在不依赖彼此内部实现的情况下进行稳定协作。
2.5 Skynet——组件的运行与调度框架
Skynet 是 openUBMC Lua 组件的重要运行框架。
它主要提供:
- 服务运行;
- 服务间消息通信;
- 工作线程调度;
- 协程调度;
- 异步任务处理;
- 周期任务等运行能力。
在 RPC、数据库和网络 I/O 等存在等待的场景中,当前协程可以挂起,使服务能够继续处理其他可执行任务。
需要区分:
- Skynet 服务不是操作系统进程;
skynet.fork创建的是协程,不是线程;- 协程调度不等于实时调度;
- 长时间阻塞操作不适合直接放在普通协程中执行;
- 对于需要真正线程隔离的阻塞型调用,可以结合相应 Worker 机制处理。
2.6 CSR / PSR——硬件自描述
openUBMC 使用硬件自描述机制降低业务软件与具体硬件之间的耦合。
CSR
CSR(Component Self-description Record)从软件视角描述部件内部实际硬件,包括:
- 器件;
- 器件之间的连接关系;
- 管理拓扑;
- 硬件能力;
- 对象实例;
- 对象属性及其数据关系。
PSR
PSR(Product Self-description Record)从整机角度描述产品硬件组成和能力。
MDS 和 CSR 关注的对象不同:
MDS
从软件组件角度定义:
“软件能够管理什么”
↓ 对应
CSR
从具体部件角度描述:
“当前硬件实际有什么”CSR 中的对象类型需要与相应 MDS 类建立对应关系,对象属性也需要与组件模型允许管理的属性保持一致。
说明
MDS 和 CSR 不是两套孤立机制,而是共同完成软件模型与实际硬件实例之间的映射。
2.7 错误引擎——统一跨组件错误语义
错误引擎用于统一组件错误的定义、生成和处理方式。
在跨组件调用链中,错误可能经过多个组件逐级返回。如果各组件自行设计错误格式,调用方难以形成统一处理逻辑。
统一错误机制使组件能够:
- 使用公共错误类型;
- 抛出标准化异常;
- 识别其他组件返回的错误;
- 对错误进行必要的转换;
- 保持跨组件调用链中的错误语义一致。
2.8 hica——组件运行编排
hica 与组件运行阶段的组织和编排相关。
在产品中,多个组件可能存在:
- 启动顺序要求;
- 子系统归属;
- 进程组织要求;
- 并行拉起需求。
hica 相关配置用于描述这些运行关系。
因此,hica 主要回答:
说明
组件进入产品后,以怎样的运行关系被组织和拉起。
它不是接口定义机制,也不是组件包管理机制。
2.9 BMC Studio 与 BMC Studio CLI (bingo)
BMC Studio 提供组件开发相关的可视化配置和工程能力,BMC Studio CLI (bingo) 则提供统一的命令行开发入口。
它将模型代码生成、测试、构建等工程动作统一起来,例如:
bingo gen
bingo test -ut
bingo test -it
bingo buildbingo 本身并不替代 MDS、测试框架、CMake 或 Conan,而是对这些工程能力进行统一组织,为不同组件提供一致的开发入口。
2.10 Conan 与 CMake——组件构建和交付基础
openUBMC 使用 Conan 与 CMake 支撑组件的独立构建和交付。
二者职责不同:
- CMake:描述源码如何编译、链接和安装;
- Conan:负责组件包及其依赖的管理、构建和交付。
常见工程文件包括:
CMakeLists.txt:组件编译和安装规则;conanfile.py:组件 Conan 构建脚本;conanbase.py:通用 Conan 构建逻辑。
组件源码经过构建后形成 Conan 组件包,组件包进一步作为产品集成的基本输入。
2.11 DT——开发者测试
开发者测试用于保证组件在进入完整产品之前具备独立验证能力,主要包括:
单元测试
验证组件内部功能单元的正确性,重点关注:
- 正常路径;
- 异常路径;
- 边界条件;
- 内部业务逻辑。
外部组件或环境依赖通常通过 Mock 方式进行隔离。
集成测试
验证组件对外资源协作接口以及组件之间的协作关系。
这类测试通常需要相应运行环境,并通过真实资源访问、属性操作或 RPC 调用验证组件边界行为。
二者关注点可以概括为:
单元测试
↓
组件内部逻辑
集成测试
↓
组件边界及跨组件协作2.12 manifest——产品组件组合
组件构建得到 Conan 包后,还没有形成完整产品。
产品构建需要通过 manifest 选择和组合相应组件包:
组件源码
↓
组件构建
↓
Conan 组件包
↓
manifest 选择和组合
↓
目标产品 / 机型制品因此,manifest 主要解决:
说明
一个具体产品由哪些组件版本组合而成。
3. 组件开发的基本机制
第 2 章分别介绍了组件开发中的核心组成。本章不再重复各概念的定义,而是重点说明它们在组件开发过程中如何相互连接。
3.1 MDS、代码生成与业务实现
MDS、代码生成和业务实现共同构成模型驱动开发链路:
MDS 模型
↓
代码生成
↓
通用基础代码
↓
业务实现MDS 负责描述组件结构、对象模型和资源边界;工程工具依据模型生成对象管理、服务初始化、RPC 桩和客户端访问等通用代码;开发者在生成框架的基础上补充具体业务逻辑。
因此,模型驱动开发的核心是将模型定义、通用框架能力和具体业务实现分离,减少重复开发,并使组件结构和接口保持一致。
3.2 公共接口、组件模型与 RPC
资源协作接口从公共契约到实际可调用能力,需要经过接口定义、组件声明和业务实现几个环节:
mdb_interface
公共接口契约
↓
MDS 声明实现 / 依赖
↓
代码生成
↓
提供方实现业务逻辑
↓
调用方通过资源协作接口访问mdb_interface 负责定义统一接口契约,MDS 描述组件实现或依赖哪些接口,代码生成机制提供基础访问和实现入口,最终由业务组件补充实际处理逻辑。
因此,需要区分“接口已经定义”和“组件已经实现该接口”。只有完成组件侧实现并进入运行态后,相应 RPC 能力才真正可供其他组件调用。
跨组件 RPC 调用还可能涉及调用上下文。RPC 回调中的 ctx 用于承载调用相关上下文信息;当调用继续跨越其他组件时,需要根据业务要求保持相应上下文。
3.3 MDS 与 CSR / PSR 的映射关系
对于硬件相关组件,软件模型和硬件自描述共同参与对象管理:
MDS
软件类 / 属性 / 接口模型
│
│ 对应
▼
业务组件管理对象
▲
│
CSR / PSR
实际硬件 / 对象实例 / 拓扑MDS 定义软件能够识别和管理的模型边界,CSR/PSR 描述具体部件或产品中的实际硬件及实例信息。组件结合两者,将通用软件模型落实到具体硬件环境中。
这种机制使业务代码能够尽量基于统一模型工作,而将不同板卡、部件或产品之间的硬件差异交由自描述配置承载。
3.4 进程、Skynet 服务与协程调度
组件运行时涉及进程、Skynet 服务和协程等不同层次:
进程
↓
Skynet 服务
↓
协程 / Task进程提供操作系统级运行和隔离环境;Skynet 服务承载具体运行态业务;协程用于组织服务内部的异步任务。Skynet 进程内部的工作线程负责调度服务执行。
因此,运行机制的重点是明确不同层次的职责:进程提供运行环境,服务承载业务,协程组织异步执行。
3.5 单元测试与集成测试
开发者测试从组件内部和组件边界两个层次验证功能:
组件内部逻辑
↓
单元测试
组件边界与协作
↓
集成测试单元测试重点验证函数、模块和内部业务逻辑,外部依赖通常通过 Mock 隔离;集成测试重点验证资源协作接口、RPC 以及组件之间的实际协作行为。
两类测试共同保证组件既能独立验证内部逻辑,也能验证对外接口是否符合预期。
3.6 CMake、Conan 与组件包
组件从源码到独立交付物,需要经过编译、安装和打包:
组件源码
↓
CMake 编译 / 安装
↓
Conan 打包与依赖管理
↓
Conan 组件包CMake 主要负责源码如何编译、链接和安装,Conan 负责组件包及其依赖的管理和交付,bingo 为这些工程动作提供统一入口。
最终形成的 Conan 组件包,是组件进入产品集成阶段的重要交付边界。
3.7 manifest 与 hica
组件进入产品后,还需要解决“选择哪些组件”和“组件如何运行”两个不同问题:
Conan 组件包
↓
manifest
选择产品包含哪些组件及版本
↓
产品制品
↓
hica
组织组件运行关系
↓
组件运行环境manifest 侧重产品组件组合,hica 侧重运行时组织和编排。二者分别解决产品构成和运行关系问题,不应混为一谈。
4. 组件开发中的关键概念关系
前文已经介绍了主要概念及其连接机制。本章只对开发过程中容易混淆的几个概念进行集中区分。
4.1 MDS 类和运行态对象
MDS model.json 中定义的是类和模型结构,运行时组件则根据模型管理具体对象实例。
可以理解为:
MDS 类
↓ 实例化 / 管理
运行态对象
↓
具体对象路径和属性数据“类”描述一类对象具有什么属性和能力,“对象”则是运行过程中实际存在和被管理的实例。
4.2 资源协作接口和 RPC 实现
公共接口定义描述接口允许访问哪些属性、调用哪些方法,但接口被定义并不意味着某个业务组件已经完成对应功能。
完整关系为:
接口契约
↓
MDS 声明
↓
代码生成
↓
业务实现
↓
运行时形成对应的资源协作能力因此,需要区分接口契约、模型声明、自动生成代码和业务实现。组件完成实际实现并进入运行态后,相关能力才能通过资源协作接口对外提供。
4.3 build/test 依赖和 required 依赖
不同依赖对应不同阶段:
| 类型 | 主要阶段 | 解决的问题 |
|---|---|---|
dependencies.build | 构建阶段 | 组件构建需要哪些 Conan 包 |
dependencies.test | 测试阶段 | 开发者测试需要哪些组件或包 |
required | 运行阶段 | 组件运行时需要访问哪些资源协作接口 |
因此,“构建时依赖某个包”和“运行时需要某个资源”不是同一个概念。组件能够成功编译,也不代表运行环境中的资源依赖已经满足。
4.4 CSR Object 和 D-Bus Object
CSR 中的 Objects 用于描述具体硬件或业务实例;D-Bus Object 则属于运行态资源协作体系中的对象,两者处于不同层次。
可以理解为:
CSR Object
↓
描述具体硬件 / 业务实例
↓
由相应组件结合 MDS 模型加载和管理
↓
需要对外提供的能力
通过相应运行态资源协作接口暴露因此,CSR 是硬件和实例配置入口,不是 D-Bus 接口定义文件,也不能简单理解为每一个 CSR Object 都与一个 D-Bus Object 一一对应。
4.5 Conan 包和运行中的组件
Conan 包是组件的构建和交付形态,运行中的 Skynet 服务等则属于组件的运行形态。
完整关系可以理解为:
组件源码
↓
构建
↓
Conan 包
↓
产品集成
↓
设备环境
↓
组件被拉起
↓
形成运行态服务因此,“组件包已经生成”只表示组件具备了可交付的软件包形态,并不表示组件已经在目标系统中运行。
5. 组件开发生命周期概览
前面的章节分别介绍了组件开发中的基本概念。将这些概念串联起来,一个组件从设计到进入产品,大致经历以下生命周期:
组件设计
↓
接口与模型定义
↓
代码生成
↓
业务实现
↓
硬件配置与适配(需要时)
↓
运行与资源协作
↓
开发者测试
↓
组件构建与打包
↓
产品集成
↓
设备运行并非所有组件都涉及完全相同的环节。例如,与具体硬件无关的纯业务组件可能不需要直接处理 CSR;不同语言和运行形态的组件在具体实现方式上也可能存在差异。
5.1 组件边界与接口契约
组件开发首先需要确定功能边界,包括组件负责管理什么业务和对象、向外提供哪些能力、依赖哪些外部能力,以及是否与具体硬件相关。
如果某项能力需要跨组件使用,还需要通过统一资源协作接口形成稳定的接口契约。
这一阶段主要确定:
说明
组件负责什么,以及组件与其他组件之间的边界在哪里。
5.2 模型定义与代码生成
组件边界明确后,通过 MDS 描述组件的软件模型,并通过公共接口定义确定需要实现或依赖的资源协作能力。
模型完成后,工程框架根据模型生成相应基础代码:
业务设计
↓
模型与接口定义
↓
代码生成
↓
组件基础框架这一阶段将业务设计转化为可实现的软件结构,并通过代码生成机制承载通用框架能力。
5.3 业务实现
在生成的基础框架上,开发者补充具体业务逻辑,使模型中定义的对象、属性和接口形成实际业务行为。
业务实现通常涉及对象管理、接口实现、跨组件调用、错误处理以及周期或异步业务逻辑等内容。
对于 Lua 组件,这些业务逻辑通常由 Skynet 服务承载。
5.4 硬件配置与适配
对于硬件相关组件,还需要结合 CSR 等硬件自描述信息,将 MDS 定义的软件模型与具体部件中的实际硬件和对象实例建立对应关系。
MDS 软件模型
│
▼
业务组件
▲
│
CSR 硬件实例描述这一环节使通用业务模型能够适配不同板卡或部件,并减少业务代码对具体硬件差异的直接依赖。
5.5 运行与资源协作
完成模型、业务实现以及必要的硬件适配后,组件进入运行环境。
运行态组件通过 Skynet 等运行框架承载业务,并通过资源协作接口与其他组件交换数据、访问属性或调用 RPC。
此时,前面定义的软件模型、接口契约和业务逻辑共同形成实际运行能力。
5.6 测试、构建与组件交付
组件需要通过开发者测试验证内部逻辑和组件边界行为,再通过构建系统形成可独立交付的组件包:
组件实现
↓
开发者测试
↓
CMake / Conan 构建
↓
Conan 组件包组件包代表组件进入产品集成阶段前的独立交付形态。
5.7 产品集成与设备运行
组件包最终需要进入具体产品。产品通过 manifest 等机制选择和组合所需组件及版本,并通过 hica 等运行编排机制组织组件在设备中的运行关系。
多个组件包
↓
产品组件组合
↓
产品制品
↓
运行编排
↓
设备中的完整 BMC 系统至此,组件完成从软件设计、实现和验证到产品集成与设备运行的完整生命周期。
6. 组件开发整体概念图
6.1 核心概念关系
可以用下面的关系理解 openUBMC 组件开发中的主要概念:
┌──────────────────────┐
│ mdb_interface │
│ 接口契约 / 公共错误 │
└──────────┬───────────┘
│
接口定义 / 实现 / 依赖
│
▼
┌──────────────┐ ┌──────────────────────┐
│ MDS │─────────▶│ 业务组件 │
│ 软件模型 │ 代码生成 │ 模型对象 + 业务代码 │
└──────┬───────┘ └──────────┬───────────┘
│ │
│ │ 资源协作接口
│ │ D-Bus / RPC
│ ▼
│ ┌────────────────────┐
│ │ 其他业务组件 │
│ └────────────────────┘
│
│ 模型与实例对应
▼
┌──────────────┐
│ CSR / PSR │
│ 硬件实例/拓扑 │
└──────────────┘
业务组件运行
↓
Skynet / Service / Coroutine
↓
开发者测试
UT / 集成测试 / Mock
↓
组件构建与交付
bingo / CMake / Conan
↓
产品集成
manifest / hica
↓
产品制品与设备运行6.2 总结
openUBMC 组件开发可以概括为一条由模型、实现、运行、验证、交付和集成共同组成的链路:MDS 定义组件的软件模型,mdb_interface 提供公共接口契约,业务组件在生成框架上实现具体功能;硬件相关组件结合 CSR/PSR 完成软件模型与实际硬件实例的对应;运行时通过 Skynet 等框架承载业务,并通过资源协作接口完成跨组件协作;组件经过开发者测试、CMake/Conan 构建后形成独立组件包,再通过 manifest 和 hica 等机制进入具体产品。