OpenBMC:时间管理机制解析
OpenBMC时间管理机制解析1. 说明OpenBMC 的时间管理不是由单一模块完成的而是由多个组件协同实现。主要涉及以下组件phosphor-time-manager phosphor-settings systemd-timedated bmcweb WebUI / Redfish 客户端它们之间大致分工如下phosphor-time-manager负责 D-Bus 时间对象导出、时间同步模式维护以及与systemd-timedated的交互phosphor-settings负责保存时间同步模式等配置systemd-timedated真正执行系统时间设置和 NTP 开关控制bmcweb负责把时间相关能力通过 Redfish/REST 暴露给 WebUI 和外部客户端WebUI作为 Redfish 接口的前端展示和操作入口OpenBMC 时间管理的核心思想是用 D-Bus 做内部状态和控制接口 用 Redfish 做外部管理接口 最终由 systemd-timedated 执行系统时间相关操作。2. 整体架构时间管理相关的主要组件和职责如下。2.1 phosphor-time-managerphosphor-time-manager是 OpenBMC 中负责时间管理的核心服务之一。它主要做几件事注册 D-Bus service xyz.openbmc_project.Time.Manager 导出对象 path /xyz/openbmc_project/time/bmc 实现 interface xyz.openbmc_project.Time.EpochTime 维护时间同步模式 NTP / Manual 与 systemd-timedated 双向同步2.2 bmcwebbmcweb对外提供 Web 和 Redfish 接口。在时间管理中它主要负责提供 Manager 资源中的DateTime提供 Manager 资源中的DateTimeLocalOffset提供 NTP 开关读写能力提供 NTP Server 相关接口将 Redfish 请求转换为 D-Bus 调用2.3 systemd-timedatedsystemd-timedated是 systemd 提供的时间管理服务对外暴露 D-Bus 服务org.freedesktop.timedate1它负责真正执行SetTime SetNTP也就是说OpenBMC 上层服务并不是直接修改系统时间而是通过 D-Bus 调用systemd-timedated完成。2.4 phosphor-settingsphosphor-settings用于保存 OpenBMC 的配置项。时间同步模式相关配置通常保存在/xyz/openbmc_project/time/sync_method对应接口为xyz.openbmc_project.Time.Synchronization时间同步模式通常有两种NTP Manual3. 时间管理的两条主线OpenBMC 时间管理中有两条关键路径。第一条是时间值本身的设置外部请求 / D-Bus 属性写入 ↓ SetTime ↓ systemd-timedated ↓ 系统时间更新第二条是时间同步模式的切换NTP / Manual ↓ phosphor-settings ↓ phosphor-time-manager ↓ systemd-timedated.SetNTP同时如果systemd-timedated中的 NTP 状态发生变化phosphor-time-manager也会反向同步回phosphor-settings。因此时间同步模式是双向同步关系phosphor-settings ---- phosphor-time-manager ---- systemd-timedated4. phosphor-time-manager 启动流程phosphor-time-manager的启动逻辑比较典型主要流程如下创建 system bus 连接 ↓ 绑定到 systemd event loop ↓ 创建 ObjectManager ↓ 创建 Manager ↓ 创建 BmcEpoch ↓ 注册 D-Bus service name ↓ 进入事件循环关键代码可以简化理解为auto bus sdbusplus::bus::new_default(); sdbusplus::server::manager_t bmcEpochObjManager(bus, objpathBmc); phosphor::time::Manager manager(bus); phosphor::time::BmcEpoch bmc(bus, objpathBmc, manager); bus.request_name(busname); sd_event_loop(bus.get_event());这几句代码对应的 D-Bus 信息是service xyz.openbmc_project.Time.Manager path /xyz/openbmc_project/time/bmc interface xyz.openbmc_project.Time.EpochTime也就是说phosphor-time-manager启动后核心工作就是把一个 BMC 时间对象挂到 D-Bus 上对外提供时间相关服务。5. BmcEpoch时间对象的 D-Bus 暴露层BmcEpoch用于导出 BMC 时间对象。它通过继承EpochTime接口实现 D-Bus server objectusing EpochTimeIntf sdbusplus::server::object_t sdbusplus::xyz::openbmc_project::Time::server::EpochTime; class BmcEpoch : public EpochTimeIntf, public PropertyChangeListner { ... };这表示BmcEpoch 是一个 D-Bus server object 实现的接口是 xyz.openbmc_project.Time.EpochTime其最关键的属性是Elapsed该属性表示当前时间的微秒计数。6. 读取 Elapsed 属性读取Elapsed属性时会调用uint64_t BmcEpoch::elapsed() const { return getTime().count(); }它直接返回当前系统时间的微秒计数。可以简单理解为读取 D-Bus 的 Elapsed ↓ BmcEpoch::elapsed() ↓ getTime() ↓ 返回当前系统时间7. 写入 Elapsed 属性写入Elapsed属性时会调用uint64_t BmcEpoch::elapsed(uint64_t value) { auto time microseconds(value); setTime(time); server::EpochTime::elapsed(value); return value; }执行过程如下收到外部写入的新时间 ↓ 转换成 microseconds ↓ 调用 setTime() ↓ 更新 D-Bus 属性值这里最关键的是setTime()。BmcEpoch并不直接修改系统时间而是通过 D-Bus 调用systemd-timedated。简化代码如下auto method bus.new_method_call( systemdTimeService, systemdTimePath, systemdTimeInterface, methodSetTime); method.append(static_castint64_t(usec.count()), false, false); bus.call_noreply(method);对应的目标服务是service org.freedesktop.timedate1 path /org/freedesktop/timedate1 interface org.freedesktop.timedate1 method SetTime因此写入 OpenBMC 的Elapsed属性后最终执行的是org.freedesktop.timedate1.SetTime8. Manager时间同步模式协调器Manager的职责不是导出时间值而是负责协调时间同步模式。它主要负责监听 OpenBMC settings 中的时间同步模式监听 systemd-timedated 中的 NTP 状态在两者之间做同步维护当前时间模式状态也就是说BmcEpoch 负责时间值 Manager 负责 NTP / Manual 模式9. Manager 的双向监听机制Manager构造函数中会创建两个监听。一个监听systemd-timedated的属性变化timedateMatches.emplace_back( bus, propertiesChanged(systemdTimePath, systemdTimeInterface), [](sdbusplus::message_t m) { onTimedateChanged(m); });另一个监听phosphor-settings中时间同步模式的变化settingsMatches.emplace_back( bus, propertiesChanged(settings.timeSyncMethod, settings::timeSyncIntf), [](sdbusplus::message_t m) { onSettingsChanged(m); });其中 systemd 时间服务相关定义为constexpr auto systemdTimePath /org/freedesktop/timedate1; constexpr auto systemdTimeInterface org.freedesktop.timedate1;settings 时间同步接口为constexpr auto timeSyncIntf xyz.openbmc_project.Time.Synchronization;这就形成了双向同步关系systemd-timedated 的 NTP 状态变化 ↓ onTimedateChanged() ↓ 更新 phosphor-settings phosphor-settings 的 TimeSyncMethod 变化 ↓ onSettingsChanged() ↓ 更新 systemd-timedated10. systemd 状态变化如何同步回 settings当systemd-timedated的 NTP 属性发生变化时会触发onTimedateChanged()该函数会读取 systemd 发来的NTP属性变化。简化逻辑如下bool newNtpMode std::getbool(iter-second);然后根据newNtpMode决定 OpenBMC settings 中的时间同步模式const auto timeMode newNtpMode ? settings::ntpSync : settings::manualSync;如果newNtpMode为 true则写入xyz.openbmc_project.Time.Synchronization.Method.NTP如果newNtpMode为 false则写入xyz.openbmc_project.Time.Synchronization.Method.Manual设置属性时会先查找 settings 对象对应的服务std::string settingManager utils::getService( bus, settings.timeSyncMethod.c_str(), settings::timeSyncIntf);然后写入属性utils::setProperty( bus, settingManager, settings.timeSyncMethod, settings::timeSyncIntf, propertyTimeMode, timeMode);也就是说如果 systemd 侧 NTP 状态变化phosphor-time-manager会把变化同步回 OpenBMC settings。11. settings 变化如何同步到 systemd当phosphor-settings中的时间同步模式变化时会触发onSettingsChanged() ↓ onPropertyChanged() ↓ onTimeModeChanged() ↓ updateNtpSetting()真正执行SetNTP的是updateNtpSetting()。简化代码如下auto method bus.new_method_call( systemdTimeService, systemdTimePath, systemdTimeInterface, methodSetNtp); method.append(isNtp, false); bus.call_noreply(method);其中constexpr auto methodSetNtp SetNTP;如果TimeSyncMethod变成 NTP则调用org.freedesktop.timedate1.SetNTP(true)如果TimeSyncMethod变成 Manual则调用org.freedesktop.timedate1.SetNTP(false)因此OpenBMC settings 中的时间同步模式最终也会同步到 systemd。12. settings::Objects动态发现时间同步设置对象Manager并没有把 settings 的对象路径完全写死而是通过 ObjectMapper 动态查找。关键接口是constexpr auto timeSyncIntf xyz.openbmc_project.Time.Synchronization;构造过程中会调用类似逻辑result getSubTree(bus, root, settingsIntfs, 0);它的作用是从根路径开始查找所有实现 xyz.openbmc_project.Time.Synchronization 接口的对象找到后将对象路径保存到settings.timeSyncMethod系统中常见的 settings 对象路径为/xyz/openbmc_project/time/sync_method对应服务通常为xyz.openbmc_project.Settings对应接口为xyz.openbmc_project.Time.Synchronization这也是Manager后续监听 settings 变化的基础。13. bmcweb 如何暴露时间能力bmcweb负责把 OpenBMC 时间管理能力暴露给 Redfish 和 WebUI。从外部看用户可以通过 WebUI 或 Redfish 进行查看 BMC 当前时间设置 BMC 当前时间查看 NTP 是否开启开启或关闭 NTP查看或配置 NTP Server这些能力最终都会被 bmcweb 转换为 D-Bus 调用。14. Manager DateTime 字段在 Redfish Manager 资源中时间相关字段包括DateTime DateTimeLocalOffset设置时间时bmcweb 会调用systemd-timedated的SetTime。简化逻辑如下crow::connections::systemBus-async_method_call( ..., org.freedesktop.timedate1, /org/freedesktop/timedate1, org.freedesktop.timedate1, SetTime, us-count(), relative, interactive);可以看到Web/Redfish 设置时间时并不是通过phosphor-time-manager的Elapsed属性间接设置而是直接调用org.freedesktop.timedate1.SetTime这和BmcEpoch::setTime()的底层目标是一样的。因此可以得出结论Web 侧 DateTime 设置 ↓ bmcweb ↓ systemd-timedated.SetTime D-Bus Elapsed 设置 ↓ phosphor-time-manager ↓ systemd-timedated.SetTime两条路径最终都落到timedated.SetTime。15. NTP 开关ManagerNetworkProtocol在 Redfish 中NTP 开关通常通过 ManagerNetworkProtocol 资源暴露。对应字段类似NTP.ProtocolEnabledbmcweb 提供的能力包括读取 NTP 状态PATCH NTP 开关读取 NTP Server 列表读取 NTP 状态时bmcweb 读取systemd-timedated的NTP属性sdbusplus::asio::getProperty( *crow::connections::systemBus, org.freedesktop.timedate1, /org/freedesktop/timedate1, org.freedesktop.timedate1, NTP, callback);写 NTP 状态时bmcweb 调用crow::connections::systemBus-async_method_call( std::move(callback), org.freedesktop.timedate1, /org/freedesktop/timedate1, org.freedesktop.timedate1, SetNTP, ntpEnabled, interactive);可以看到Redfish 的 NTP 开关最终也是调用org.freedesktop.timedate1.SetNTP因此WebUI 上的 NTP 开关控制的是systemd-timedated随后phosphor-time-manager再通过监听timedated状态变化把结果同步回phosphor-settings。16. OpenBMC 时间管理调用路径总结16.1 设置系统时间通过 D-Bus 设置时间写 /xyz/openbmc_project/time/bmc 的 Elapsed ↓ BmcEpoch::elapsed(value) ↓ BmcEpoch::setTime() ↓ org.freedesktop.timedate1.SetTime通过 Redfish/WebUI 设置时间WebUI / Redfish ↓ bmcweb ↓ org.freedesktop.timedate1.SetTime两者最终目标相同都是systemd-timedated.SetTime16.2 设置 NTP 开关通过 settings 设置时间同步模式修改 TimeSyncMethod ↓ phosphor-settings ↓ phosphor-time-manager::onSettingsChanged() ↓ org.freedesktop.timedate1.SetNTP通过 Redfish/WebUI 设置 NTPWebUI / Redfish ↓ bmcweb ↓ org.freedesktop.timedate1.SetNTP ↓ phosphor-time-manager 监听 timedated 变化 ↓ 同步回 phosphor-settings16.3 timedated 状态同步回 settingssystemd-timedated NTP 属性变化 ↓ phosphor-time-manager::onTimedateChanged() ↓ 设置 TimeSyncMethod ↓ phosphor-settings17. 调试命令参考查看 BMC 时间对象busctl tree xyz.openbmc_project.Time.Manager查看 BMC 时间接口busctl introspect \ xyz.openbmc_project.Time.Manager \ /xyz/openbmc_project/time/bmc读取 BMC 时间属性busctl get-property \ xyz.openbmc_project.Time.Manager \ /xyz/openbmc_project/time/bmc \ xyz.openbmc_project.Time.EpochTime \ Elapsed查看 systemd timedatedbusctl introspect \ org.freedesktop.timedate1 \ /org/freedesktop/timedate1读取 NTP 状态busctl get-property \ org.freedesktop.timedate1 \ /org/freedesktop/timedate1 \ org.freedesktop.timedate1 \ NTP查看时间同步模式 settingsbusctl introspect \ xyz.openbmc_project.Settings \ /xyz/openbmc_project/time/sync_method查看服务状态systemctl status phosphor-time-manager systemctl status systemd-timedated systemctl status bmcweb查看日志journalctl -u phosphor-time-manager journalctl -u bmcweb18. 总结OpenBMC 的时间管理不是一个简单的“修改系统时间”问题而是一个由 D-Bus、settings、systemd 和 Redfish 共同协作的机制。可以概括为phosphor-time-manager 负责导出 BMC 时间对象并协调时间同步模式 phosphor-settings 保存 TimeSyncMethod 等配置 systemd-timedated 真正执行 SetTime 和 SetNTP bmcweb 通过 Redfish 把时间能力暴露给 WebUI 和外部客户端 WebUI 是 Redfish 接口的前端展示和操作入口其中最关键的两条路径是时间值设置最终落到 systemd-timedated.SetTime NTP/Manual 模式切换最终落到 systemd-timedated.SetNTP而phosphor-time-manager的核心价值在于对内提供 OpenBMC 标准 D-Bus 时间对象 并在 phosphor-settings 与 systemd-timedated 之间维护时间同步模式的一致性。