libsomp

版本信息

项目内容
组件版本3.10.15
首发版本openUBMC 26.09
文档作者openUBMC 社区
最后更新2026-09-15
许可证Mulan PSL v2

1. 组件概述

1.1 组件简介

libsomp 是 openUBMC 微组件框架中的共享内存库,mds/service.jsontypelibrarydescriptionlibrary of shm for micro component framework。编译产物为共享库 libsomp.so(CMake 目标名 somp),安装到 usr/lib64,权限配置见 dist/permissions.iniusr/lib64/libsomp.so,mode 554,uid/gid 0)。

组件不启动独立服务进程、不注册资源协作接口(无 model.jsonmds/service.jsonrequired 为空),也不提供 IPMI 命令或 Lua 模块入口。公开能力分为两部分:

  • shmlock:跨进程对象级读写锁(C API + C++ RAII 封装)。
  • shm / D-Bus 基础设施:MDB 对象树、信号匹配、IPMI 路由表、消息队列等共享内存数据结构,供微组件框架在进程间共享对象元数据。

构建依赖见 mds/service.jsonboost/1.87.0.b004@openubmc/stablehuawei_secure_c/[>=1.0.0]@openubmc/stableliblogger/[>=1.80.5];CMake 还链接 glib-2.0dbus-1。Conan 包对外导出 libs = ["somp"]libdirs = ["usr/lib64"]

1.2 解决什么问题

多进程 BMC 服务需要在共享内存中同步对象访问,并共享 D-Bus/MDB 对象树。直接使用 System V 信号量或 Boost.Interprocess 需要自行处理进程崩溃残留锁、PID 重用、服务心跳和对象路径树。libsomp 提供:

  • object_id 为粒度的读写锁,带所有者跟踪与健康检查。
  • 进程指纹(PID + 启动时间 + 随机 token)避免 PID 重用误判。
  • C++ LockHandle RAII,避免漏释放。
  • 共享内存中的 MDB 对象树、接口/属性/方法/信号、Match 规则和 IPMI 路由。

1.3 核心功能

  • 对象级读写锁:阻塞获取、非阻塞尝试、写锁降级为读锁、批量读锁。
  • 进程/服务注册与注销;可选心跳,超时未心跳的服务视为死亡。
  • 健康监控线程:清理死亡进程残留锁;陈旧锁阈值见 SHM_STALE_LOCK_THRESHOLD_SEC(30 秒)。
  • 单例 ShmLockManager:打开或创建段 MDBUS_SHM_LOCK_MANAGER(默认 10 MiB,环境变量 MDBUS_SHM_LOCK_MANAGER_SIZE,上限 100 MiB)。
  • 单例 shm::shared_memory:打开或创建段 MDBUS_SHM(默认 90 MiB,测试构建 50 MiB;环境变量 MDBUS_SHM_SIZE,上限 200 MiB)。
  • D-Bus Match 规则树(进程内 DBus::Match::Matchs 与共享内存 shm::matchs)。
  • 路径分段与忽略大小写比较工具 DBus::utils

1.4 关键术语表

术语解释
object_id锁保护的对象标识。GLOBAL_LOCK_OBJECT_ID1,预留为全局对象锁。
ServiceId / shm_service_id_t逻辑进程 ID、进程内 service_id、incarnation 组成的服务标识。
LockHandle / shm_lock_handle_t一次加锁结果;C++ 析构时调用 shm_lock_release
进程指纹pid + start_time + random_token + sequence,用于区分 PID 重用。
incarnation服务实例代数,用于识别服务重启。
MDBUS_SHMBoost.Interprocess 共享内存段名,路径 /dev/shm/MDBUS_SHM
MDBUS_SHM_LOCK_MANAGER锁管理器共享内存段名,路径 /dev/shm/MDBUS_SHM_LOCK_MANAGER
well-known name不以 : 开头的总线名;is_wellknow_name() 按此判断。
RAIILockHandle 离开作用域自动释放锁。

1.5 外部交互边界图

仓库中没有 model.jsonipmi.json。以下接口均为进程内 C/C++ API,不能通过 busctl 调用。

2. API 使用说明与示例

头文件安装路径:

