D-Bus 对象模型

由于 D-Bus 最初的概念是作为几个面向组件的通信系统的替代品,因此 D-Bus 与其前身共享一个对象模型,在该对象模型中表达客户端和服务之间通信的语义。D-Bus 对象模型中使用的术语模仿了一些面向对象编程语言使用的术语。但这并不意味着 D-Bus 在某种程度上仅限于 OOP 语言 —— 事实上,最常用的实现(libdbus)是用 C 编写的,这是一种过程编程语言。

在 D-Bus 中,进程通过暴露对象来提供服务。这些对象具有可以调用的方法,以及对象可以发出的信号。方法和信号统称为对象的成员。任何连接到总线的客户端都可以通过使用对象的方法、发出请求或命令对象执行操作来与对象交互。例如,表示时间服务的对象可以由客户端使用返回当前日期和时间的方法来查询。客户端还可以侦听对象在其状态由于某些事件而发生变化时发出的信号,这些事件通常与底层服务相关。例如,当管理硬件设备(例如 USB 或网络驱动程序)的服务发出“添加新硬件设备”事件的信号时。客户应该指示总线他们有兴趣从特定对象接收某些信号,因为 D-Bus 总线只将信号传递给那些对它们感兴趣的进程。

连接到 D-Bus 总线的进程可以请求它导出任意数量的 D-Bus 对象。每个对象由一个对象路径标识,由一串数字、字母和下划线分隔并以斜杠字符为前缀,因为它们与 Unix 文件系统路径相似,所以称其为对象路径。对象路径由请求进程选择,并且在该总线连接的上下文中必须是唯一的。有效对象路径的示例是 /org/kde/kspread/sheets/3/cells/4/5。然而,在对象路径中形成层次结构并没有被强制(但也不被劝阻)。服务对象的特定命名约定完全取决于此类服务的开发人员,但许多开发人员选择使用项目保留的域名来命名它们的前缀(例如 /org/kde)。

每个对象都不可避免地与导出它的特定总线连接相关联,并且从 D-Bus 的角度来看,它只存在于这种连接的上下文中。因此,为了能够使用某个服务,客户端不仅必须指明提供所需服务的对象路径,还必须指明服务进程连接到总线的总线名称。这反过来又允许连接到总线的几个进程可以毫不含糊地导出具有相同对象路径的不同对象。

接口指定可以与对象一起使用的成员(方法和信号)。它是一组方法声明(包括其传递和返回参数)和信号(包括其参数),这些声明由类似 Java 语言接口符号的点分隔名称标识。一个有效接口名称的例子是 org.freedesktop.Introspectable。尽管接口名称和总线名称相似,但注意不要弄错了。一个 D-Bus 对象可以实现多个接口,但至少必须实现一个,为它定义的每个方法和信号提供支持。 一个对象实现的所有接口的组合称为对象类型。

使用对象时,客户端进程最好在成员名称之外提供成员的接口名称,但仅当对象实现的不同接口中可用的成员名称重复而导致歧义时才强制执行 —— 否则,选定的成员未定义或错误。另一方面,发出的信号必须始终指示它属于哪个接口。

D-Bus 规范还定义了对象可能想要实现的几个标准接口,除了它自己的接口。尽管在技术上是可选的,但大多数 D-Bus 服务开发人员选择在他们的导出对象中支持它们,因为它们为 D-Bus 总线客户端,例如 introspection。这些标准接口是:

  • org.freedesktop.DBus.Peer:提供了一种测试 D-Bus 连接是否有效的方法。
  • org.freedesktop.DBus.Introspectable:提供了一种自省机制,通过该机制,客户端进程可以在运行时获取对象实现的接口、方法和信号的描述(以 XML 格式)。
  • org.freedesktop.DBus.Properties:允许 D-Bus 对象公开底层的本机对象属性或属性,或者在不存在时模拟它们。
  • org.freedesktop.DBus.ObjectManager:当 D-Bus 服务分层排列其对象时,此接口提供了一种方法来查询对象关于其路径下的所有子对象,以及它们的接口和属性,使用单个方法调用。

D-Bus 规范定义了许多管理总线操作(称为“总线服务”) ,这些操作将使用驻留在 org.freedesktop.DBus 总线名称中的 /org/freedesktop/DBus 对象来执行。每条总线都为自己保留这个特殊的总线名称,并管理专门针对这个总线名称和对象路径组合的任何请求。总线提供的管理操作是由对象的接口 org.freedesktop.DBus 定义的。例如,这些操作用于提供有关总线状态的信息,或管理其他知名总线名称的请求和释放。