1. 组件概述
1.1 组件简介
libmcpp 是一个现代 C++ 开发框架,专为 openUBMC 项目设计,旨在提供高效、安全、易用的 C++ 开发环境。该框架采用五层分层模块化设计,遵循现代 C++17 最佳实践,主要面向嵌入式系统(尤其是 BMC 固件)和服务器应用程序开发。框架提供了丰富的基础设施和功能组件,涵盖反射系统、异常处理、日志系统、应用框架、变体类型、信号槽、Future/Promise 异步编程、D-Bus 通信、共享内存数据库等核心能力,使开发人员能够快速构建可靠的应用程序。
libmcpp 采用 Mulan PSL v2 开源许可证,支持 GCC 7+、Clang 5+、MSVC 2019+ 等主流编译器,可在 Linux、macOS、Windows、FreeBSD 等多种平台上运行。
1.2 解决什么问题
传统 BMC 开发面临以下痛点:
- 代码复用性差:缺乏统一的开发框架,各模块重复实现基础功能,导致开发效率低下
- 模块耦合度高:模块间直接依赖,难以独立开发、测试和维护,无法实现功能热插拔
- 内存管理复杂:手动内存管理容易出错,内存泄漏和悬垂指针问题频发,影响系统稳定性
- 配置管理混乱:缺乏统一的配置管理机制,各模块各自解析配置,格式不统一
- 调试和维护困难:缺乏统一的日志和诊断机制,问题定位耗时长
- 类型安全问题:传统 C 风格接口缺乏编译期类型检查,运行时错误难以防范
libmcpp 通过以下方式解决上述问题:
- 统一开发框架:提供插件化架构(Plugin + Service + Supervisor),通过
application单例协调各管理器工作 - 现代 C++ 安全保障:采用 RAII、智能指针、编译期类型检查等现代 C++ 技术确保内存和类型安全
- 声明式配置管理:基于 JSON 的统一配置管理系统,支持命令行参数解析和配置文件加载
- 结构化日志系统:提供多级日志、多目标输出、结构化日志格式,便于日志检索和问题定位
- 反射系统:提供运行时类型信息获取和序列化/反序列化能力,简化数据交换
1.3 核心功能
libmcpp 框架采用五层分层架构设计,从底层到顶层依次为:
| 层次 | 模块 | 功能说明 |
|---|---|---|
| 基础设施层 | common, exception, filesystem, string, time, error, sync, memory | 通用工具、异常处理、文件系统、字符串处理、时间工具、错误码/错误域、并发同步、智能指针 |
| 数据处理层 | variant, dict, json, reflect, expr | 动态类型系统、键值对容器、JSON 解析/生成、运行时反射、表达式引擎 |
| 功能服务层 | db, log, interprocess | 共享内存对象数据库、结构化日志系统、进程间通信(共享内存、互斥锁等) |
| 应用程序框架层 | core (plugin_manager, service_factory, service_manager, config_manager, supervisor_manager) | 插件管理、服务注册/创建、配置管理、监督树生命周期管理 |
| 应用层 | discovery_service, micro_component | 自发现服务、MicroComponent 微组件标准接口 |
核心特性一览:
- 反射系统:运行时类型信息获取与序列化,支持嵌套类型和 STL 容器,通过
MC_REFLECT宏注册元数据 - 异常处理:带上下文追踪的统一异常体系,支持异常链(exception chain)、错误码映射和堆栈符号解析
- 日志系统:多后端异步日志,支持 ConsoleAppender、FileAppender、RotatingFileAppender 等,提供结构化日志格式
${key} - 应用程序框架:插件+服务架构,支持插件动态加载(dlopen)、服务工厂模式、监督树(Supervisor Tree)故障恢复
- 变体类型(variant):类似于
std::any的动态类型,支持序列化和与 JSON 无缝互操作 - D-Bus 通信:提供 C++ 和 Lua 双层接口,支持同步/异步通信、信号订阅、对象路径注册
- Future/Promise:异步编程模型,支持链式操作和异常传播
- MicroComponent 接口:标准化的 BMC 微组件接口,包含健康检查、配置管理、调试、日志限流、性能统计等
1.4 关键术语表
| 术语 | 解释 |
|---|---|
| Plugin(插件) | 可动态加载的功能模块,通过 dlopen 加载 .so 文件,向框架注册服务类型 |
| Service(服务) | 应用程序的核心功能单元,有明确的生命周期(init → start → run → stop → cleanup) |
| Supervisor(监督器) | 负责监督服务生命周期、检测故障并执行恢复策略(借鉴 Erlang/OTP 监督树模式) |
| DDS(Device Description Schema) | 定义部件的设备描述规范,包括接口定义、属性声明和方法签名 |
| CSR(Component Self-Description Record) | 存储部件的自描述记录,包含配置数据、运行时状态和元数据信息 |
| MicroComponent(微组件) | openUBMC 的核心架构单元,每个 BMC 组件都继承自 micro_component_object |
| variant | 动态类型容器,类似 std::any,支持类型安全的访问和序列化 |
| dict | 键值对容器,保持插入顺序,支持嵌套和共享数据模型(copy-on-write) |
| RAII | Resource Acquisition Is Initialization,资源获取即初始化,确保资源自动管理 |
| MC_REFLECT | 反射注册宏,用于向反射系统注册类的成员和方法元数据 |
| MC_INTERFACE | 接口元数据声明宏,用于声明 D-Bus 接口信息 |
| MC_OBJECT | 对象元数据声明宏,定义业务对象的类名、DBus 路径模板和接口列表 |
1.5 外部交互边界图
2. API 使用说明与示例
2.1 反射系统
功能说明
反射系统提供运行时类型信息获取和对象序列化/反序列化能力。通过 MC_REFLECT 宏注册类的成员元数据后,即可在运行时动态访问成员、进行序列化操作,支持嵌套类型和 STL 容器。
参数说明
属性参数说明
反射系统通过 MC_REFLECT(ClassName, (member1)(member2)...) 宏注册类的可反射成员,每个成员会被赋予对应的名称用于运行时访问和序列化。
方法参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 | 取值范围 |
|---|---|---|---|---|
| MC_REFLECT(Class, ...) | 类名, 成员列表 | void | 注册类的反射元数据 | 宏定义,编译时展开 |
| MC_INTERFACE(Name, Version) | 接口名称, 版本号 | void | 声明接口元数据 | 字符串常量 |
| MC_OBJECT(Class, ClassName, PathTmpl, Interfaces) | 类, 类名, 路径模板, 接口列表 | void | 注册业务对象元数据 | 编译时常量 |
返回值与异常
| 返回值 | 含义 | 触发条件 | 处理建议 |
|---|---|---|---|
| 反射操作成功 | 成员访问正确 | 成员名匹配、类型正确 | 无 |
std::bad_variant_access | 类型访问错误 | variant 类型不匹配 | 检查 .as<T>() 的类型参数 |
应用场景
- 对象序列化为 JSON 字符串进行持久化存储或网络传输
- 从 JSON 字符串反序列化为 C++ 对象
- 运行时动态访问对象成员
- 与 D-Bus 属性系统集成,自动处理属性变更信号
限制条件
- 需要 C++17 或更高版本编译器支持
- 反射注册必须在编译期完成,不支持运行时动态添加新成员类型
- 反射信息存储为静态数据,增大可执行文件体积
调试示例
代码调试
#include <mc/reflect.h>
class User {
public:
std::string name;
int id;
std::vector<std::string> tags;
};
MC_REFLECT(User, (name)(id)(tags))
// 序列化对象
User user{"admin", 1001, {"staff", "devops"}};
mc::variant var = user;
std::cout << "JSON: " << mc::json::to_string(var);
// 反序列化对象
std::string json = "{\"name\":\"guest\",\"id\":1002,\"tags\":[\"visitor\"]}";
User new_user = mc::json::from_string(json).as<User>();2.2 日志系统
功能说明
日志系统提供灵活的多级日志记录和输出功能,采用模块化设计,支持多种日志级别(trace/debug/info/notice/warn/error/fatal)、多种输出目标(控制台、文件、滚动文件、按日期轮转)和结构化日志格式。
参数说明
属性参数说明
日志级别定义:
| 级别 | 数值 | 说明 |
|---|---|---|
| trace | 0 | 最详细的跟踪信息 |
| debug | 1 | 调试信息 |
| info | 2 | 一般运行信息 |
| notice | 3 | 重要节点通知 |
| warn | 4 | 警告信息 |
| error | 5 | 错误信息 |
| fatal | 6 | 致命错误 |
方法参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 | 取值范围 |
|---|---|---|---|---|
| MC_LOG_REGISTER_APPENDER | 名称, Appender类型 | void | 注册日志输出目标 | 宏定义 |
| ilog / wlog / elog / dlog / nlog / tlog / flog | format, args... | void | 全局日志宏(需引入 mc/log.h) | 结构化格式 ${key} |
| mc_ilog / mc_wlog / mc_elog | logger, format, args... | void | 基于指定 Logger 的日志宏 | 同上 |
返回值与异常
| 返回值 | 含义 | 触发条件 | 处理建议 |
|---|---|---|---|
| 日志写入成功 | 日志已输出到目标 | 日志级别满足过滤条件 | 无 |
| 日志被丢弃 | 日志级别低于当前设置 | 日志级别过滤 | 调整日志级别或使用更高级别宏 |
应用场景
- 系统运行状态记录和审计
- 故障排查和问题定位
- 性能监控和分析
- 运维诊断
限制条件
- 在生产环境中建议使用 info 或以上级别,避免大量 debug 日志影响性能
- 异步日志模式下,程序异常退出可能导致少量日志丢失
- 日志文件需要定期清理或轮转,防止磁盘占满
调试示例
代码调试
#include <mc/log.h>
// 注册日志后端
MC_LOG_REGISTER_APPENDER(console, mc::log::ConsoleAppender);
int main() {
// 配置日志系统
mc::log::Logger::getInstance()
.setAppender<mc::log::RollingFileAppender>("app.log", 10*1024*1024, 5)
.setLevel(mc::log::Level::Debug);
// 记录简单日志
MC_LOG_INFO("系统初始化完成");
// 记录结构化日志
MC_LOG_INFO("用户活动")
<< "user_id=" << 42
<< "action=" << "login";
// 使用全局日志宏
ilog("系统初始化完成");
ilog("用户 ${user} 从 ${ip} 登录成功", ("user", "admin")("ip", "192.168.1.100"));
elog("连接数据库失败: ${error}", ("error", "Connection timeout"));
return 0;
}2.3 应用程序框架
功能说明
应用程序框架是 libmcpp 的核心模块,采用插件 + 服务的架构模式。通过 application 单例协调 PluginManager、ServiceFactory、ServiceManager、ConfigManager 和 SupervisorManager 的工作,实现模块化应用程序开发。
参数说明
属性参数说明
应用程序框架的基本组成部分:
| 管理器 | 主要职责 |
|---|---|
| ConfigManager | 命令行参数解析、配置文件加载、配置数据提供 |
| PluginManager | 动态加载/卸载插件,管理插件依赖和生命周期 |
| ServiceFactory | 服务类型注册和创建 |
| ServiceManager | 管理服务实例的生命周期(创建、启动、停止) |
| SupervisorManager | 监督器的创建和管理,服务健康监控和故障恢复 |
方法参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 | 取值范围 |
|---|---|---|---|---|
application::instance() | void | application& | 获取应用程序单例 | 全局唯一实例 |
application::initialize(argc, argv) | int, char** | bool | 初始化应用程序 | 成功返回 true |
application::start() | void | application& | 启动应用程序 | 链式调用 |
application::exec() | void | void | 进入事件循环 | 阻塞直到 stop() |
application::stop() | void | void | 停止应用程序 | 安全退出 |
register_plugin<T>() | 插件类型 | bool | 注册插件 | 模板参数 |
返回值与异常
| 返回值 | 含义 | 触发条件 | 处理建议 |
|---|---|---|---|
| true | 操作成功 | 初始化/启动正常完成 | 无 |
| false | 操作失败 | 配置错误、插件加载失败、依赖缺失 | 检查日志定位失败原因 |
应用场景
- BMC 应用程序开发和部署
- 需要插件化架构的服务端程序
- 需要高可靠性的嵌入式系统(监督树模式故障恢复)
限制条件
- 单个应用程序实例不支持超过 256 个插件同时加载
- 插件动态卸载可能导致依赖该插件的服务暂时不可用
- 需要 POSIX 线程库支持
调试示例
代码调试
#include <mc/core/application.h>
// 定义服务
class my_service : public mc::service_base<my_service> {
public:
static const char* service_type() { return "example.my_service"; }
static void register_options(mc::po::options_description& cfg_opts) {
cfg_opts.add_options()
("my_service.timeout", mc::po::value<int>()->default_value(30), "超时时间(秒)");
}
static const std::vector<std::string>& dependencies() {
static std::vector<std::string> deps = {"logger"};
return deps;
}
bool init(mc::dict args) override {
m_timeout = args.get("my_service.timeout").as<int>();
return true;
}
bool start() override {
set_state(mc::service_state::running);
return true;
}
bool stop() override {
set_state(mc::service_state::stopped);
return true;
}
private:
int m_timeout;
};
// 定义插件
class my_plugin : public mc::plugin_base<my_plugin> {
public:
static const char* plugin_name() { return "my_plugin"; }
bool initialize() override { return true; }
void startup() override {}
void shutdown() override {}
};
// 应用程序入口
int main(int argc, char** argv) {
try {
mc::application& app = mc::application::instance();
app.set_version("1.0.0");
app.register_plugin<my_plugin>();
app.initialize(argc, argv);
app.startup();
app.exec();
return 0;
} catch (const std::exception& e) {
std::cerr << "错误: " << e.what() << std::endl;
return 1;
}
}2.4 异常处理
功能说明
libmcpp 提供统一的异常处理机制,基于 C++ 标准异常进行扩展,支持异常链(exception chain)、错误码映射、堆栈符号解析、异常信息格式化等能力。通过 MC_THROW、MC_RETHROW_EXCEPTION 等宏简化异常的抛出和重新抛出。
参数说明
方法参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 | 取值范围 |
|---|---|---|---|---|
| MC_THROW(ExceptionType, msg) | 异常类型, 消息 | void | 抛出指定类型异常 | 宏定义 |
| MC_RETHROW_EXCEPTION(msg) | 消息 | void | 捕获并重新抛出,添加上下文 | 宏定义 |
| e.what() | void | const char* | 获取异常描述 | 重写 std::exception::what |
| e.code() | void | int64_t | 获取异常错误码 | 返回异常代码 |
| e.to_string() | level | std::string | 获取异常信息的字符串表示 | level 为日志级别 |
返回值与异常
| 返回值 | 含义 | 触发条件 | 处理建议 |
|---|---|---|---|
| 异常被捕获 | 异常已处理 | catch 块匹配异常类型 | 根据异常类型采取对应处理 |
| 异常未被捕获 | 程序终止 | 无匹配的 catch 块 | 添加通用 catch 块兜底 |
应用场景
- 数据库操作失败时的错误传播
- 插件加载失败的错误报告
- 第三方库异常的包装和转换
- 异步操作中的异常传播
调试示例
代码调试
#include <mc/exception.h>
void database_operation() {
try {
throw std::runtime_error("连接失败");
} catch (const std::exception& e) {
MC_THROW(mc::db_error, "数据库操作失败")
<< mc::error_info("operation", "query")
<< mc::error_info("sql", "SELECT * FROM users")
<< mc::error_cause(e);
}
}
int main() {
try {
database_operation();
} catch (const mc::exception& e) {
std::cerr << "错误: " << e.what() << std::endl;
std::cerr << "错误码: " << e.code() << std::endl;
}
return 0;
}2.5 插件系统
功能说明
插件系统负责动态加载和管理插件模块。支持两种模块类型:内建模块(编译时静态链接)和动态模块(运行时动态加载 .so 文件)。每个模块拥有独立的反射工厂,实现模块间的完全类型隔离。
参数说明
方法参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 | 取值范围 |
|---|---|---|---|---|
| load_plugin(name) | string | bool | 加载指定插件 | 插件名称 |
| load_plugins(names) | vector<string> | bool | 加载插件列表 | 插件名称列表 |
| unload_plugin(name) | string | bool | 卸载指定插件 | 已加载的插件名 |
| unload_all_plugins() | void | void | 卸载所有插件 | 无 |
| find_plugin(name) | string | plugin* | 查找已加载的插件 | 插件名称 |
| get_loaded_plugins() | void | vector<string> | 获取已加载插件列表 | 无 |
返回值与异常
| 返回值 | 含义 | 触发条件 | 处理建议 |
|---|---|---|---|
| true | 加载成功 | 插件 .so 文件有效、依赖满足 | 无 |
| false | 加载失败 | 插件文件不存在、版本不兼容、依赖缺失 | 查看日志定位具体原因 |
应用场景
- 热更新:不重启应用更新插件
- 动态功能切换:根据运行条件动态加载/卸载功能模块
- 故障隔离:卸载故障插件而不影响整个系统
- 资源优化:卸载暂时不需要的插件释放内存
限制条件
- 插件开发必须提供
create_plugin和destroy_plugin导出函数 - 动态模块使用
dlopen的RTLD_LOCAL实现符号隔离 - 卸载插件时需要考虑依赖关系和正在运行的服务实例
调试示例
代码调试
#include <mc/module.h>
// 内建模块定义
MC_MODULE(mc_devices)
namespace mc::devices {
class sensor {
public:
void set_name(const std::string& name) { m_name = name; }
const std::string& get_name() const { return m_name; }
private:
std::string m_name{"default_sensor"};
};
}
MC_MODULE_REFLECT(mc_devices,
(mc::devices::sensor, "Sensor"),
((set_name, "setName"))
((get_name, "getName")))
// 在 .cpp 中实现内建模块
MC_BUILTIN_MODULE_IMPL(mc_devices);
// 使用模块
int main() {
auto devices_module = mc::load_module("mc.devices");
if (!devices_module) {
elog("无法加载设备模块");
return -1;
}
auto factory = devices_module->get_factory();
auto sensor_obj = factory->try_create_object("Sensor");
sensor_obj->invoke_method("setName", {mc::variant("温度传感器")});
return 0;
}2.6 D-Bus 通信
功能说明
D-Bus 模块提供对 D-Bus 协议的完整封装,支持 C++ 和 Lua 双层接口,实现类型安全的消息传递和信号订阅机制。支持同步和异步通信模式,基于 io_context 的事件驱动架构。
参数说明
方法参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 | 取值范围 |
|---|---|---|---|---|
| connection::open_system_bus(executor) | io_context& | connection | 打开系统总线连接 | 需要权限 |
| connection::open_session_bus(executor) | io_context& | connection | 打开会话总线连接 | 需要 D-Bus 守护进程运行 |
| conn.request_name(name, flags) | string, uint32 | tuple<bool, optional<error>> | 请求总线名称 | 格式如 org.example.Service |
| conn.send(msg) | message | bool | 异步发送消息 | 不等待回复 |
| conn.send_with_reply_and_block(msg, timeout) | message, duration | message | 同步发送并阻塞等待回复 | timeout 默认 25s |
| conn.async_send_with_reply(msg, timeout) | message, duration | future<message> | 异步发送并返回 future | 非阻塞模式 |
| conn.add_match(rule, cb, id) | match_rule, callback, uint64 | void | 添加信号匹配并订阅 | 信号订阅 |
| conn.register_path(path, handler) | string, handler | void | 注册对象路径处理器 | 用于提供 D-Bus 方法 |
返回值与异常
| 返回值 | 含义 | 触发条件 | 处理建议 |
|---|---|---|---|
| {true, nullopt} | 请求名称成功 | 名称未被占用 | 无 |
| {false, error} | 请求名称失败 | 名称冲突、权限不足 | 检查 error.message |
| future.get() | 获取异步回复 | 异步操作完成 | 超时处理 |
应用场景
- BMC 组件间进程间通信
- 信号订阅(如 NameOwnerChanged)
- Redfish 数据的 D-Bus 属性查询
- 系统服务之间的方法调用
限制条件
- 依赖 D-Bus 守护进程正常运行
- 需要 libdbus-1 底层库支持
- 异步消息需要
io_context事件循环运行
调试示例
代码调试
#include <mc/dbus/connection.h>
#include <mc/dbus/message.h>
mc::runtime::thread_pool executor(4);
executor.start();
auto conn = mc::dbus::connection::open_session_bus(executor);
conn.start();
// 请求服务名称
auto [success, err_opt] = conn.request_name("org.example.MyService");
if (!success) {
std::cerr << "请求名称失败" << std::endl;
}
// 同步调用
auto msg = mc::dbus::message::new_method_call(
"org.freedesktop.DBus", "/org/freedesktop/DBus",
"org.freedesktop.DBus", "ListNames");
auto reply = conn.send_with_reply_and_block(msg, mc::seconds(5));
// 信号订阅
auto rule = mc::dbus::match_rule::new_signal("NameOwnerChanged", "org.freedesktop.DBus");
conn.add_match(rule, [](mc::dbus::message& msg) {
std::cout << "收到信号: " << msg.get_member() << std::endl;
}, 1);
// 命令行调试 D-Bus
// busctl introspect org.example.MyService /org/example/MyService
// busctl call org.example.MyService /org/example/MyService org.example.MyService MethodName2.7 MicroComponent 接口
功能说明
MicroComponent(微组件)是 openUBMC 的核心架构单元,每个 BMC 组件都继承自 micro_component_object,通过 7 大标准化 D-Bus 接口与系统进行交互。
| 接口 | D-Bus 接口名 | 功能描述 |
|---|---|---|
| 基础接口 | bmc.kepler.MicroComponent | 基础属性(名称/版本/作者等)和健康检查 |
| 配置管理 | bmc.kepler.MicroComponent.ConfigManage | 配置导入导出、备份恢复、校验 |
| 调试接口 | bmc.kepler.MicroComponent.Debug | 调试控制台、一键收集、日志级别动态设置 |
| 平滑重启 | bmc.kepler.MicroComponent.Reboot | Prepare → Process → Action → Cancel 四阶段流程 |
| 热复位 | bmc.kepler.MicroComponent.Reset | 支持 warm/cold/factory 三种复位类型 |
| 日志限流 | bmc.kepler.Release.Maintenance | 动态控制日志输出频率 |
| 性能统计 | bmc.kepler.MicroComponent.Performance | 统计信号/RPC 收发数量,输出到 CSV |
2.7.1 基础接口 (bmc.kepler.MicroComponent)
属性
| 属性名 | 类型 | 说明 |
|---|---|---|
| Pid | int32 | 进程 ID |
| Name | string | 组件名称 |
| Author | string | 作者信息 |
| Description | string | 组件描述 |
| License | string | 许可证信息 |
| Status | string | 组件状态 |
| Version | string | 版本号 |
方法参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 | 取值范围 |
|---|---|---|---|---|
| HealthCheck | 无 | int32 | 健康检查 | 0=健康, 负数=异常(-1=关键资源不可用, -2=网络不可用) |
使用示例
class my_component_object : public mc::app::micro_component_object {
public:
void init(mc::string_view service_name) {
mc::app::micro_component_object::init(service_name);
m_mc_iface.m_status = "Running";
}
};
class my_service : public mc::engine::service {
public:
int32_t on_health_check() const override {
if (check_resources()) return 0; // 健康
return -1; // 不健康
}
};2.7.2 配置管理接口 (bmc.kepler.MicroComponent.ConfigManage)
方法参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 |
|---|---|---|---|
| Export(type) | string | string | 导出配置,返回 JSON 字符串 |
| Import(data, type) | string, string | void | 导入配置 |
| Backup(filepath) | string | [(string, string)] | 备份配置,返回备份文件列表 |
| Recover(preserve_list) | map<string, string> | void | 恢复配置 |
| Verify(data) | string | string | 校验配置,返回校验结果 JSON |
| GetPreservedConfig(preserve_flag) | map<string, string> | string | 获取保留配置 |
| GetTrustedConfig | 无 | string | 获取信任配置 |
使用示例
mc::string on_export_config(mc::string type) override {
mc::json config;
config["my_setting"] = get_my_setting();
return mc::string(config.dump(2));
}
void on_import_config(mc::string data, mc::string type) override {
auto config = mc::json::parse(data);
if (config.contains("my_setting")) {
set_my_setting(config["my_setting"]);
}
}2.7.3 调试接口 (bmc.kepler.MicroComponent.Debug)
属性
| 属性名 | 类型 | 说明 |
|---|---|---|
| DlogLevel | string | 调试日志级别(debug/info/notice/warn/error) |
| DlogType | string | 调试日志类型(file/remote/local) |
方法参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 |
|---|---|---|---|
| SetDlogLevel(level, hours) | string, uint8 | void | 设置调试日志级别及有效时长(小时) |
| Dump(filepath) | string | void | 一键收集调试信息到指定目录 |
| AttachDebugConsole(port) | uint32 | void | 连接调试控制台 |
| DetachDebugConsole | 无 | void | 断开调试控制台 |
使用示例
void on_dump(mc::string filepath) override {
mc::string my_info = mc::string::concat(filepath, "/my_info.json");
mc::json info;
info["connections"] = get_connection_count();
mc::filesystem::write_file(my_info, info.dump(2));
}2.7.4 平滑重启接口 (bmc.kepler.MicroComponent.Reboot)
方法参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 |
|---|---|---|---|
| Prepare | 无 | int32 | 准备阶段:完成数据持久化 |
| Process | 无 | int32 | 处理阶段:执行业务逻辑 |
| Action | 无 | int32 | 执行阶段:完成最终操作 |
| Cancel | 无 | void | 取消阶段:回滚所有操作 |
处理流程:Normal → Prepare → Process → Action → Normal,任意阶段失败可通过 Cancel 回滚。
使用示例
int32_t on_reboot_prepare() override {
save_critical_data();
flush_pending_operations();
return 0;
}
void on_reboot_cancel() override {
rollback_changes();
}2.7.5 热复位接口 (bmc.kepler.MicroComponent.Reset)
方法参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 |
|---|---|---|---|
| Prepare(reset_type) | string | int32 | 准备阶段 |
| Action(reset_type) | string | int32 | 执行阶段 |
| Cancel(reset_type) | string | void | 取消阶段 |
复位类型:warm(热复位)、cold(冷复位)、factory(恢复出厂设置)。
2.7.6 日志限流接口 (bmc.kepler.Release.Maintenance)
方法参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 |
|---|---|---|---|
| DlogLimit(enabled, duration_mins) | bool, uint8 | void | 设置日志限流状态和时长(分钟) |
enabled=true:启用日志限流(默认状态)enabled=false, duration_mins=N:禁用限流 N 分钟后自动恢复enabled=false, duration_mins=0:永久禁用限流
2.7.7 性能统计接口 (bmc.kepler.MicroComponent.Performance)
方法参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 |
|---|---|---|---|
| StartProfiling(duration) | uint32 | void | 启动性能统计,指定时长(分钟) |
统计项:sent_signals(发送信号数)、received_signals(接收信号数)、sent_rpcs(发送 RPC 数)、received_rpcs(接收 RPC 数)。结果存储于 /dev/shm/profiling.csv。
返回值与异常
| 返回值 | 含义 | 触发条件 | 处理建议 |
|---|---|---|---|
| 0 | 健康/成功 | 组件运行正常 | 无 |
| -1 | 不健康/失败 | 关键资源不可用 | 检查组件状态和日志 |
| -2 | 网络不可用 | 网络连接异常 | 检查网络配置 |
应用场景
- BMC 组件的标准化开发和运行管理
- 通过
mdbctl命令行工具进行远程调试和诊断 - 组件健康状态监控与告警
- 平滑重启场景下的数据持久化和恢复
- 生产环境日志限流与性能分析
调试示例
命令行调试
# 查看组件信息
busctl introspect org.openubmc.my_component /bmc/kepler/my_component/MicroComponent
# 健康检查
busctl call org.openubmc.my_component \
/bmc/kepler/my_component/MicroComponent \
bmc.kepler.MicroComponent HealthCheck
# 设置调试日志级别(有效期1小时)
busctl call org.openubmc.my_component \
/bmc/kepler/my_component/MicroComponent \
bmc.kepler.MicroComponent.Debug SetDlogLevel a{ss}sy 0 debug 1
# 一键收集调试信息
busctl call org.openubmc.my_component \
/bmc/kepler/my_component/MicroComponent \
bmc.kepler.MicroComponent.Debug Dump s /tmp/my_dump
# 启动性能统计(60分钟)
busctl call org.openubmc.my_component \
/bmc/kepler/my_component/MicroComponent \
bmc.kepler.MicroComponent.Performance StartProfiling q 60
# 禁用日志限流(30分钟后恢复)
busctl call org.openubmc.my_component \
/bmc/kepler/my_component/MicroComponent \
bmc.kepler.Release.Maintenance DlogLimit by 0 30
# 使用 mdbctl 工具
mdbctl set-log-level --service org.openubmc.my_component --level debug --hours 1
mdbctl dump --service org.openubmc.my_component --path /tmp/my_dump
mdbctl profiling --service org.openubmc.my_component --duration 602.8 数据校验 API
功能说明
mc::validate::validator 提供面向 C++ 的通用参数校验能力,支持对对象属性数据的类型、取值范围、字符串长度、正则格式和 JSON 格式进行语义和语法校验。校验失败时抛出分类明确的异常,异常消息格式与 Lua 模块 lvalidate 保持一致,便于 C++ 与 Lua 两侧复用同一套校验规则。
参数说明
方法参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 | 取值范围 |
|---|---|---|---|---|
| check_integer(name, val, min, max, need_convert) | string_view, double, double, double, bool | void | 校验整数值及范围 | val 须为整数值且在 [min, max] 内 |
| ranges(name, val, min, max, need_convert, allow_nil) | string_view, double, double, double, bool, bool | void | 校验数值范围 | val 在 [min, max] 内 |
| lens(name, val, min, max, need_convert, allow_nil) | string_view, string_view, int, int, bool, bool | void | 校验字符串长度 | 长度在 [min, max] 内 |
| regex(name, val, pattern, need_convert, allow_nil) | string_view, string_view, string_view, bool, bool | void | 校验正则匹配 | pattern 为 GLib 正则表达式 |
| json(val) | string_view | void | 校验 JSON 格式 | 合法 JSON 字符串 |
need_convert控制错误消息格式是否与 Lualvalidate一致。allow_nil用于对齐 Lua 的nil语义。
返回值与异常
| 异常类型 | 含义 | 触发条件 | 处理建议 |
|---|---|---|---|
property_value_type_error | 类型不符合 | 期望整数但传入浮点数或 NaN | 检查属性值类型,确保传入整数值 |
property_value_out_of_range | 数值超出范围 | val 不在 [min, max] 区间 | 检查属性取值范围,确保在合法范围内 |
string_length_error | 字符串长度不满足约束 | 字符串长度不在 [min, max] 区间 | 检查字符串长度限制 |
format_error | 格式不匹配 | 正则不匹配或正则编译失败 | 检查正则表达式和待校验字符串 |
json_error | JSON 解析失败 | JSON 字符串格式非法 | 检查 JSON 字符串语法 |
所有异常均继承自 mc::validate::validation_exception,并最终继承 std::runtime_error。
应用场景
- Redfish 接口入参校验(如 PATCH 请求的属性值合法性检查)
- 配置文件加载后的合法性验证
- D-Bus 方法调用参数的预处理校验
- 与 Lua 模块配合,实现 C++/Lua 统一的校验规则
限制条件
- 正则匹配基于 GLib 的
GRegex,需链接 glib 库 allow_nil参数用于 Lua 互操作,C++ 侧无nil概念,建议使用std::optional<T>或调用前自行判断
调试示例
代码调试
#include <mc/validate/validator.h>
void validate_request(std::string_view user, double age, std::string_view payload) {
// 校验整数 + 范围:age 必须为 0-200 的整数
mc::validate::validator::check_integer("Age", age, 0, 200, false);
// 校验字符串长度:用户名 1-64 字符
mc::validate::validator::lens("User", user, 1, 64, false);
// 校验 JSON 格式
mc::validate::validator::json(payload);
// 校验正则:MAC 地址格式
mc::validate::validator::regex("MAC", "AA:BB:CC:DD:EE:FF",
"^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$", false);
}
// 异常处理
try {
mc::validate::validator::check_integer("Prop1", 3.14, 0, 100, false);
} catch (const mc::validate::validation_exception& e) {
// e.what() 的文案与 Lua lvalidate 报错保持一致
elog("校验失败: ${error}", ("error", e.what()));
}2.9 对象代理访问 API
功能说明
proxy_object 类提供了 D-Bus 对象的代理封装,允许通过 C++ 接口访问远程 D-Bus 对象的属性和方法。该类封装了 D-Bus 通信细节,提供类型安全的属性访问(获取/设置/批量查询)和方法调用(同步/安全调用)接口,是构建对象和属性联接的核心组件。
参数说明
属性参数说明
| 属性名 | 类型 | 读写 | 说明 |
|---|---|---|---|
| service | string | 只读 | 目标 D-Bus 服务名称 |
| path | string | 只读 | 目标 D-Bus 对象路径 |
| interface | string | 只读 | 目标 D-Bus 接口名称 |
方法参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 | 取值范围 |
|---|---|---|---|---|
| get_property(name) | string | mc::variant | 获取单个属性值 | 属性名 |
| set_property(name, value) | string, mc::variant | void | 设置属性值(自动类型校验) | value 类型需匹配接口定义 |
| get_all_properties() | void | mc::dict | 获取所有属性值 | 返回属性名→值的字典 |
| get_properties(prop_names) | mc::variants | pair<mc::dict, mc::dict> | 批量获取指定属性 | 返回(成功字典, 错误字典) |
| call_method(name, args) | string, mc::variant | mc::variants | 调用 D-Bus 方法(抛异常) | args 须为数组类型 variant |
| call_method_pcall(name, args, error) | string, mc::variant, optional<string>& | mc::variants | 安全调用方法(不抛异常) | 失败时 error 包含错误信息 |
| has_property(name) | string | bool | 检查属性是否存在 | — |
| has_method(name) | string | bool | 检查方法是否存在 | — |
| is_volatile(name) | string | bool | 检查属性是否为易变属性 | — |
返回值与异常
| 返回值 | 含义 | 触发条件 | 处理建议 |
|---|---|---|---|
| get_property 成功返回 variant | 属性值获取成功 | 属性存在且目标服务可访问 | 使用 as<T>() 转换到目标类型 |
| mc::exception 异常 | 属性不存在 | 属性名未在接口中定义 | 使用 has_property() 预先检查 |
| mc::exception 异常 | 方法不存在 | 方法名未在接口中定义 | 使用 has_method() 预先检查 |
| call_method_pcall 返回空列表 | 方法调用失败 | 参数类型不匹配或服务端错误 | 检查 error 参数获取详细信息 |
应用场景
- 在 MDB(管理数据库)中访问其他 BMC 组件的 D-Bus 对象属性
- Redfish 资源查询时通过代理对象获取底层硬件状态
- 跨服务的方法调用(如更新固件、查询传感器数据)
- 批量读取对象属性以提高性能(替代多次 get_property 调用)
限制条件
proxy_object不是线程安全的,多线程环境需外部同步- 每次属性访问和方法调用都会进行 D-Bus 通信,高频场景建议使用
get_all_properties批量获取 - 依赖底层
sd_bus连接,需确保 D-Bus 守护进程正常运行
调试示例
代码调试
// 代理对象通过代理接口实现,示例如下
namespace mc::app::discovery {
class object_group_proxy : public mc::engine::interface_proxy<object_group_proxy> {
public:
MC_INTERFACE_PROXY("bmc.kepler.ObjectGroup");
MC_PROXY_METHOD(get_binary_objects_response, GetBinaryObjects, "a{ss}s",
((mc::dict, filters))((mc::string, owner)));
};
}2.10 任务管理机制
功能说明
mc::app::task::TaskMgmt 提供通用的任务资源管理机制,遵循 Redfish TaskService 规范,支持任务的创建、进度跟踪、状态更新和自动回收。任务以 D-Bus 对象的形式暴露(接口 bmc.kepler.TaskService.Task),便于外部系统通过 D-Bus 查询和监控异步任务的执行状态。
参数说明
属性参数说明
任务 D-Bus 对象包含以下属性:
| 属性名 | 类型 | 读写 | 说明 |
|---|---|---|---|
| Id | uint32 | 只读 | 任务唯一标识 |
| Name | string | 只读 | 任务名称 |
| StartTime | string | 只读 | 开始时间(ISO 8601) |
| EndTime | string | 只读 | 结束时间(ISO 8601) |
| State | string | 读写 | 任务状态(New/Running/Completed/Exception/Cancelled 等) |
| Status | string | 读写 | 健康状态(OK/Warning/Error/Critical) |
| Progress | uint32 | 读写 | 进度百分比(0-100) |
| EstimatedDuration | uint32 | 读写 | 预计持续时间(分钟) |
| Stage | string | 读写 | 当前阶段描述 |
| Description | string | 读写 | 任务描述 |
| MessageId | string | 读写 | 消息 ID |
| Parameters | string | 读写 | 任务参数(JSON) |
任务状态枚举
| 状态 | 说明 |
|---|---|
| New | 初始状态,任务刚创建 |
| Starting | 任务正在启动 |
| Running | 任务正在执行 |
| Suspended | 任务已挂起 |
| Interrupted | 任务被中断 |
| Pending | 任务等待中 |
| Stopping | 任务正在停止 |
| Completed | 任务已完成 |
| Killed | 任务被杀死 |
| Exception | 任务异常 |
| Cancelled | 任务已取消 |
自动关联规则:设置
State = Completed时自动将Progress设为 100;设置Progress = 100时自动将State设为Completed。
方法参数说明
| 方法名 | 入参类型 | 出参类型 | 描述 |
|---|---|---|---|
| instance() | void | TaskMgmt& | 获取单例实例 |
| init(service) | dbus::service* | void | 初始化任务管理器(使用前必须调用一次) |
| create_task(name, parent_path, timeout_mins, timeout_cb, props) | string, string, optional<int>, optional<callback>, optional<dict> | tuple<CreateResult, string, task_id> | 创建任务,返回(结果, 错误信息, 任务ID) |
| update_task(id, data) | task_id, dict | UpdateResult | 更新任务属性(Progress/State/Status 等) |
| destroy_task(id) | task_id | void | 销毁任务,释放 D-Bus 对象 |
| get_task_obj(id) | task_id | task_object* | 获取任务对象指针 |
| get_task_count() | void | size_t | 获取当前任务数量 |
| get_all_task_ids() | void | vector<task_id> | 获取所有任务 ID 列表 |
| set_auto_delete_timeout(mins) | int | void | 设置已结束任务的自动删除超时(10-1440 分钟,默认 10 分钟) |
返回值与异常
| 返回值 | 含义 | 触发条件 | 处理建议 |
|---|---|---|---|
| CreateResult::Success (0) | 任务创建成功 | 任务数量未达上限、路径合法 | task_id 可用于后续操作 |
| CreateResult::MaxTasksReached (-1) | 创建失败 | 任务数达上限(32)、未初始化或路径无效 | 等待已有任务完成或检查 parent_path 格式 |
| CreateResult::TimeoutOutOfRange (-2) | 创建失败 | 超时值为负数 | 使用非负数超时时间 |
| UpdateResult::Success (0) | 更新成功 | 任务存在、数据合法 | — |
| UpdateResult::TaskNotFound (-1) | 更新失败 | 任务 ID 不存在 | 检查任务是否已被销毁 |
| UpdateResult::TaskEnded (-9) | 更新失败 | 任务已结束(Completed/Exception/Cancelled) | 已结束任务不可更新 |
生命周期
create_task() → [New] → update_task(State=Running) → [Running]
→ update_task(Progress=100) 或 update_task(State=Completed/Exception/Cancelled)
→ [已结束, alive=false] → destroy_timer 触发(默认 10 分钟后)→ 自动释放关键机制:
- 超时自动结束:创建时指定
timeout_minutes,超时后自动将任务标记为已结束 - 延迟销毁:任务结束后默认 10 分钟后自动释放 D-Bus 对象和内部资源
- 满时回收:任务数量达到上限(32)时自动清理最早结束的任务
- 并发安全:全局
m_tasks_mutex保护任务表,每个 TaskEntry 有独立锁保护更新操作
应用场景
- 固件升级流程跟踪(创建升级任务 → 更新进度 → 完成/异常)
- 配置导入导出等长时间操作的进度报告
- Redfish TaskService 资源的标准实现
- 日志收集、诊断信息导出等异步操作的状态管理
限制条件
- 单次最多创建 32 个活跃任务
- 任务 parent_path 必须以
/开头,符合 D-Bus 对象路径规范 - 需在服务启动时调用
init()一次,传入 D-Bus service 指针
调试示例
代码调试
#include <mc/app/task/task_mgmt.h>
auto& mgmt = mc::app::task::TaskMgmt::instance();
mgmt.init(&service); // 首次初始化
// 创建固件升级任务
auto [result, error, task_id] = mgmt.create_task(
"FirmwareUpdate", // 任务名称
"/bmc/kepler/UpdateService", // 父路径(必须以'/'开头)
60, // 超时 60 分钟
nullptr, // 超时回调(可选)
{{"EstimatedDuration", 10}, {"Description", "BMC固件升级"}} // 初始属性
);
if (result == mc::app::task::CreateResult::Success) {
// 启动任务
mgmt.update_task(task_id, {{"State", "Running"}, {"Stage", "验证镜像"}});
// 更新进度
for (int i = 0; i <= 100; i += 10) {
mgmt.update_task(task_id, {{"Progress", i}});
// 执行实际升级工作...
}
// 完成
mgmt.update_task(task_id, {{"State", "Completed"}}); // Progress 自动设为 100
}
// D-Bus 路径: /bmc/kepler/UpdateService/TaskService/Tasks/{task_id}命令行调试 D-Bus
# 查看任务列表
busctl introspect org.openubmc.my_component /bmc/kepler/UpdateService/TaskService
# 查看任务属性
busctl introspect org.openubmc.my_component /bmc/kepler/UpdateService/TaskService/Tasks/1
# 查询单个属性
busctl get-property org.openubmc.my_component \
/bmc/kepler/UpdateService/TaskService/Tasks/1 \
bmc.kepler.TaskService.Task Progress2.11 业务对象框架
功能说明
业务对象框架是 libmcpp 的核心编程模型,提供从接口定义、对象声明、生命周期管理到实例注册上树的完整能力。开发者通过继承 mc::app::object<T> 定义业务对象,使用 MC_OBJECT / MC_INTERFACE 宏声明 D-Bus 元数据,框架自动管理对象的 6 个生命周期阶段(init → post_init → register → ready → unregister → unready),并通过 service::register_object() 将对象注册到服务对象树中。
核心概念
接口定义(mc::engine::interface)
每个业务对象可包含多个接口,每个接口对应一个 D-Bus 接口定义:
class sensor_iface : public mc::engine::interface<sensor_iface> {
public:
MC_INTERFACE("bmc.example.Sensor") // D-Bus 接口名称和版本
double Value{0.0};
mc::string Unit;
};
MC_REFLECT(sensor_iface, ((Value, "Value"))((Unit, "Unit")))对象声明(mc::app::object<T>)
通过 MC_OBJECT 宏声明对象的类名、D-Bus 路径模板和接口列表:
class sensor : public mc::app::object<sensor> {
public:
MC_OBJECT(sensor, "Sensor",
"/bmc/example/Sensors/${object_name}",
(sensor_iface))
sensor_iface iface;
void on_init(const mc::dict& props) override {
mc::engine::object_impl::on_init(props); // 通过反射回填属性
}
};
MC_REFLECT(sensor, ((iface, "iface")))对象生命周期钩子
| 钩子 | 触发时机 | 信号状态 | 对象已注册 | D-Bus 可见 | 典型用途 |
|---|---|---|---|---|---|
on_init(props) | init 阶段,同步执行 | 信号抑制中 | ❌ | ❌ | 属性回填、设置默认值、声明 wait_for 依赖 |
on_post_init() | on_init 后、注册前,同步执行 | 信号可外发 | ❌ | ❌ | 派生属性计算、内部数据结构准备 |
on_register() | 加入对象树后,异步 post | 信号可外发 | ✅ | ❌ | 确认 D-Bus 路径、注册 match 规则 |
on_ready() | register 后自动触发,异步 post | 信号可外发 | ✅ | ✅ | 启动后台任务、发送业务信号 |
on_unready() | 需业务显式调用 unready() | 信号可外发 | ❌ | ❌ | 清理后台任务 |
on_unregister() | 从对象树移除前,异步 post | 信号可外发 | ❌ | ❌ | 释放资源、断开外部连接 |
对象注册与实例管理
对象通过 service::register_object() 注册到服务对象树,核心流程为:
- 将对象加入
service_object_table(以 D-Bus 路径为键) - 沿路径向上查找父对象,建立 owner 父子关系
- 触发
on_register→on_ready生命周期钩子 - 发出
InterfacesAddedD-Bus 信号(仅在 service started 后)
解注册通过 service::unregister_object(path) 对称执行:递归解注册子对象 → 从对象树移除 → 触发 on_unregister。
InterfacesAdded/InterfacesRemoved信号从父对象的 D-Bus 路径发出(遵循 D-Bus ObjectManager 规范)。
参数说明
对象注册方法
| 方法名 | 入参类型 | 出参类型 | 描述 |
|---|---|---|---|
| service::register_object(obj) | abstract_object& | void | 注册对象到服务对象树 |
| service::unregister_object(path) | string | void | 解注册指定路径的对象(递归移除子对象) |
| obj.register_object() | void | void | 对象自行注册(需已关联 service) |
| obj.unregister_object() | void | void | 对象自行解注册 |
| obj.defer_registration() | void | void | 延迟注册(dispatcher 不自动 register,业务自行决定时机) |
| obj.defer_ready() | void | void | 延迟就绪(on_register 后不自动 ready,业务显式调用 ready()) |
| obj.ready() | void | void | 使对象进入就绪状态(幂等安全) |
| obj.unready() | void | void | 使对象退出就绪状态 |
属性初始化方法
| 方法名 | 入参类型 | 出参类型 | 描述 |
|---|---|---|---|
| on_init(props) | const mc::dict& | void | 初始化回调,props 包含派发数据,信号抑制期内执行。业务不应该重写on_init接口 |
| get_property(name, default, index) | string, mc::variant, int | mc::variant | 获取初始化属性(支持默认值) |
| ensure_default(name, default) | string, T | void | 确保属性有默认值(属性为空时填充) |
返回值与异常
| 返回值 | 含义 | 触发条件 | 处理建议 |
|---|---|---|---|
| register_object 成功 | 对象已上树 | 路径合法、service 已注册 | 对象对 D-Bus 可见(service started 后) |
| MC_ASSERT 断言失败 | 路径非法 | 路径为空或不符合 D-Bus 对象路径规范 | 检查对象路径模板和实际展开值 |
| 对象未创建 | MC_OBJECT 类名不匹配 | 自发现场景下 class_name 未注册 | 确认 register_object_types() 中已调用 metadata() |
应用场景
- 定义 BMC 硬件组件的 D-Bus 对象(传感器、网络适配器、固件等)
- 自发现服务创建和管理硬件对象
- 对象间的父子层级关系管理
- 属性默认值填充和生命周期精细控制
调试示例
代码调试
#include <mc/engine/object.h>
#include <mc/engine/service.h>
// 1. 定义接口
class my_iface : public mc::engine::interface<my_iface> {
public:
MC_INTERFACE("bmc.example.MyInterface")
mc::string Name;
int Value{0};
};
MC_REFLECT(my_iface, ((Name, "Name"))((Value, "Value")))
// 2. 定义业务对象
class my_object : public mc::app::object<my_object> {
public:
MC_OBJECT(my_object, "MyObject",
"/bmc/example/MyObjects/${object_name}",
(my_iface))
explicit my_object(mc::engine::core_object* parent = nullptr)
: mc::app::object<my_object>(parent) {}
my_iface iface;
protected:
void on_init(const mc::dict& props) override {
// 信号抑制期:设置默认值、回填属性
ensure_default("Name", mc::string("default"));
ensure_default("Value", 0);
mc::engine::object_impl::on_init(props);
}
void on_register() override {
// 对象已上树,获取 D-Bus 路径
ilog("对象已注册: ${path}", ("path", get_object_path()));
}
void on_ready() override {
// 对象完全就绪,InterfacesAdded 已发出
ilog("对象已就绪: ${path}", ("path", get_object_path()));
// 可启动后台轮询、订阅事件等
}
void on_unregister() override {
// 清理资源
ilog("对象已移除: ${path}", ("path", get_object_path()));
}
};
MC_REFLECT(my_object, ((iface, "iface")))
// 3. 注册对象
void register_objects(mc::engine::service& svc) {
auto obj = mc::make_shared<my_object>(nullptr);
obj->init(props); // 触发 on_init → on_post_init
obj->set_object_path("/bmc/example/MyObjects/obj_1");
svc.register_object(*obj); // 触发 on_register → on_ready
}命令行调试 D-Bus
# 确认对象已注册
busctl introspect org.openubmc.my_service /bmc/example/MyObjects/obj_1
# 查看对象属性
busctl get-property org.openubmc.my_service \
/bmc/example/MyObjects/obj_1 \
bmc.example.MyInterface Name
# 监听对象 InterfacesAdded/Removed 信号
busctl monitor --match "type='signal',interface='org.freedesktop.DBus.ObjectManager'"3. 组件扩展案例
3.1 扩展能力概述
libmcpp 框架提供以下扩展能力,允许开发者根据业务需求进行定制和扩展:
- 自定义服务开发:通过继承
mc::service_base实现业务服务,注册到服务工厂后由框架统一管理生命周期(init → start → run → stop → cleanup) - 自定义插件开发:通过继承
mc::plugin实现插件模块,支持内建模块(静态链接)和动态模块(dlopen 加载.so文件)两种方式 - 自发现服务开发:通过继承
mc::app::discovery_service实现自发现服务,自动接收 hwdiscovery 派发的对象组并创建业务对象 - 自定义异常类型:通过继承
mc::exception定义业务特定异常,注册到异常工厂(exception_factory)后支持动态创建、序列化和跨模块传递
3.2 扩展点说明
| 扩展点 | 基类 | 注册方式 | 加载方式 | 适用场景 |
|---|---|---|---|---|
| 自定义服务 | mc::service_base<T> | factory.register_service<T>() | 插件内注册,框架管理生命周期 | 业务功能模块 |
| 自定义插件 | mc::plugin / mc::plugin_base<T> | app.register_plugin<T>() 或 MC_EXPORT_PLUGIN 宏 | 静态链接或 dlopen 动态加载 .so | 功能模块打包与热插拔 |
| 自发现服务 | mc::app::discovery_service | register_object_types() + metadata() | 继承 discovery_service,随应用启动 | 硬件对象自动发现与管理 |
| 自定义异常 | mc::exception | exception_factory::instance().register_exception<T>() | 程序初始化时注册 | 业务异常分类处理与跨模块传递 |
3.3 二次开发指导
步骤一:自定义服务开发
通过继承 mc::service_base 实现自定义服务,定义服务类型名、配置选项和依赖关系:
class my_service : public mc::service_base<my_service> {
public:
static const char* service_type() { return "example.my_service"; }
static void register_options(mc::po::options_description& cfg_opts) {
cfg_opts.add_options()
("my_service.timeout", mc::po::value<int>()->default_value(30), "超时时间")
("my_service.retry_count", mc::po::value<int>()->default_value(3), "重试次数");
}
static const std::vector<std::string>& dependencies() {
static std::vector<std::string> deps = {"logger", "database"};
return deps;
}
bool init(mc::dict args) override {
m_timeout = args.get("my_service.timeout").as<int>();
return true;
}
bool start() override {
set_state(mc::service_state::running);
return true;
}
bool stop() override {
set_state(mc::service_state::stopped);
return true;
}
bool is_healthy() const override { return true; }
private:
int m_timeout;
};步骤二:自定义插件开发
通过继承 mc::plugin 实现自定义插件,注册服务类型并使用 MC_EXPORT_PLUGIN 导出:
class example_plugin : public mc::plugin {
public:
const mc::plugin_info& get_info() const override {
static mc::plugin_info info = {
.name = "example",
.version = "1.0.0",
.dependencies = {"core", "logger"}
};
return info;
}
bool init(mc::service_factory& factory) override {
factory.register_service<my_service>();
return true;
}
};
MC_EXPORT_PLUGIN(example_plugin)步骤三:自发现服务开发
通过继承 mc::app::discovery_service 实现自发现服务,完成业务对象的创建和生命周期管理:
class network_adapter_iface : public mc::engine::interface<network_adapter_iface> {
public:
MC_INTERFACE("bmc.kepler.Systems.NetworkAdapter")
std::uint8_t SlotNumber{0};
mc::string Name;
};
MC_REFLECT(network_adapter_iface, ((SlotNumber, "SlotNumber"))((Name, "Name")))
class network_adapter : public mc::app::object<network_adapter> {
public:
MC_OBJECT(network_adapter, "NetworkAdapter",
"/bmc/kepler/Systems/${SystemId}/NetworkAdapters/${object_name}",
(network_adapter_iface))
network_adapter_iface iface;
void on_init(const mc::dict& props) override {
mc::engine::object_impl::on_init(props);
}
void on_ready() override {
// 对象就绪,可启动后台任务
}
void on_unregister() override {
// 清理资源
}
};
MC_REFLECT(network_adapter, ((iface, "iface")))
class my_discovery_service : public mc::app::discovery_service {
public:
explicit my_discovery_service(mc::string name)
: mc::app::discovery_service(std::move(name)) {}
protected:
void register_object_types() override {
(void)network_adapter::metadata();
}
mc::app::discovery::service_object_group_source::config
make_service_source_config() const override {
auto cfg = mc::app::discovery_service::make_service_source_config();
cfg.get_binary_objects_owner = "network_adapter";
return cfg;
}
};步骤四:自定义异常类型
通过继承 mc::exception 定义自定义异常,并注册到异常工厂:
class my_exception : public mc::exception {
public:
enum code_enum { code_value = MY_EXCEPTION_CODE };
my_exception(mc::log_message&& msg = mc::log_message(
mc::log::level::error, "我的异常"))
: exception(std::move(msg), code_value, "my_exception", "我的异常描述") {}
my_exception(const my_exception& e) : exception(e) {}
my_exception(my_exception&& e) : exception(std::move(e)) {}
explicit my_exception(const mc::exception& e) : exception(e) {}
std::shared_ptr<mc::exception> dynamic_copy_exception() const override {
return std::make_shared<my_exception>(*this);
}
void dynamic_rethrow_exception() const override { throw *this; }
};
// 注册异常类型
mc::exception_factory::instance().register_exception<my_exception>();3.4 自发现加载与卸载机制
自发现服务通过 dispatcher 自动管理业务对象的加载(发现 → 创建 → 注册)和卸载(消失 → 解注册 → 销毁)全生命周期:
加载流程(对象出现时):
hwdiscovery 派发 ObjectGroup 信号
→ service_object_group_source 接收数据
→ dispatcher::on_source_update() 编排对象
→ dispatcher::dispatch_object():解码属性 → factory_hook.create() → obj.init(props)
→ register_or_defer():
├─ [无依赖] 立即 service->register_object() → 对象上树
├─ [有 required 依赖] 登记到 dependency_tracker,依赖满足后 register
└─ [defer_registration] 业务自行决定注册时机
→ on_register → on_ready(生命周期钩子链)卸载流程(对象消失时):
hwdiscovery 派发 ObjectGroup 信号(该对象不再出现)
→ dispatcher::on_source_update() 发现对象缺失
→ dispatcher::on_object_disappear(key)
→ service->unregister_object(path)
├─ 递归解注册所有子对象
├─ 触发 on_unregister() 生命周期钩子
├─ 从 object_table 移除
├─ 清除 owner 关系
└─ emit InterfacesRemoved D-Bus 信号三种注册策略:
| 条件 | 行为 | 说明 |
|---|---|---|
| 无 required 依赖 | 立即 service->register_object() | 最常见路径 |
| 有 required 依赖 | 登记到 tracker,依赖满足后回调注册 | 确保依赖对象先注册 |
业务调 defer_registration() | dispatcher 不主动 register | 业务自行管理注册时机 |
依赖声明示例:
void on_init(const mc::dict& props) override {
// required: 依赖未满足时不 register
wait_for("#/External.RequiredService", mc::app::wait_kind::required);
// optional: 不阻塞 register
wait_for("#/External.OptionalService", mc::app::wait_kind::optional);
mc::engine::object_impl::on_init(props);
}关键要点:
- 自发现对象的
register_object()走的是与直接注册完全相同的核心链路 - 对象移除时自动递归解注册所有子对象
on_unregister()中应完成资源清理(关闭文件描述符、停止后台任务、断开连接)- 若业务实现了
on_unready()逻辑,需在on_unregister()中显式调用unready()
4. 日志说明
4.1 一键日志收集
| 文件路径 | 内容说明 |
|---|---|
| /var/log/app.log | 应用程序运行日志 |
| /var/log/mcpp/*.log | libmcpp 框架内部日志 |
| /tmp/dump_*/ | Dump 接口一键收集调试信息目录 |
| /dev/shm/profiling.csv | 性能统计数据文件 |
4.2 关键日志信息
| 日志片段 | 日志级别 | 含义解读 | 建议处理动作 |
|---|---|---|---|
NOTIC xxx on_init: ... path=... | NOTICE | 业务对象的 on_init 生命周期回调被调用 | 用于判断对象创建是否正常触发 |
NOTIC xxx on_register: ... path=... | NOTICE | 业务对象已注册到 D-Bus 对象表 | 确认对象注册成功 |
NOTIC xxx on_ready: ... path=... | NOTICE | 业务对象已完全就绪,InterfacesAdded 已发出 | 确认对象对外可见 |
NOTIC xxx on_unregister: ... path=... | NOTICE | 业务对象已从 D-Bus 移除 | 确认对象生命结束,检查资源清理 |
ERROR ... Connection timeout | ERROR | D-Bus 连接或方法调用超时 | 检查目标服务是否正常运行 |
ERROR ... request name failed | ERROR | D-Bus 名称请求失败 | 检查名称是否被占用或权限不足 |
WARN ... plugin load failed | WARN | 插件加载失败 | 检查 .so 文件是否存在、依赖是否满足 |
ERROR ... MC_THROW ... | ERROR | 异常被抛出 | 根据异常信息和错误码定位根因 |
5. 问题定界指南
5.1 典型问题定界
| 现象描述 | 是否为本组件问题 | 判断依据 | 关键证据收集方法 |
|---|---|---|---|
| 应用程序启动失败 | 可能是 | 检查配置文件和插件是否正常加载 | 查看启动日志,检查 config.json 语法 |
| 插件加载失败 | 是 | 插件 .so 文件不存在或依赖缺失 | 查看 PluginManager 日志,使用 ldd 检查依赖 |
| 服务无法启动 | 可能是 | 检查服务依赖是否满足、配置是否正确 | 查看 ServiceManager 日志,检查依赖关系 |
| D-Bus 方法调用超时 | 可能是 | 检查目标服务是否运行、D-Bus 守护进程状态 | 使用 busctl 检查服务可用性,查看 D-Bus 错误 |
| 日志未输出 | 是 | 检查日志级别配置和 Appender 配置 | 检查 LogManager 配置,确认日志级别阈值 |
| 共享内存数据库访问失败 | 是 | 检查共享内存权限和状态 | 使用 ipcs 查看共享内存段状态 |
| 自发现服务未收到对象组 | 可能是 | 检查 hwdiscovery 服务状态和 allowed_owners 配置 | 查看 D-Bus 信号总线,确认 ObjectGroup 信号 |
| 反射序列化结果不正确 | 是 | 检查 MC_REFLECT 宏注册是否正确 | 对比实际输出和预期 JSON 格式 |
| 组件健康检查失败 | 是 | HealthCheck 返回非 0 值 | 查看组件内部状态,检查关键资源 |
5.2 错误码速查表
libmcpp 框架异常通过 mc::exception 基类的 code() 方法返回异常代码,异常代码定义在 exception_code 枚举中。常见异常代码及排查建议如下:
| 错误码 | 含义 | 可能原因 | 排查建议 |
|---|---|---|---|
| 0 | 未知异常 | 未指定具体异常类型的通用错误 | 查看异常消息 e.what() 获取详细信息 |
| 1 | 未处理的第三方异常 | 捕获了标准库或第三方库的异常,未映射为框架异常 | 使用 MC_CAPTURE_AND_WRAP_EXCEPTION 包装第三方异常 |
| 2 | 超时异常 | D-Bus 调用超时、异步操作超时、网络请求超时 | 增大超时时间,检查目标服务是否正常运行 |
| 3 | 文件未找到 | 配置文件路径错误、插件 .so 文件缺失 | 检查文件路径,确认文件是否存在及权限是否正确 |
| 4 | 解析错误 | JSON 格式错误、配置文件语法错误、证书格式错误 | 使用 JSON 校验工具检查格式,对照配置模板修正 |
| 5 | 无效参数 | 方法调用传入了类型不匹配或范围不正确的参数 | 检查参数类型和取值范围,使用 variant::as<T>() 时确认类型 |
| 6 | 键未找到 | dict 中访问不存在的键、配置项缺失 | 使用 dict::get() 替代直接下标访问,或先检查 has() |
| 7 | 类型转换异常 | variant 类型不匹配的转换、std::bad_variant_access | 使用 variant::is<T>() 预先检查类型 |
| 8 | 越界异常 | 数组或容器访问超出范围 | 使用 at() 替代 [],检查容器大小 |
| 9 | 取消操作异常 | 异步任务被取消、用户主动终止操作 | 正常情况,检查取消操作的触发条件 |
| 10 | 断言异常 | MC_ASSERT 宏检查失败 | 检查断言条件,确认前置条件是否满足 |
| 14 | 无效操作异常 | 在不正确的状态下调用方法(如服务未启动时调用 stop) | 检查调用时序,确认对象当前状态是否允许该操作 |
| 21 | 繁忙异常 | 资源被占用、并发操作冲突 | 等待重试或检查资源锁状态 |
| 23 | 未实现异常 | 调用了未实现的方法或接口 | 检查虚函数是否正确重写,确认功能是否已实现 |
| 27 | 未找到异常 | 请求的服务、插件或对象不存在 | 检查名称是否拼写正确,确认目标是否已注册 |
| 31 | 权限不足 | D-Bus 操作权限不足、文件访问权限不足 | 检查 D-Bus 策略配置,确认文件权限 |
5.3 调试方法
开启调试日志
# 开启 libmcpp 框架详细日志
busctl call org.openubmc.my_component \
/bmc/kepler/my_component/MicroComponent \
bmc.kepler.MicroComponent.Debug SetDlogLevel a{ss}sy 0 debug 1
# 通过 mdbctl 设置
mdbctl set-log-level --service org.openubmc.my_component --level debug --hours 1
# 查看实时日志
tail -f /var/log/app.log | grep -E 'NOTIC|ERROR|WARN'
# 程序内设置日志级别
#include <mc/log.h>
mc::log::default_logger().set_level(mc::log::level::debug);D-Bus 调试方法
# 查看服务接口
busctl introspect org.openubmc.my_component /bmc/kepler/my_component/MicroComponent
# 查看系统总线上所有服务
busctl list
# 监听 D-Bus 信号
busctl monitor --match "type='signal'"
# 调用方法
busctl call org.openubmc.my_component \
/bmc/kepler/my_component/MicroComponent \
bmc.kepler.MicroComponent HealthCheck复现问题方法
前置条件设置:
- 确保 libmcpp 应用程序服务正常运行
- 确保 D-Bus 守护进程正常运行
- 配置文件路径正确
操作步骤:
- 使用
mdbctl set-log-level --level debug开启详细日志 - 执行触发问题的操作
- 使用
mdbctl dump --path /tmp/dump收集调试信息 - 分析日志和调试信息定位问题
预期结果:
- 日志显示完整的操作流程和异常信息
- dump 目录包含所有相关调试数据
6. 常见问题解答
Q1:为什么插件加载失败?
问题描述 应用程序启动时,插件管理器(PluginManager)报告插件加载失败,服务不可用。
一句话答案 检查插件 .so 文件是否存在、依赖库是否满足、版本兼容性是否匹配。
根因说明 可能原因包括:插件动态库文件路径不正确、插件依赖的其他 .so 库缺失或版本不匹配、插件签名验证失败、插件目录不存在或权限不足。
解决方案
- 确认插件 .so 文件存在于 PluginManager 的
plugin_dir目录 - 使用
ldd plugin.so检查插件的动态库依赖是否满足 - 检查插件版本与框架版本的兼容性
- 检查文件权限,确保应用程序有读取权限
- 开启 debug 日志查看详细的加载失败原因
- 确认插件 .so 文件存在于 PluginManager 的
规避方案 将核心功能实现为内建模块(静态链接),减少对动态加载的依赖。
适用版本 openUBMC 1.0.0 以上
Q2:D-Bus 方法调用超时怎么处理?
问题描述 使用
send_with_reply_and_block或async_send_with_reply调用 D-Bus 方法时超时,未收到回复。一句话答案 检查目标服务是否正在运行、方法名和接口名是否正确、超时时间是否足够。
根因说明 目标服务可能未启动或已崩溃、方法调用参数格式错误导致服务端处理异常、超时时间设置过短、
io_context事件循环未运行导致消息未分发。解决方案
- 使用
busctl list确认目标服务在 D-Bus 总线上注册 - 使用
busctl introspect确认方法名和接口名正确 - 检查
io_context是否正确启动(executor.start()) - 适当增大超时时间(如
mc::seconds(30)) - 使用
busctl monitor观察消息是否发送成功
- 使用
规避方案 使用异步模式
async_send_with_reply配合 future,避免阻塞主线程。适用版本 openUBMC 1.0.0 以上
Q3:日志没有输出到期望的文件?
问题描述 代码中调用了日志宏(ilog、elog 等),但日志文件未生成或没有内容。
一句话答案 检查 LogManager 配置、Appender 注册和日志级别阈值。
根因说明 可能原因:未注册对应的 Appender、日志级别阈值高于当前日志级别导致被过滤、日志文件路径无写入权限、异步日志缓冲区未刷新。
解决方案
- 确认已调用
MC_LOG_REGISTER_APPENDER注册 Appender - 确认已调用
setLevel()设置合适的日志级别 - 检查日志文件路径的写入权限
- 使用
ilog("系统启动")进行测试 - 程序退出前确保调用日志系统的 flush 或清理操作
- 确认已调用
规避方案 在开发阶段使用 ConsoleAppender 直接输出到控制台,避免文件权限问题。
适用版本 openUBMC 1.0.0 以上
Q4:自发现服务没有创建业务对象?
问题描述 自发现服务(discovery_service)启动后,hwdiscovery 已派发对象组,但业务对象未被创建。
一句话答案 检查
register_object_types()是否正确注册、MC_OBJECT的 class_name 是否与派发数据匹配。根因说明 可能原因:
register_object_types()未调用业务对象的metadata()方法、MC_OBJECT中声明的类名与 hwdiscovery 派发的class_name不匹配、allowed_owners()限制了来源但实际来源不在列表中、get_binary_objects_owner配置不正确。解决方案
- 确认
register_object_types()中已调用所有业务对象的metadata() - 核对
MC_OBJECT的类名(第二个参数)与 hwdiscovery 派发的class_name一致 - 检查
allowed_owners()返回值的空或包含正确的来源 - 确认
get_binary_objects_owner配置的组件名与 hwdiscovery 注册的一致
- 确认
规避方案 使用
busctl monitor监听 ObjectGroup 信号,确认 hwdiscovery 确实在发送对象组。适用版本 openUBMC 1.0.0 以上
Q5:如何新增一个自定义异常类型?
问题描述 需要定义组件特定的异常类型以便更好地分类和处理错误。
一句话答案 继承
mc::exception并注册到异常工厂。根因说明 框架提供统一的异常处理机制,自定义异常需要遵循框架的异常层次结构,提供动态拷贝和重新抛出的能力。
解决方案
- 定义自定义异常类继承
mc::exception - 提供自定义的
exception_code枚举值 - 实现
dynamic_copy_exception()和dynamic_rethrow_exception()方法 - 在程序初始化时调用
exception_factory::instance().register_exception<T>()注册 - 使用
MC_THROW(my_exception, "消息")抛出异常
- 定义自定义异常类继承
规避方案 如果不需要在异常工厂中动态创建异常,可以直接抛出而不注册。
适用版本 openUBMC 1.0.0 以上