D-Bus Tutorial

D-Bus Tutorial

What is D-Bus?

D-Bus是一个用于进程间通讯的系统。从架构上来讲,分为三部分:java

  • 一个libdbus库,容许两个app链接,交换信息
  • 一个基于libdbus的daemon,各类app能够链接到daemon。Daemon将信息从一个app路由到其余apps。
  • 基于app framework的封装库或者绑定。好比,libdbus-glib和libdebus-qt。也有针对语言的绑定,好比Python。大多开发者直接使用的是这些封装库,这些库声明了Dbus相关的编程细节。libdbus做为底层后台,用来支持上层的bindings。libdbus的不少API只对binding的实现有效。

libdbus只支持one-to-one类型的链接,如同原始套接字那样,区别是,发送的不是字节流(byte streams)而是消息。消息包含消息header和body,header标明信息类型,body包含消息主体内容。 Libdbus也能够容许实现特定的传输通道,从而来完成好比像认证之类的应用细节。python

消息总线守护进程将D-bus上链接的全部程序构成一个轮形hub。轮子上的每个轮辐都是基于libdbus的一对一链接,链接了一个app。app经过轮辐向daemon发送消息,daemon在合适的时机将消息转发到其余已链接的app。daemon能够理解为一个路由器。编程

总线守护进程在系统中有多个实例。好比全局惟一的实例,一个如同sendmail或者Apache的系统daemon,对于接收什么样消息用来进程间通讯,有着很严格的安全要求(system bus?)。另一些daemon实例由用户登陆时创建,用于该用户的session中的app之间通讯。api

系统级的daemon和per-user daemon是分离的。session内的IPC不会影响系统级的message bus,反之亦然。安全

D-Bus applications

有许多技术用来实现IPC或者网络信息传递,好比:CORBA, DCE, DCOM, DCOP, XML-RPC, SOAP, MBUS, Internet Communications Engine (ICE)。每一种都是针对特定种类的应用程序量身定作的。
Dbus为两种状况而生:网络

  • 同一desktop session中的app之间信息传递,这样就将session视为一个总体,解决进程生命周期的问题。
  • desktop session与操做系统之间的信息传递,即session和kernel或者其余系统daemon之间的通讯。

Concepts

图片描述

Native Objects and Object Paths

开发者在本身的编程架构中会定义object,一般基于一个基础的类,好比 java.lang.Object, GObject, QObject, python's base Object, or whatever. 咱们称为native object。session

dbus协议和libdbus中的api不关心native object如何定义,而是提供了object path的概念。有了object path,上层的bindings就可以命名native object实例,而且容许远程的app引用这些object。
object path如同文件系统中的文件路径。好比,一个object能够被命名为/org/kde/kspread/sheets/3/cells/4/5。易于理解的路径很棒,若有须要的话,是你也能够命名为诸如 /com/mycompany/c5yo817y0c1y1c5b。
名称中指定命名空间是明智的作法,好比将路径开头设置为你开发的domain名称。这样能够保证同一进程中的不一样模块互不干扰。架构

Methods and Signals

每一个object都有成员(members),成员分为方法(methods)和信号(signals)。方法(methods)是object能够调用的一组操做,带有输入或者输出参数。信号被广播到任何一个对该信号感兴趣的对象;信号也能够承载数据。app

引用方法或者信号都是经过它们的名字,好比Frobate或者OnClickeddom

Interfaces

一个object只是一个或多个interface。一个interface中包含了一组方法和信号(methods and signals),概念如同 GLib or Qt or Java中的方法和信号。接口定义了object实例的类型。
dbus用简单的命名空间字符串标识一个interface,好比org.freedesktop.Introspectable;大多数绑定会将这些interface直接映射到对应的编程语言结构,好比Java接口或者C++的纯虚类。

Proxies

代理(proxy)object是native object,用于表明其余进程中的远程object。底层的DBus API建立一个Methods call,发送,而后接收,处理这些回复的消息。能够把代理看作是一个普通的native object,可是当你调用代理中的方法,绑定(bindings)将该操做转化为
dbus Method call消息,等待远端回复消息,将返回值解包,而后返回到native 方法......

如下是一个伪代码的例子,不使用proxy:

Message message = new Message("/remote/object/path", "MethodName", arg1, arg2);
  Connection connection = getBusConnection();
  connection.send(message);
  Message reply = connection.waitForReply(message);
  if (reply.isError()) {
     
  } else {
     Object returnValue = reply.getReturnValue();
  }

使用proxy

Proxy proxy = new Proxy(getBusConnection(), "/remote/object/path");
  Object returnValue = proxy.MethodName(arg1, arg2);

Bus Names

每当app链接dbus daemon时,daemon为app分配一个惟一的链接名字,以冒号开头。一个bus name永远指向同一个app,不会被复用。例如:34-907。冒号后面的数字除了它们的惟一性没有意义。
app也能够请求便于理解的名字。好比,com.mycompany.TextEditor。对应的object的路径能够为/com/mycompany/TextFileManager,interface为org.freedesktop.FileHandler

