D-Bus 是一个进程间通信(IPC)和远程过程调用(RPC)机制,最初是为了 Linux 开发,用来取代现有的竞争的 IPC 解决方案,提供一个统一的协议。它也被设计来允许系统级进程(比如打印机和硬件驱动程序服务)和普通用户进程之间的通信。
它使用一个快速的二进制消息传递协议,适合在同一台机器上的通信,因为其低延迟和低开销。其规范当前由 freedesktop.org 项目定义,并向所有方开放。
通信通常是通过一个中心的服务器应用程序,称为“总线”(因此得名),但是应用程序之间的直接通信也是可能的。在总线上通信时,应用程序可以查询其他可用的应用程序和服务,也可以按需激活一个。
D-Bus 总线用于需要多对多通信的场景。为了实现这一点,在任何应用程序连接到总线之前,都需要启动一个中心服务器:这个服务器负责跟踪已连接的应用程序,并正确将消息从源路由到目的地。
此外,D-Bus 定义了两个众所周知的总线,称为系统总线和会话总线。这些总线的特殊之处在于它们有明确定义的语义:一些服务被定义为可以在这两个总线中的一个或两个总线中找到。
例如,一个想要查询连接到计算机的硬件设备列表的应用程序,可能会与系统总线上提供的一个服务进行通信,而提供打开用户浏览器的服务可能会在会话总线上找到。
在系统总线上,也可以期望找到对每个应用程序允许提供的服务的限制。因此,可以合理确定,如果某个服务存在,它是被一个可信的应用程序提供的。
在底层,应用程序通过向彼此发送消息在D-Bus上进行通信。消息被用来传递远程过程调用以及与之相关的回复和错误。当在总线上使用时,消息具有一个目的地,这意味着它们仅被路由到感兴趣的各方,避免了由“蜂拥”或广播引起的拥塞。
一种特殊的称为“信号消息”的消息(这个概念基于Qt的信号和槽机制),然而,没有预定义的目的地。由于其目的是在一对多的上下文中使用,因此信号消息被设计为在“按需选择”机制上工作。
Qt D-Bus模块将底层的消息概念完全封装成了一种更简单的、面向Qt开发者熟悉的面向对象的方式。在大多数情况下,开发者不必担心发送或接收消息。
它通过信号和槽以及调用远程接口的方法来抽象底层的D-Bus消息传递,使开发者可以更轻松地使用D-Bus的IPC功能,而不需要关注底层的消息发送和接收。
在总线上进行通信时,应用程序会获取所谓的“服务名称”:这是应用程序选择在同一总线上的其他应用程序中所知道的名称。服务名称由 D-Bus 总线守护进程进行管理,并用于在一个应用程序与另一个应用程序之间路由消息。一个类似服务名称的概念是 IP 地址和主机名:一台计算机通常有一个 IP 地址,并且根据它向网络提供的服务,可能关联有一个或多个主机名。
另一方面,如果不使用总线,也就不会使用服务名称。如果我们再次拿计算机网络进行比较,这相当于点对点网络:由于对端是已知的,因此不需要使用主机名来查找它或它的 IP 地址。
D-Bus 服务名称的格式实际上与主机名非常相似:它是一个由字母和数字组成的以点分隔的序列。常见的做法是根据定义该服务的组织的域名来命名服务名称。
例如,D-Bus 服务是由 freedesktop.org 定义的,可以在总线上以服务名称 org.freedesktop.DBus 找到。
所以在 D-Bus 中服务名称和 IP 地址/主机名类似,用于标识提供服务的应用程序。选择一个合理的服务名称有助于组织和管理 D-Bus 上的服务。
org.freedesktop.DBus
像网络主机一样,应用程序通过导出对象为其他应用程序提供特定的服务。这些对象以层次结构组织,非常类似于从QObject派生的类所具有的父子关系。然而,有一个不同之处是,存在“根对象”的概念,所有的对象都将其作为最终父对象。
如果我们继续 Web 服务的类比,对象路径相当于 URL 的路径部分:
例如,在 Web 中,路径 /users/12345 标识一个特定的用户资源。在 D-Bus 中,对象路径也以相似的方式标识对象。
像文件系统中的路径名一样,D-Bus 中的对象路径由斜杠分隔的标签组成,每个标签由字母、数字和下划线字符(“_”)组成。对象路径必须以斜杠开头,且不能以斜杠结尾。
接口类似于C++抽象类和Java的接口关键字,并声明在调用者和被调用者之间建立的“契约”。,也就是说,它们建立了可用的方法、信号和属性的名称,以及在建立通信时从任何一方所期望的行为。
Qt在其插件系统中使用了一个非常类似的机制:C++中的基类通过Q_DECLARE_INTERFACE()
宏与唯一标识符相关联。
事实上,D-Bus
接口名称的命名方式类似于Qt插件系统所建议的方式:通常由定义该接口的实体的域名构造的标识符。
服务名 | 类比于 | 名字格式 |
---|---|---|
服务名 | 网络主机名 | 点分割,看起来像一个主机名 |
对象路径 | URL路径 | 斜杠分割,看起来像个路径 |
接口 | 插件id | 点分割 |
在开发使用D-Bus的应用程序时,能够看到每个应用程序通过总线发送和接收的消息的相关信息有时会非常有用。
这个功能可以通过在运行每个应用程序之前设置QDBUS_DEBUG
环境变量来针对每个应用程序启用。例如,我们可以只为D-Bus
远程控制汽车示例中的汽车启用调试,通过以下方式运行控制器和汽车:
examples/dbus/remotecontrolledcar/controller/controller &
QDBUS_DEBUG=1 examples/dbus/remotecontrolledcar/car/car &
信息会直接打印到启动程序的控制台。
麒麟V10 会用到D-Bus,而且用的比较重。我自己试了直接把 下图的678进程杀掉,系统直接黑屏。