类别头文件说明
C 锁 APIshmlock/shmlock.hshmlock/shmlock_constants.h错误码、容量常量和全部 C 函数。
C++ 锁 APIshmlock/shmlock_manager.h转发到 shmlock_manager_core.h,导出 LockManagerShmLockManager
C++ 辅助类型shmlock/lock_handle.hservice_id.hlock_stats.hshmlock_exception.hRAII 句柄、服务 ID、统计、LockException
共享内存对象树dbus/shm_tree/*.hshared_memoryobjecttreemessage_queueipmi_route_table 等。
Matchdbus/match/rule.hmatchs.hnode.hD-Bus 匹配规则。
工具dbus/utils/utils.h路径分段、忽略大小写比较。

容量与默认超时(shmlock.h / shmlock_constants.h):

取值含义
SHM_MAX_PROCESSES64最大进程数。
SHM_MAX_SERVICES4096全系统最大服务数(64×64)。
SHM_MAX_OBJECTS10000锁表最大对象数。
SHM_LOCK_TIMEOUT_MS5000C 层注释中的默认锁超时。
SHM_TIMEOUT_DEFAULT_MS1000C++ acquire_*_lock 默认超时。
MAX_SERVICE_ID63ShmLockManager::allocate_service_id 进程内服务 ID 上限(0–63)。

2.1 C 初始化与进程/服务注册

功能说明

在调用方提供的共享内存上初始化锁管理器,并注册进程与服务。进程注册会分配逻辑进程 ID 并写入进程指纹。

c
#include "shmlock/shmlock.h"

shm_lock_manager_t *manager = NULL;
int ret = shm_lock_init(&manager, shm_base, shm_size);
if (ret != SHM_LOCK_OK) {
    return ret;
}

shm_service_id_t process_svc;
ret = shm_lock_register_process(manager, &process_svc);

shm_service_id_t service;
ret = shm_lock_register_service(manager, process_svc.logical_process_id, 1, &service);

参数说明

函数参数返回值说明
shm_lock_init(manager, shm_base, shm_size)输出管理器指针、共享内存基址与大小shm_lock_error_tshm_base 上构造 shm_lock_manager_t
shm_lock_destroy(manager)管理器void销毁管理器。
shm_lock_register_process(manager, out_service)管理器、输出 shm_service_id_tshm_lock_error_t注册当前进程。
shm_lock_unregister_process(manager, logical_process_id)管理器、逻辑进程 IDshm_lock_error_t注销进程。
shm_lock_register_service(manager, logical_process_id, service_id, service)逻辑进程 ID、进程内服务 ID、输出服务标识shm_lock_error_t在已注册进程上注册服务。
shm_lock_unregister_service(manager, service)服务标识shm_lock_error_t注销服务。
shm_lock_is_process_alive(manager, logical_process_id)逻辑进程 IDbool按指纹检查进程是否存活。

返回值与异常

C 接口返回 shm_lock_error_t,不抛异常。shm_lock_error_string(error) 将错误码转为英文描述。

返回值含义触发条件处理建议
0SHM_LOCK_OK成功参数合法且操作完成
-1SHM_LOCK_TIMEOUT超时在超时时间内未能完成锁操作增大 timeout_ms 或检查持锁方
-2SHM_LOCK_INVALID参数非法空指针、未初始化或句柄无效检查管理器、服务 ID 和句柄
-3SHM_LOCK_NOMEM内存不足共享内存或锁表耗尽扩大段大小或减少对象数
-4SHM_LOCK_BUSY锁忙非阻塞获取时锁被占用稍后重试或改用阻塞接口
-5SHM_LOCK_OWNER_DEAD持有者已死锁所有者进程/服务已失效走健康检查/强制解锁路径

应用场景

业务进程在使用读写锁前必须先 shm_lock_init(或由 C++ LockManager/ShmLockManager 完成),再注册进程和服务。

限制条件

  • 锁表与进程映射位于共享内存,跨进程只能通过相对偏移访问(mgr_locks_ptr 等内联辅助函数)。
  • SHM_MAX_PROCESSES 为 64;服务总数上限 4096。
  • 进程异常退出时不应再调用 shm_lock_unregister_service;应依赖 shm_lock_health_check 清理,避免与自动清理重复解锁。

调试示例

C 调试
c
shm_service_id_t process_svc;
if (shm_lock_register_process(manager, &process_svc) != SHM_LOCK_OK) {
    return -1;
}
shm_service_id_t service;
if (shm_lock_register_service(manager, process_svc.logical_process_id, 1, &service) != SHM_LOCK_OK) {
    return -1;
}

2.2 C 锁操作

功能说明

object_id 获取读锁或写锁。读锁可被多个服务同时持有;写锁互斥。object_id == 1 为全局对象,使用 global_reader_refcount 支持同一 service 的读锁重入;其他对象用 reader_bitmap,无重入计数。

参数说明

函数参数返回值说明
shm_lock_acquire_read(manager, object_id, service, timeout_ms, handle)对象 ID、服务、超时毫秒、输出句柄shm_lock_error_t阻塞获取读锁。
shm_lock_acquire_write(...)同上shm_lock_error_t阻塞获取写锁。
shm_lock_try_acquire_read(...)无超时参数shm_lock_error_t非阻塞读锁;忙则 SHM_LOCK_BUSY
shm_lock_try_acquire_write(...)无超时参数shm_lock_error_t非阻塞写锁。
shm_lock_downgrade(handle)写锁句柄shm_lock_error_t写锁降级为读锁。
shm_lock_release(handle)句柄shm_lock_error_t释放锁。
shm_lock_acquire_multi_read(manager, object_ids, count, service, timeout_ms, handles)对象 ID 数组与输出句柄数组shm_lock_error_t批量读锁。
shm_lock_acquire_multi_write(...)同上shm_lock_error_t批量写锁。
shm_lock_release_multi(handles, count)句柄数组shm_lock_error_t批量释放。
shm_lock_force_unlock(manager, object_id)对象 IDshm_lock_error_t强制解锁指定对象。

返回值与异常

成功为 SHM_LOCK_OK;超时、忙、参数非法见 2.1。shm_lock_type_tSHM_LOCK_NONE=0SHM_LOCK_READ=1SHM_LOCK_WRITE=2

应用场景

多进程并发读写共享对象;读多写少场景优先读锁。全局配置类对象可使用 object_id == 1

限制条件

  • 同一 Service 的多个线程并行访问同一非全局对象时,上层 reader_bitmap 可能漏计数(源码 README 已说明)。
  • SHM_REFCOUNT_MAX 为 255,全局对象上单服务并发读锁不超过该值。
  • 句柄是本地内存中的引用,不可放入共享内存跨进程传递。

调试示例

c
shm_lock_handle_t handle;
int ret = shm_lock_acquire_write(manager, 12345, &service, 1000, &handle);
if (ret != SHM_LOCK_OK) {
    fprintf(stderr, "%s\n", shm_lock_error_string(ret));
    return ret;
}
ret = shm_lock_downgrade(&handle);
ret = shm_lock_release(&handle);

2.3 C 健康监控与统计

功能说明

注册服务心跳、启动后台健康检查、查询统计信息或转储锁表状态。

参数说明

函数参数返回值说明
shm_lock_register_service_heartbeat(manager, service, heartbeat_interval_ms)服务、心跳间隔shm_lock_error_t注册心跳;超时倍数为 SHM_HEARTBEAT_TIMEOUT_MULTIPLIER(3)。
shm_lock_service_heartbeat(service)服务shm_lock_error_t发送一次心跳。
shm_lock_check_service_alive(manager, service)服务shm_lock_error_t检查服务是否存活。
shm_lock_start_health_monitor(manager)管理器shm_lock_error_t启动后台健康线程。
shm_lock_stop_health_monitor(manager)管理器shm_lock_error_t请求停止健康线程。
shm_lock_health_check(manager)管理器shm_lock_error_t立即执行一次健康检查。
shm_lock_cleanup_dead_readers(manager)管理器shm_lock_error_t清理死亡读者。
shm_lock_get_stats(manager, stats)输出 shm_lock_stats_tshm_lock_error_t统计加解锁、超时、活跃读写锁、竞争次数。
shm_lock_dump_state(manager, buffer, size)输出缓冲区shm_lock_error_t最多转储 SHM_DUMP_MAX_LOCKS(20)个锁。

shm_lock_stats_t 字段:total_lockstotal_unlockstotal_timeoutsactive_read_locksactive_write_lockslock_contentions。头文件注明死锁检测已移除。

返回值与异常

同 2.1。健康检查成功返回 SHM_LOCK_OK

应用场景

守护进程创建锁管理器后调用 shm_lock_start_health_monitor;业务服务按注册间隔调用 shm_lock_service_heartbeat

限制条件

  • 心跳超时为间隔的 3 倍;超时未心跳的服务会被判定死亡并释放其锁。
  • vos_get_tick / vos_get_tick_timespec 用于规避单调时钟/真实时钟回跳;时钟跳跃阈值见 SHM_CLOCK_JUMP_THRESHOLD_SEC(7200 秒)。
  • DEBUG 构建才导出 shm_lock_debug_get_semid_for_object

调试示例

c
shm_lock_register_service_heartbeat(manager, &service, 5000);
shm_lock_start_health_monitor(manager);
shm_lock_service_heartbeat(&service);

shm_lock_stats_t stats;
shm_lock_get_stats(manager, &stats);

char buf[8192];
shm_lock_dump_state(manager, buf, sizeof(buf));

2.4 C++ LockManager / LockHandle / ServiceId

功能说明

namespace shmlock 对 C API 的封装。失败时抛 LockExceptionwhat() 为描述,error_code()shm_lock_error_t)。LockHandle 禁止拷贝、支持移动;析构或 release() 时释放锁。

包含头文件:

cpp
#include "shmlock/shmlock_manager.h"

构造方式:

构造函数说明
LockManager(managed_shared_memory& shm, size_t size = 0)从 Boost 段中分配并初始化。
LockManager(void* shm_base, size_t size)使用已有内存基址。
LockManager(managed_shared_memory& shm, const char* block_name, size_t size_bytes)命名块:size_bytes > 0 时创建并初始化;size_bytes == 0 时仅附着已有 meta。附着方析构不会销毁底层结构。

参数说明

方法参数返回值说明
register_process()ServiceId注册进程。
unregister_process(logical_process_id)uint16_tvoid失败抛 LockException
register_service(logical_process_id, service_id)逻辑进程 ID、服务 IDServiceId注册服务。
unregister_service(service)ServiceIdvoid正常退出前调用。
is_process_alive(logical_process_id)uint16_tbool进程存活检查。
acquire_read_lock(object_id, service, timeout_ms)默认超时 SHM_TIMEOUT_DEFAULT_MSLockHandle阻塞读锁。
acquire_write_lock(...)同上LockHandle阻塞写锁。
try_acquire_read_lock / try_acquire_write_lock无超时LockHandle失败抛 LockException(含 SHM_LOCK_BUSY)。
acquire_multi_read_locks(object_ids, service, timeout_ms)vector<uint64_t>vector<LockHandle>批量读锁。C++ 层未封装批量写锁。
force_unlock(object_id)对象 IDvoid强制解锁。
get_stats()LockStatstotal_deadlocks 固定为 0
health_check()boolshm_lock_health_check == SHM_LOCK_OK
register_service_heartbeat / service_heartbeat / check_service_alive服务与间隔bool心跳相关。
start_health_monitor()bool等价于 start_health_monitoring()
stop_health_monitoring()bool停止健康线程。
dump_state()std::string状态转储。

LockHandle 方法:release()is_valid()type()downgrade()(仅写锁可降级)。

ServiceId 方法:logical_process_id()service_id()incarnation()get() / get_internal_id()

返回值与异常

未初始化时抛 LockException(SHM_LOCK_INVALID, "Lock manager not initialized")。锁操作失败时消息为 Failed to ...: + shm_lock_error_string。附着命名块但找不到 block_name.meta 时抛 Attach failed: meta not found

应用场景

测试与自定义共享内存段使用 LockManager;openUBMC 运行时推荐 2.5 节单例。

限制条件

  • 拷贝 LockHandle 已删除,只能移动。
  • 仅进程正常退出前调用 unregister_service;异常退出依赖健康检查。
  • C++ 未封装 shm_lock_acquire_multi_write / shm_lock_release_multi / shm_lock_cleanup_dead_readers

调试示例

C++ 调试(创建 + 附着)
cpp
#include "shmlock/shmlock_manager.h"
#include <boost/interprocess/managed_shared_memory.hpp>

constexpr const char* kSegmentName  = "ubmc_shmlock";
constexpr const char* kManagerBlock = "ubmc_lock_manager_block";
constexpr std::size_t kSegmentSize  = 16 * 1024 * 1024;

boost::interprocess::managed_shared_memory shm(
    boost::interprocess::create_only, kSegmentName, kSegmentSize);
shmlock::LockManager manager(shm, kManagerBlock, kSegmentSize);
manager.start_health_monitor();

auto process = manager.register_process();
auto service = manager.register_service(process.logical_process_id(), 1);
manager.register_service_heartbeat(service, 5000);

{
    auto lock = manager.acquire_write_lock(12345, service);
    (void)lock;
}

auto read_lock = manager.acquire_read_lock(12345, service, 1000);
if (read_lock.is_valid()) {
    read_lock.release();
}

客户端以 open_only 打开段,并用 LockManager(shm, kManagerBlock, 0) 附着。test_package/test_init_attach_gtest.cpp 验证附着方析构不会销毁底层锁表。

2.5 C++ ShmLockManager 单例

功能说明

面向 BMC 运行时的 Facade:open_or_createMDBUS_SHM_LOCK_MANAGER,内部块名 "lock_manager",并把文件属主改为 uid 101(comm_user)、gid 103(apps),权限 0660

cpp
shmlock::ShmLockManager& mgr = shmlock::ShmLockManager::get_instance();
uint16_t sid = shmlock::ShmLockManager::allocate_service_id();
mgr.register_service(sid);
auto lock = mgr.acquire_read_lock(object_id, sid);

参数说明

方法参数返回值说明
get_instance()ShmLockManager&进程内单例;构造失败抛 std::runtime_error("open shm failed")
destroy()bool删除单例,并 semctl(IPC_RMID) 清理 key 0x12340000 的 System V 信号量集,再 shared_memory_object::remove("MDBUS_SHM_LOCK_MANAGER")
allocate_service_id()uint16_t进程内递增,最大返回 63
allocate_object_id(path)对象路径uint32_tg_str_hash(path);若结果为 1 则改为 2,避免占用全局锁 ID。
register_service(service_id)进程内服务 IDServiceId在本进程已注册的 logical process 上注册,并缓存到内部 map。
service_heartbeat(service_id)服务 IDboolmap 中无该 ID 时会先 register_service
acquire_read_lock / acquire_write_lockobject_idservice_id、可选超时LockHandle未缓存的 service_id 会先注册。
get_logical_process_id()uint16_t本进程逻辑 ID。
get_base()LockManager&底层管理器;未初始化抛 std::runtime_error
get_stats() / dump_state()统计 / 字符串转发到 LockManager

环境变量 MDBUS_SHM_LOCK_MANAGER_SIZE:十进制字节数;小于 0 回退默认 10 MiB,大于 100 MiB 截断为上限,0 也回退默认值。

返回值与异常

单例打开失败为 std::runtime_error("open shm failed")。锁失败仍为 LockException

应用场景

微组件框架在各业务进程中获取同一锁管理器段,按对象路径分配 object_id 后加锁。

限制条件

  • allocate_object_id 使用 32 位哈希,不同路径可能碰撞。
  • allocate_service_id 到达 63 后不再递增,后续调用重复返回 63。
  • destroy() 会删除整段共享内存并移除信号量,仅应在明确的框架销毁路径调用。

调试示例

cpp
auto& mgr = shmlock::ShmLockManager::get_instance();
uint16_t service_id = shmlock::ShmLockManager::allocate_service_id();
uint32_t object_id = shmlock::ShmLockManager::allocate_object_id("/bmc/kepler/Example");
mgr.register_service(service_id);
try {
    auto handle = mgr.acquire_write_lock(object_id, service_id, 1000);
    (void)handle;
} catch (const shmlock::LockException& e) {
    // e.error_code() 为 shm_lock_error_t
}

2.6 shm::shared_memory 与对象树

功能说明

shm::shared_memory::get_instance() 打开或创建段 MDBUS_SHM/dev/shm/MDBUS_SHM),在段内 find_or_construct<shared_memory_base>("shared_memory_base")。默认大小 90 MiB(ENABLE_TEST 时 50 MiB)。环境变量 MDBUS_SHM_SIZE 为十进制字节;非法或非正回退默认,超过 200 MiB 截断;当 /proc/meminfoMemTotal ≤ 2 GiB 时,自定义大小不会超过默认值。文件属主同样设为 uid 101、gid 103,权限 0660

对象路径必须以 / 开头,按段插入 tree_nodeobject 上可注册 interface,再添加 method / property / signal

参数说明

shm::shared_memory 主要方法:

方法说明
get_instance()单例;失败抛 std::runtime_error("open shm failed")
get_base()返回 shared_memory_base&
get_segment_manager()Boost 段管理器。
get_tree(wellknow_name)按 well-known name 取或创建 object_tree;名称非法抛 invalid wellknow name
remove_tree(wellknow_name)删除树上 MDB 对象、订阅规则,并擦除树。
get_wellknow_names() / get_unique_names()名称到 object_tree 的 map。
add_mdb_object / remove_mdb_object / find_mdb_object维护 path+interface 视图。
query_interface_view(iface_name, filter)按接口名查询。
set_harbor_name / del_harbor_name / get_harbor_nameunique name 与 harbor 映射。
lock / unlock / lock_shared / unlock_shared当前实现中 shared 变体与独占锁均转发到 get_base().lock()/unlock()
is_exist()检查 /dev/shm/MDBUS_SHM 是否存在。
destory()源码头文件拼写为 destory(不是 destroy):移除共享内存段。
ipmi_match(net_fn, cmd, payload)查询 IPMI 路由,返回 ipmi_match_ret_t
add_ipmi_cmd / remove_ipmi_cmd维护 IPMI 命令表。
new_mutex(name)在共享内存中创建/获取命名互斥量。

shm::object / interface / property / method / signal 用于在树上注册 D-Bus 风格成员(add_m / add_p / add_sfind_m / find_p / find_s)。tree_base::register_object / find_object / unregister_object / travel 操作路径树。

object_tree::create_message_queue(shm, size) 创建进程间消息队列;push_back(data, timeout_ms) / pop_front(cb, timeout_ms, max_read_count, read_buf)。单条消息长度不能超过 uint16_t 最大值。

shm::ipmi_handler 保存 net_fncmdpriorityprivilegefilterpath 等;ipmi_route_node 按 payload 分段组织处理器。

返回值与异常

路径不以 / 开头时 treestd::runtime_error("tree: invalid path")。打开段失败抛 open shm failedget_tree 在非 well-known 名称时抛 invalid wellknow name

应用场景

微组件框架把各服务的 D-Bus 对象、接口和 IPMI 命令放入共享内存,供其他进程按路径或 well-known name 查找。上层业务一般不直接操作这些类,而是通过框架封装。

限制条件

  • 对象路径最长相关约束:源码中 MAX_DBUS_PATH 为 512。
  • lock_sharedlock 当前实现相同,不能当作真正的读写锁语义使用。
  • 销毁 API 符号名为 destory
  • 仓库 test_package 对对象树没有与 shmlock 同级别的用例覆盖;调用约定以头文件为准。

调试示例

cpp
#include "dbus/shm_tree/shared_memory.h"

auto& shm = shm::shared_memory::get_instance();
shm::object_tree* tree = shm.get_tree("bmc.kepler.example");
shm::object& obj = tree->register_object(shm, "/bmc/kepler/Example/1");
auto iface = obj.register_interface(shm, false, "bmc.kepler.Example");
iface->add_m(shm, "Ping", "", "s");

2.7 DBus::Match 匹配规则

功能说明

进程内匹配引擎,按 D-Bus match rule 字段过滤消息。Rule 为链式配置;Matchs::add_rule 注册规则与回调;run / test_matchContext 求值。

参数说明

DBus::Match::Rule 可配置字段:

方法含义
type(MessageType)signal / method_call / method_return / error / invalid
sender / destination / interface / member字符串匹配字段。
path / path_namespace对象路径;namespace 允许前缀匹配,段可为 *
property_interface / properties属性变更信号过滤。
set_eavesdrop(bool)eavesdrop 标志。
as_string()序列化为 match 字符串。
disconnect()调用已绑定 slot_t::remove

路径含 * 时,仅整段为 * 合法,否则抛 D-Bus error: invalid fuzzy object path;最终路径须通过 dbus_validate_path

Matchs

方法说明
add_rule(RulePtr&, match_cb_t&&)正在 run 时延迟到本次匹配结束再插入。
run(Context&)按规则树匹配并执行回调。
test_match(Context&)只测试是否匹配。

共享内存侧对应 shm::matchs::add_rule / remove_rules / run / test_match,订阅者是 object_tree*

返回值与异常

非法对象路径抛 std::runtime_errorrun / test_match 返回 bool

应用场景

D-Bus 信号订阅与属性变更(org.freedesktop.DBus.Properties.PropertiesChanged,签名 sa{sv}as)过滤。Context::extract_properties_change_info 从该信号提取接口名和属性表。

限制条件

源码未覆盖独立的 Lua/命令行调试接口;应在链接 libsomp 的宿主进程内调用。

调试示例

cpp
#include "dbus/match/matchs.h"
#include "dbus/match/rule.h"

auto rule = std::make_shared<DBus::Match::Rule>();
rule->type(DBus::Match::MessageType::signal)
    .interface("org.freedesktop.DBus.Properties")
    .member("PropertiesChanged")
    .path_namespace("/bmc/kepler");

DBus::Match::Matchs matchs;
matchs.add_rule(rule, [](DBus::Match::Context& ctx) {
    (void)ctx;
});

2.8 DBus::utils

功能说明

路径分段与忽略大小写字符串比较,供对象树和 Match 使用。test_package/DBusUtilsTest.cpp 覆盖 str_less_i

参数说明

接口说明
get_segment(path)返回可重复调用的 segment_t,每次给出 (当前段, 剩余路径)
str_less_i(a, b)忽略大小写的小于比较。
string_view_ignore_case用于 map 查找的忽略大小写 string_view
std_string_lesstransparent comparator,支持 stringstring_view

返回值与异常

比较函数返回 bool。路径分段不抛异常;空路径得到空段。

应用场景

/bmc/kepler/Foo/1 拆成 bmckeplerFoo1 以插入对象树。

限制条件

str_less_i 按忽略大小写比较,"Apple""apple" 互不小于。

调试示例

cpp
#include "dbus/utils/utils.h"

std::string_view path = "/bmc/kepler/Foo";
auto next = DBus::utils::get_segment(path);
auto first = next();  // first.first == "bmc"

3. 组件扩展案例

3.1 扩展能力概述

libsomp 不提供插件机制、配置 DSL 或运行时扩展点。mds/service.jsonrequired 为空,仓库没有 model.jsonipmi.json。扩展方式是代码级二次开发:

  • 上层组件增加对 libsomp(Conan 库名 somp)的链接依赖后调用现有 C/C++ API。
  • src/lualib-src/dbus/ 中新增实现,并保证 include/install(FILES ...) 同步导出头文件。

3.2 扩展点说明

扩展位置作用触发时机
include/shmlock/shmlock.hsrc/lualib-src/dbus/shmlock/shmlock.c新增 C 锁 API链接 libsomp 的进程调用时。
include/shmlock/shmlock_manager_core.hshmlock_manager_core.cpp新增 C++ 封装调用 LockManager / ShmLockManager 时。
src/lualib-src/dbus/shm_tree/扩展对象树、IPMI 路由、消息队列shm::shared_memory::get_instance() 之后。
src/lualib-src/dbus/match/扩展 Match 字段或匹配算法Matchs::add_rule / run 时。
src/lualib-src/dbus/CMakeLists.txt将新 .c/.cpp 编入 somp 目标重新构建时。

3.3 二次开发指导

步骤一:实现 C 或 C++ 入口

锁相关新接口应同时更新 shmlock.hshmlock.c,错误码复用 shm_lock_error_t,并通过 shm_lock_error_string 提供描述。C++ 封装失败时抛 LockException

步骤二:导出头文件

include/shmlock/ 由 CMake install(DIRECTORY include/shmlock) 安装;shm_tree / match / utils 头文件需在 src/lualib-src/CMakeLists.txt 中逐文件 install(FILES ...)

示例代码

上层组件使用运行时单例:

cpp
#include "shmlock/shmlock_manager.h"

auto& mgr = shmlock::ShmLockManager::get_instance();
uint16_t sid = shmlock::ShmLockManager::allocate_service_id();
mgr.register_service(sid);
auto lock = mgr.acquire_read_lock(
    shmlock::ShmLockManager::allocate_object_id("/bmc/kepler/Example"), sid);

验证方法

  1. 使用项目构建环境编译,确认生成 libsomp.so 并安装到 usr/lib64
  2. test_package 中按 test_base.hpp 创建独立 Boost 段与 LockManager,覆盖成功、超时、非法参数。
  3. 多进程场景参考 test_init_attach_gtest.cpp:一方 size_bytes > 0 创建,另一方 size_bytes == 0 附着。
  4. 预期成功返回 SHM_LOCK_OK 或有效 LockHandle;失败返回文档化错误码或 LockException

注意事项

  • 保持 C 函数名、错误码和 C++ 类名向后兼容。
  • 修改清理路径时必须同步 System V 层计数与 shmlock 层位图,避免重复解锁。
  • 同一 Service 多线程访问同一对象时评估 reader_bitmap 漏计数风险。
  • debug_log 当前实现为空函数(见 4.1),新增日志前需先接通实际日志后端。
  • 若新增 API,须同步更新本文档的 API、错误码、日志和 FAQ 章节。

4. 日志说明

4.1 一键日志收集

libsomp 是动态库,不创建独立日志文件,也没有仓库内声明的 on_dump 回调。include/dbus/logging.h 定义 debug_log 宏,调用 shm_debug_log_func。当前 src/lualib-src/dbus/logging.cpp 中该函数体为空(注释为「暂时不打印日志」),get_log_module_name() 返回空字符串。因此一键日志收集默认不会得到 libsomp 自己的日志行。

文件路径内容说明
不适用(实现为空)源码预留了 DLOG_ERROR / DLOG_WARN / DLOG_NOTICE / DLOG_INFO 等级别调用点,但当前不会输出。
/dev/shm/MDBUS_SHMD-Bus/MDB 对象树共享内存段,不是文本日志。
/dev/shm/MDBUS_SHM_LOCK_MANAGER锁管理器共享内存段。

排障应使用 dump_state()get_stats() 以及 System V 信号量/共享内存是否存在,而不是检索组件私有 log 文件。

4.2 关键日志信息

下列字符串存在于源码 debug_log(...) 调用中;在日志后端接通之前,它们不会出现在宿主日志里。接通后可按关键字过滤。

日志片段级别含义解读建议处理动作
create shared memory lock managerINFOShmLockManager 构造成功。无。
open shm failed异常打开/创建 MDBUS_SHMMDBUS_SHM_LOCK_MANAGER 失败。检查 /dev/shm 空间、权限和段是否被损坏。
LockManager: register_process failed, not initializedERROR管理器未初始化就注册进程。先完成 LockManager 构造或 shm_lock_init
LockManager: create path block=... free=...NOTICE正在创建命名锁管理块。确认 free 足够;WSL 上需保留 headroom。
chown lock_manager failed / chmod lock_manager failedINFO无法将锁段改为 uid 101 / gid 103 或 0660检查进程权限;非 root 时常见。
chown shared_memory failed / chmod shared_memory failedERRORMDBUS_SHM 属主或权限设置失败。同上。
ShmLockManager: removed stale System V semaphoreINFOdestroy() 删除了残留信号量集。确认没有其它进程仍在使用该 semid。
ShmLockManager: remove MDBUS_SHM_LOCK_MANAGER failedWARNshared_memory_object::remove 失败,改为 std::remove 文件。检查段是否仍被映射。
invalid shared memory sizeINFOMDBUS_SHM_SIZE 非法,已回退默认或上限。修正环境变量。

5. 问题定界指南

5.1 典型问题定界

问题描述是否为本组件问题判断依据关键证据收集方法
链接不到 libsomp.so / somp可能是未安装到 usr/lib64 或 Conan 未声明依赖。ldd 查看宿主进程;确认包中存在 usr/lib64/libsomp.so
get_instance()open shm failed可能是Boost 段创建/打开失败。检查 /dev/shm 容量、MDBUS_SHM* 文件和进程对 /dev/shm 的写权限。
acquire_*_lockLock timeout可能是持锁方未释放,或本组件健康检查未清残留错误码 -1dump_state()get_stats().total_timeouts;检查对端进程是否存活。
try_acquire_* 失败 Lock busy通常是调用方预期内非阻塞接口在锁被占用时返回 -4改用带超时的阻塞接口,或确认写锁持有者。
附着 LockManager(..., 0) 抛 meta not found是调用顺序问题创建方尚未写入 block_name.meta确认守护进程已用 size_bytes > 0 构造。
busctl 找不到 libsomp 服务不是组件不是 D-Bus 服务。不要用 busctl;应在宿主进程内调 C/C++ API。
管理站/IPMI 客户端行为异常通常不是本组件只提供路由表存储,不实现 IPMI 协议栈。ipmi_core / rmcpd 等组件定界。

5.2 错误码速查表

错误码宏 / 异常含义排查建议
0SHM_LOCK_OK / "Success"成功继续检查业务数据。
-1SHM_LOCK_TIMEOUT / "Lock timeout"锁等待超时增大超时、检查写锁持有者、执行 health_check
-2SHM_LOCK_INVALID / "Invalid parameter"参数或句柄非法检查是否已 init、服务是否已注册、是否对非写锁 downgrade
-3SHM_LOCK_NOMEM / "No memory available"内存不足扩大 MDBUS_SHM_LOCK_MANAGER_SIZE 或减少对象。
-4SHM_LOCK_BUSY / "Lock busy"非阻塞获取时锁被占用重试或改阻塞接口。
-5SHM_LOCK_OWNER_DEAD / "Lock owner dead"持有者已死健康检查清理后重试。
其他"Unknown error"未识别错误码记录原始整型值。
std::runtime_error("open shm failed")单例共享内存段打开失败见 5.1。

C++ LockStats.total_deadlocks 恒为 0(C 统计结构已移除死锁计数)。不要再按已删除的 SHM_LOCK_DEADLOCK 编写分支。

5.3 调试方法

开启调试日志

当前 shm_debug_log_func 为空,没有组件级日志开关。排障以 API 返回值、dump_state() 和系统 IPC 状态为准。

复现问题方法

  1. 确认 usr/lib64/libsomp.so 可被宿主进程加载。
  2. test_packageShmLockTestBase 在独立段上复现加解锁,排除 BMC 全局段干扰。
  3. 多进程问题:一方创建命名块,另一方 open_only + size_bytes == 0 附着。
  4. 检查 /dev/shm/MDBUS_SHM/dev/shm/MDBUS_SHM_LOCK_MANAGER 以及 ipcs -s 中与 key 0x12340000 相关的信号量。
  5. 调用 get_stats() / dump_state() 保存锁表快照。

libsomp 没有 D-Bus 对象,因此不存在 busctl 命令行调试方式。

5.4 错误对象解读

C API 返回 intshm_lock_error_t)。C++ 锁 API 抛 shmlock::LockException,用 error_code() 对齐上表。单例与对象树部分抛 std::runtime_error,没有 D-Bus 结构化错误对象。

6. 常见问题解答

Q1:为什么链接或运行时报找不到 libsomp.so

  • 问题描述:动态链接失败,或 dlopen 找不到库。
  • 一句话答案:通过 Conan/组件包安装 usr/lib64/libsomp.so,并在链接时指定 somp
  • 根因说明:CMake install(TARGETS somp DESTINATION usr/lib64),Conan cpp_info.libs = ["somp"]
  • 解决方案:确认产物路径、RPATH/LD_LIBRARY_PATH 和依赖(glib、libdbus、boost、liblogger、securec)。
  • 规避方案:不要只拷贝单个 .so 而忽略依赖与头文件。
  • 适用版本:3.10.15。

Q2:get_instance() 抛出 open shm failed 怎么办?

  • 问题描述:ShmLockManagershm::shared_memory 单例构造失败。
  • 一句话答案:检查 /dev/shm 是否可写、段大小环境变量是否合法、旧段是否损坏。
  • 根因说明:open_or_create 在权限不足、空间不足或段元数据损坏时会失败。
  • 解决方案:查看 /dev/shm/MDBUS_SHM*;必要时在无业务进程占用时调用 ShmLockManager::destroy() / shared_memory::destory() 后重建。
  • 规避方案:用环境变量把段大小限制在默认与上限之间。
  • 适用版本:3.10.15。

Q3:加锁总是超时,如何区分死锁残留和正常竞争?

  • 问题描述:acquire_write_lock / shm_lock_acquire_* 返回 SHM_LOCK_TIMEOUT
  • 一句话答案:先看对端进程是否存活和是否在发心跳,再用 dump_state 看持锁者。
  • 根因说明:写锁互斥;进程崩溃可能留下 System V 层计数。健康检查按秒级(进程)到约 30 秒(陈旧锁)恢复。
  • 解决方案:对仍存活的持有者检查业务逻辑;对死亡进程调用 health_check 或等待监控线程。不要在异常退出路径再 unregister_service
  • 规避方案:使用 LockHandle RAII,并为服务注册心跳。
  • 适用版本:3.10.15。

Q4:客户端附着锁管理器失败,提示 meta not found?

  • 问题描述:LockManager(shm, block_name, 0)Attach failed: meta not found
  • 一句话答案:必须先有创建方用非 0 的 size_bytes 初始化同名块。
  • 根因说明:附着路径只查找 block_name + ".meta",不负责初始化。
  • 解决方案:调整进程启动顺序,或改用 ShmLockManager::get_instance() 走框架固定段名。
  • 规避方案:测试中先构造 daemon LockManager,再构造 attach-only 实例。
  • 适用版本:3.10.15。

Q5:能否用 busctlrequire() 调用 libsomp?

  • 问题描述:按业务组件方式查找 D-Bus 服务或 Lua 模块失败。
  • 一句话答案:不能。libsomp 只导出 C/C++ 共享库。
  • 根因说明:无 luaopen_*、无 model.json、无组件 D-Bus 服务名。
  • 解决方案:在 C++/C 宿主中 #include 对应头文件并链接 somp
  • 规避方案:通过微组件框架间接使用对象树和锁,而不是把 libsomp 当成独立服务。
  • 适用版本:3.10.15。