能够将:34-907(unique name)看成是IP地址,com.mycompany.TextEditor看成是域名。com.mycompany.TextEditor映射 :34-907就如沟通mycompany.com映射到192.168.0.5。

除了路由消息,bus name还可用来追踪app生命周期。当一个app退出或者crash时,操做系统内核(operating system kernel)会断开message bus的链接,而后message bus发送消息通知
其余app该name已经失去owner了。经过追踪这些notification,能够检测其余app的生命周期。

Bus name名字还能够检测应用是否已经启动,这能够用来实现单实例启动程序。

Addresses

应用做为dbus的server或者client,server监听来自client的链接,client链接到server。一旦链接创建,它就是一个对称的消息流。
Dbus address指明server去哪监听,client去哪里链接。例如,地址unix:path=/tmp/abcdef指明了server将监听一个Unix socket,路径是/tmp/abcdef, client将链接到这个socket。
address也能指定TCP/IP sockets,或者其余类型的传输方式。

Big Conceptual Picture

把上面的概念组合起来获得如下流程:

Address -> [Bus Name] -> Path -> Interface -> Method

Message

Dbus工做原理是在两个进程之间发送消息,若是工做在至关上层的binging中,是不会直接处理消息的。
四种类型消息:

  • Method call消息,用来调用一个object的一个方法
  • message return消息,返回一个Method的处理结果
  • 错误消息,返回method调用中的exception
  • 信号消息,通知一个指定信号触发了,能够理解成事件触发
  • method call消息对应一个message return消息或者一个错误消息

每一个消息有一个消息头,包含field和body和桉树。能够将header看成消息的路由信息,body看成消息体。header可能包含如下信息:发送方bus name,目的地的busname,调用的方法或者信号,等等。
header中有一个描述body中值的类型的field。好比,字母i岱庙32位整数,ii标识body有两个32位整数。

Calling a Method

一个方法调用(method call)由两条消息组成,一个方法调用消息从进程A发送给进程B,对应的方法返回消息从进程B发送给进程A,发送和返回消息都要通过bus daemon路由。
每一个call消息包含一串独有的数字,reply消息也包含这个数字以便和发送方匹配。

发送消息带有参数,会传递给远程method,回复消息可能包含一个错误或者远程method返回的数据。

Dbus中的方法调用流程以下:

  • 语言相关的绑定可能提供一个代理(proxy),调用一个本进程object内的方法,其实是调用远程进程的object上的一个方法。这种状况下,代理上会组织一个方法调用消息发送到远程进程。
  • 对于底层的API,应用能够本身组织一个方法调用消息,不使用proxy。
  • 方法调用消息会包含如下信息:远程进程的bus
    name;方法名称;方法须要的参数;远程进程的object路径;包含method的接口(interface)(可选)
  • 方法调用消息被发送到bus daemon
  • bus daemon根据目的地bus name找到远程进程,将消息发往目标进程,若找不到,则返回错误
  • 接收方进程对消息进行解包。简单的使用底层API的状况下,可能当即调用方法并返回结果到bus
    daemon。当时用上层绑定时,binding可能检查object路径,接口,方法,而后将method call消息转换为对本地object(GObject, java.lang.Object, QObject,etc.)的调用,而后将本地方法的返回值转换为方法的返回消息。
  • Bus daemon收到方法的返回消息而后发送给method call的发送方
  • 发送方收到reply消息,解析数据,也可能返回错误。当使用绑定时,返回消息会被转换成代理方法(proxy
    method)的返回值或者exception。

Bus daemon不会为消息排序,也就是说,若是发送两条method call消息到同一接收方,接收的顺序与发送顺序相同。接收方不必定按照接收的顺序来回复消息。好比,接收方可能在两个不一样的线程中处理method call,哪一个消息先处理完就先返回。

Emitting a Signal

信号由一条消息组成,从一个进程发送到另外一个或多个进程。也就是说,信号是单向的广播。信号可能包含参数(数据部分),由于它是一个广播,没有返回值。

信号的发送者不知道接受者是谁。接收者为了接收信号须要向bus daemon进行注册,基于“match rules”,这些规则包含了发送者和接受者的名字。Bus daemon只将信号发送给对该信号感兴趣的接收者。

信号的流程大体以下:

  • 当使用dbus底层接口时,信号须要应用本身建立和发送到daemon,使用dbus高层接口时,可使用相关对象进行发送,如Glib里面提供的信号触发机制。
  • 信号包含的内容有:信号的接口名称,信号名称,发送进程的bus name,以及其余参数。
  • 任何进程均可以依据”match rules”注册相关的信号,daemon有一张注册的列表。
  • daemon检测信号,决定哪些进程对这个信号感兴趣,而后把信号发送给这些进程
  • 每一个进程收到信号后,若是是使用了dbus高层接口,能够选择触发代理对象上的信号。若是是dbus底层接口,须要检查发送者名称和信号名称,而后决定怎么作。

Introspection

Dbus objects支持org.freedesktop.DBus.Introspectable,dbus的标准接口,无需参数,返回一个XML字符串,描述了接口,方法,信号。

相关文章
相关标签/搜索