D-Bus学习
有很多IPC(interprocess communication ) ,用于不同的解决方案:CORBA 是用于面向对象编程中复杂的 IPC 的一个强大的解决方案。DCOP 是一个较轻量级的 IPC 框架,功能较少,但是可以很好地集成到 K 桌面环境中。SOAP 和 XML-RPC 设计用于 Web 服务,因而使用 HTTP 作为其传输协议。D-BUS 设计用于桌面应用程序和 OS 通信。D-Bus(其中D原先是代表桌面“Desktop” 的意思),即:用于桌面操作系统的通信总线。现在逐渐被引入到嵌入式系统中,不过名字还是保留原先的叫法而已。
典型的桌面都会有多个应用程序在运行,而且,它们经常需要彼此进行通信。DCOP 是一个用于 KDE 的解决方案,但是它依赖于 Qt,所以不能用于其他桌面环境之中。类似的,Bonobo 是一个用于 GNOME 的解决方案,但是非常笨重,因为它是基于 CORBA 的。它还依赖于 GObject,所以也不能用于 GNOME 之外。 D-BUS 的目标是将 DCOP 和 Bonobo 替换为简单的 IPC,并集成这两种桌面环境。由于尽可能地减少了 D-BUS 所需的依赖,所以其他可能会使用 D-BUS 的应用程序不用担心引入过多依赖。相对于其它的IPC, D-Bus丢掉了一些不必要的、复杂的东西,也正是因为这个原因,D-Bus比较快、简单。D-Bus不和低层的IPC直接竞争,比如sockets, shared memory or message queues.这些低层点的IPC有它们自己的特点,和D-Bus并不冲突。
D-Bus的三个层面
D-Bus是一个为应用程序间通信的消息总线系统, 用于进程之间的通信。它是个3层架构的IPC 系统,包括:
- 函数库libdbus ,用于两个应用程序互相联系和交互消息。
- 一个基于libdbus构造的消息总线守护进程,可同时与多个应用程序相连,并能把来自一个应用程序的消息路由到0或者多个其他程序。
- 基于特定应用程序框架的封装库或捆绑(wrapper libraries or bindings )。例如,libdbus-glib和libdbus-qt,还有绑定在其他语言,例如Python的。大多数开发者都是使用这些封装库的API,因为它们简化了D-Bus编程细节。libdbus被有意设计成为更高层次绑定的底层后端(low-level backend )。大部分libdbus的 API仅仅是为了用来实现绑定。
System bus和session bus
在D-Bus中,“bus”是核心的概念,它是一个通道:不同的程序可以通过这个通道做些操作,比如方法调用、发送信号和监听特定的信号。在一台机器上总线守护有多个实例(instance)。这些总线之间都是相互独立的。
一个持久的系统总线(system bus):
- 它在引导时就会启动。这个总线由操作系统和后台进程使用,安全性非常好,以使得任意的应用程序不能欺骗系统事件。
- 它是桌面会话和操作系统的通信,这里操作系统一般而言包括内核和系统守护进程。
- 这种通道的最常用的方面就是发送系统消息,比如:插入一个新的存储设备;有新的网络连接;等等。
还将有很多会话总线(session buses):
- 这些总线当用户登录后启动,属于那个用户私有。它是用户的应用程序用来通信的一个会话总线。
- 同一个桌面会话中两个桌面应用程序的通信,可使得桌面会话作为整体集成在一起以解决进程生命周期的相关问题。 这在GNOME和KDE桌面中大量使用。
对于一些远程的工作站,在system bus中可能会有一些问题,例如热插拔,是否需要通知远端的terminal,这会是的kernel暴露一些设备的能力,不过,我们现在关心D-Bus,是因为手持终端设备的使用,这些将不会出现。在Internet Tablet,也包括我们的手机系统,所有的应用程序都是使用一个用户ID运行的,所以只有一个会话通道,这一点是和Linux桌面系统是有明显区别的。
D-Bus是低延迟而且低开销的,设计得小而高效,以便最小化传送的往返时间。另外,协议是二进制的,而不是文本的,这样就排除了费时的序列化过程。从开发者的角度来看,D-BUS 是易于使用的。有线协议容易理解,客户机程序库以直观的方式对其进行包装。D-Bus的主要目的是提供如下的一些更高层的功能:
- 结构化的名字空间
- 独立于架构的数据格式
- 支持消息中的大部分通用数据元素
- 带有异常处理的通用远程调用接口
- 支持广播类型的通信
Bus daemon总线守护
Bus daemon是一个特殊的进程:这个进程可以从一个进程传递消息给另外一个进程。当然了,如果有很多applications链接到这个通道上,这个 daemon进程就会把消息转发给这些链接的所有程序。在最底层,D-Bus只支持点对点的通信,一般使用本地套接字(AF_UNIX)在应用和bus daemon之间通信。D-Bus的点对点是经过bus daemon抽象过的,由bus daemon来完成寻址和发送消息,因此每个应用不必要关心要把消息发给哪个进程。D-Bus发送消息通常包含如下步骤(正常情况下):
- 创建和发送消息 给后台bus daemon进程,这个过程中会有两个上下文的切换
- 后台bus daemon进程会处理该消息,并转发给目标进程,这也会引起上下文的切换
- 目标程序接收到消息,然后根据消息的种类,做不同的响应:要么给个确认、要么应答、还有就是忽略它。最后一种情况对于“通知”类型的消息而言,前两种都会引起进一步的上 下文切换。
综上原因,如果你准备在不同的进程之间传递大量的 数据,D-Bus可能不是最有效的方法,最有效的方法是使用共享内存,但是对共享内存的管理也是相当复杂的。
D-Bus的方式在移动手机操作系统中非常重要,包括Maemo,Moblin等以Linux为基础的操作系统。估计Andriod也大量使用。D-Bus的相关学习资料见: http://www.freedesktop.org/wiki/Software/dbus ,在网上也有大量的学习资料,在 http://blog.chinaunix.net/u3/111961/ 中有不少的中文资料,收集了很多网络资料。我决定认真学习一下,做一下笔记。主要资料来源于上面两个网站,尤其是freedesktop的D-Bus Tutorial,并参考了wiki,以及 http://www.ibm.com/developerworks/cn/linux/l-dbus.html 和 http://blog.csdn.net/cuijpus/archive/2008/01/30/2073962.aspx 。
原生对象和对象路径
所有使用D-BUS的应用程序都包含一些对象, 当经由一个D-BUS连接受到一条消息时,该消息是被发往一个对象而不是整个应用程序。在开发中程序框架定义着这样的对象,例如JAVA,GObject,QObject等等,在D-Bus中成为native object。
对于底层的D-Bus协议,即libdbus API,并不理会这些native object,它们使用的是一个叫做object path的概念。通过object path,高层编程可以为对象实例进行命名,并允许远程应用引用它们。这些名字看起来像是文件系统路径,例如一个对象可能叫做“/org/kde/kspread/sheets/3/cells/4/5”。 易读的路径名是受鼓励的做法,但也允许使用诸如“/com/mycompany/c5yo817y0c1y1c5b”等,只要它可以为你的应用程序所用。Namespacing的对象路径以开发者所有的域名开始(如 /org/kde)以避免系统不同代码模块互相干扰。
简单地说:一个应用创建对象实例进行D-Bus的通信,这些对象实例都有一个名字,命名方式类似于路径,例如/com/mycompany,这个名字在全局(session或者system)是唯一的,用于消息的路由。
方法和信号Methods and Signals
每一个对象有两类成员:方法和信号。方法就是JAVA中同样概念,方法是一段函数代码,带有输入和输出。信号是广播给所有感兴趣的其他实体,信号可以带有数据payload。
Tutorial这里说法有点不太清楚。在 D-BUS 中有四种类型的消息:方法调用(method calls)、方法返回(method returns)、信号(signals)和错误(errors)。要执行 D-BUS 对象的方法,您需要向对象发送一个方法调用消息。它将完成一些处理(就是执行了对象中的Method,Method是可以带有输入参数的。)并返回,返回消息或者错误消息。
信号的不同之处在于它们不返回任何内容:既没有“信号返回”消息,也没有任何类型的错误消息。
接口Interface
每一个对象支持一个或者多个接口,接口是一组方法和信号,接口定义一个对象实体的类型。D-Bus对接口的命名方式,类似于org.freedesktop.Introspectable。开发人员通常将使用编程语言类的的名字作为接口名字。
Proxies代理
代理对象用来表示其他远程的remote object。当我们触发了proxy对象的method时,将会在D-Bus上发送一个method_call的消息,并等待答复,根据答复然后返回。使用非常方便,就像调用一个本地的对象。
上面是从开发人员编程的角度,经常涉及的一些概念,下面是D-Bus在工作或者处理时所涉及的一些概念。
Bus Names总线名字
当一个应用连接到bus daemon,daemon立即会分配一个名字给这个连接,称为unique connection name ,这个唯一标识的名字以冒号:开头,例如:34-907,这个名字在daemon的整个生命周期是唯一的。但是这种名字总是临时分配,无法确定的,也难以记忆,因此应用可以要求有另外一个名字well-known name 来对应这个唯一标识,就像我们使用域名来对应IP地址一样。例如可以使用com.mycompany来映射:34-907。应用程序可能会要求拥有额外的周知名字(well-known name ) 。例如,你可以 定义一个名字叫做 com.mycompany.TextEditor。你的协议可以指定自己拥有这个名字,一个应用程序应该在路径/com/mycompany/TextFileManager下有一个支持接口org.freedesktop.FileHandler的对象。应用程序就可以发送消息到这个总线名字,对象,和接口以执行方法调用。
当一个应用结束或者崩溃时,OS kernel会关闭它的总线连接。总线发送notification消息告诉其他应用,这个应用的名字已经失去他的owner。当检测到这类notification时,应用可以知道其他应用的生命周期。这种方式也可用于只有一个实例的应用,即不开启同样的两个应用的情况(单实例)。
地址
使用DBus的应用程序不是server就是client,一个server监听进来的连接;一个client连接server.一旦连接建立,双方就是一个对称的消息流;server和client的区别仅仅在建立连接的时候存在。
如果你正在使用dbus daemon, 你的应用程序将是dbus daemon的client。bus daemon监听连接,你的应用程序初始化到bus daemon的连接。
一个D-Bus地址指明了一个server监听什么地方,和一个client将连接到什么地方。例如unix:path=/tmp/abcedf指明server将在路径/tmp/abcedf的UNIX domain socket监听,而client将连接到这个socket。一个地址也可以用TCP/IP socket指定 或者其他的将在D-Bus协议规范中定义的传输方式。
当使用message bus daemon的时候,libdbus将通过读取环境变量自动获取每一个session bus damon的地址。通过检查一个指定的UNIX domain socket路径来获取system bus的地址(可以使用环境变量来覆盖这个地址)。
如果用没有bus daemon的方式使用D-bus,需要你自己来定义哪个应用程序是server,哪个应用程序是client,并自定义一套机制使他们认可并找到server的地址,但这不是通常的做法。
通过上面的描述,我们把所有的概念放到一起,可以获得下面的视图(指定一个特定对象实例的方法调用):
Address –> [Bus Name] –> Path –> Interface –> Method
bus name不是必须的--在使用bus daemon的情况下,bus name仅仅是一个名字用于路由一个method call到正确的应用程序。如果使用点对点的方式直接连接应用程序,Bus Name是不需要的。
Interface 同样是可选的,主要是由于历史原因,DCOP 不需要指定接口, 仅仅是禁止在同一个对象实例中使用相同的方法名。 DBus让你去掉interface, 但是如果你的method名是不明确的,哪一个method将会被调用是没有定义的。
简单地说 :Address是D-Bus中server用来监听client的地址,当一个client连接上D-Bus,通常是Daemo的方式,这个client就有了一个Bus Name。其他应用可以根据消息中所带的Bus Name,来判断和哪个应用相关。消息在总线中传递的时候,传递到应用中,再根据object path,送至应用中具体的对象实例中,也就是应用中根据Interface创建的对象。这些Interface有method和singal两种,用来发送、接收、响应消息。
这些概念对初学者可能会有些混淆,但是通过后面学习一个小程序,就很清楚,这是后面一个例子的示意图,回过头来看看之前写的这篇文章,这个示意图或许会更清楚。
消息通过D-Bus在进程间传递。如果你使用一个足够高层的捆绑,或许不用直接和消息打交道。
有四类消息:
一、Method call消息:将触发对象的一个method
二、Method return消息:返回触发的方法的结果
三、Error消息:返回触发的方法后产生的一个异常
四、Signal消息:通知,可以看作为事件消息。
一个消息有两部分:
1.消息头header,包含field,消息头包含消息体的路由信息,头field可能包括发送者的bus名,
目的地的bus名,方法或者signal名等等,其中一个头的field是用于描述body中的参数值的类型,
例如“i“表示32位整数,"ii”表示净荷为2个32位整数。
2.消息体body,包含参数arguments。消息体是payload。
发送Method call消息的场景
一个DBus中的method call消息包含了两个消息,从进程A发送到进程B的一个method call消息,
一个相应的从进程B发送到进程A的返回消息,这两个消息都是通过bus daemon路由的。发送者会在
每一个method call消息中加入一个不同的序列号,返回消息中包含同样的号码来让发送者匹配。
call message包含所有的method的参数。返回消息可以是一个错误,是从被调用method返回的数据。
处理过程如下:
* 如果使用高层绑的proxy,通过触发本地一个对象(proxy)的方法从而触发另一个进程的远端对象的方法。
应用调用proxy的一个方法,proxy构造一个method call消息发送到远端进程。
* 对于底层的API,应用可以不使用proxy,需要自己构造method call消息。
* 无论以上的两种方式中的哪一个,一个method call消息包含:远端进程的bus name,
远端进程中object path,方法名字,方法的参数,可选的接口名字(指定了method)。
* method call消息发送到bus daemon
* bus daemon查看目的地的bus name。如果一个进程对应这个名字,bus daemon将method call消息发送到该进程中。
如果没有发现匹配,bus daemon创建一个error消息作为应答message返回。
* 进程接收后将method call消息分拆。对于简单的底层API情况,将立即执行方法,
并发送一个method reply消息给bus daemon。对于高层绑定的API,它将检查对象path,interface和method名,
把method call message 转化为一个对本地对象的方法调用,再将本地方法的返回值封装在一个method reply消息中。
* bus daemon收到method reply消息,将其转发到原来的进程中
* 进程查看method reply消息,获取返回值。这个method replay消息也可以标识一个error的发生。
当使用高级的的捆绑,method reply消息将被转换为proxy方法的返回值,或者一个exception。
Bus daemon保证message的顺序,不会乱序。例如我们发送两个method call消息到同一个接受方,
他们将按顺序接受。接收方并不要求一定按顺序回复,例如用两个线程分别处理不同的method call消息。
method call消息有一个唯一的序列号来匹配返回的消息。
发送Signal的场景
DBus 中的signal只包含了一个单独的消息,没有返回消息,由一个进程发送给其他多个进程,是个单向广播,不需要响应。
signal可以包含 arguments(a data payload), 但是因为它是广播,它不会有返回值。
和method call 的message对比,method call message 会有对应的返回的message。
signal的发送者并不知道signal的接受者。接收者在bus daemon上注册要接收什么样的signal
(根据匹配规则,包括发送者和signal名),bus daemon把每一个signal只发送到那些对这个signal感兴趣的接收者。
处理流程如下:
* 一个signal消息被创建并发送到bus daemon。如果使用底层API,需要手动创建。用绑定会自动做。
* signal消息中包含了发布该信号的interface名字,signal的名字,发送进程的bus名字,以及参数。
* 任何进程都可以通过所注册的匹配条件(match rules)来表明它所感兴趣的signal。总线有个注册match rules列表。
* bus daemon检查哪些进程对该信号有兴趣,将信号消息发送到这些进程中。
* 收到信号的进程决定如何处理。如果使用高层的捆绑,那么绑定可以选择发送一个本地信号给一个porxy对象。
如果使用底层的API,接受进程需要检查信号的发送者和信号的名字来决定如果进行处理。
Introspection反省自省
D-Bus对象可能支持一个接口org.freedesktop.DBus.Introspectable。该接口有一个方法Introspect,
不带参数,将返回一个XML string。这个XML字符串描述了这个对象,包括它的接口(方法和信号), objects below it in the object path tree, and its properties。
D-Bus提供两个命令dbus-monitor,可以查看bus,dbus-send命令,可以发送消息,可以用man来检查:
dbus-send [--system | --session] --type=method_call(或者是signal,缺省是signal) --print-reply
--dest=连接名 对象路径 接口名.方法名 参数类型:参数值 参数类型:参数值
我们通过这个接口.方法来更好地了解D-Bus。我们使用到一个方法ListNames来查看:
[wei@wei-desktop ~]$ dbus-send --print-reply --type=method_call --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListNames
method return sender=org.freedesktop.DBus -> dest=:1.75 reply_serial=2
array [
string "org.freedesktop.DBus"
string ":1.7"
string "org.freedesktop.Notifications "
string "org.freedesktop.Telepathy.Client.EmpathyMoreThanMeetsTheEye"
... ...
string ":1.6"
string ":1.19"
]
例如其中有org.freedesktop.Notifications这样一个Name,我们希望进一步查看
[wei@wei-desktop ~]$ dbus-send --print-reply --type=method_call --dest=org.freedesktop.Notifications / org.freedesktop.DBus.Introspectable.Introspect
method return sender=:1.19 -> dest=:1.79 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<node name="org"/>
</node>
"
例如Node名字,表明路径,从/org开始
[wei@wei-desktop ~]$ dbus-send --print-reply --type=method_call --dest=org.freedesktop.Notifications /org org.freedesktop.DBus.Introspectable.Introspect
method return sender=:1.19 -> dest=:1.80 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<node name="freedesktop"/>
<node name="moblin"/>
</node>
"
Node名字,表明路径,又从/org/freedesktop开始
[wei@wei-desktop ~]$ dbus-send --print-reply --type=method_call --dest=org.freedesktop.Notifications /org/freedesktop org.freedesktop.DBus.Introspectable.Introspect
method return sender=:1.19 -> dest=:1.81 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<node name="Notifications"/>
</node>
"
Node名字,表明路径,又从/org/freedesktop/Notifications开始,可以获取这个接口有什么method和singnal。
[wei@wei-desktop ~]$ dbus-send --print-reply --type=method_call --dest=org.freedesktop.Notifications /org/freedesktop/Notifications org.freedesktop.DBus.Introspectable.Introspect
method return sender=:1.19 -> dest=:1.82 reply_serial=2
string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" direction="out" type="s"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg name="interface" direction="in" type="s"/>
<arg name="propname" direction="in" type="s"/>
<arg name="value" direction="out" type="v"/>
</method>
<method name="Set">
<arg name="interface" direction="in" type="s"/>
<arg name="propname" direction="in" type="s"/>
<arg name="value" direction="in" type="v"/>
</method>
<method name="GetAll">
<arg name="interface" direction="in" type="s"/>
<arg name="props" direction="out" type="a{sv}"/>
</method>
</interface>
<interface name="org.freedesktop.Notifications">
<method name="GetServerInformation">
<arg name="name" type="s" direction="out"/>
<arg name="vendor" type="s" direction="out"/>
<arg name="version" type="s" direction="out"/>
</method>
<method name="GetCapabilities">
<arg name="caps" type="as" direction="out"/>
</method>
<method name="CloseNotification">
<arg name="id" type="u" direction="in"/>
</method>
<method name="Notify">
<arg name="app_name" type="s" direction="in"/>
<arg name="id" type="u" direction="in"/>
<arg name="icon" type="s" direction="in"/>
<arg name="summary" type="s" direction="in"/>
<arg name="body" type="s" direction="in"/>
<arg name="actions" type="as" direction="in"/>
<arg name="hints" type="a{sv}" direction="in"/>
<arg name="timeout" type="i" direction="in"/>
<arg name="return_id" type="u" direction="out"/>
</method>
<signal name="NotificationClosed">
<arg type="u"/>
<arg type="u"/>
</signal>
</interface>
</node>
"
从Tutorial中学习基本概念后,进入实战阶段,通过学习,我们给出几个小例子,同步例子和异步例子。
GLib的DBus捆绑定义在<dbus/dbus-glib.h>头文件中。
D-Bus和GType的类型映射
DBus的GLib捆绑的关键在于绑定提供的DBus"type signatures"和GLib types(GType)的类型映射。
DBus的类型系统是由一系列的基本类型和容器类型组成的。
基础类型和GType的映射表格如下。在后面的程序小例子中我们会看到具体如何对应。
D-Bus basic type | GType | Free function | Notes |
BYTE | G_TYPE_BOOLEAN | | |
INT16 | G_TYPE_INT | | Will be changed to a G_TYPE_INT16 once GLib has it |
UINT16 | G_TYPE_UINT | | Will be changed to a G_TYPE_UINT16 once GLib has it |
INT32 | G_TYPE_INT | | Will be changed to a G_TYPE_INT32 once GLib has it |
UINT32 | G_TYPE_UINT | | Will be changed to a G_TYPE_UINT32 once GLib has it |
INT64 | | | G_TYPE_GINT64 |
UINT64 | G_TYPE_GUINT64 | | |
DOUBLE | G_TYPE_DOUBLE | | |
STRING | G_TYPE_STRING | g_free | |
OBJECT_PATH | DBUS_TYPE_G_PROXY | g_object_unref | The returned proxy does not have an interface set; use dbus_g_proxy_set_interface to invoke methods |
基本类型的映射是非常简单的。
容器类型的映射
在DBus类型系统中也有一些容器类型,比如DBUS_TYPE_ARRAY和DBus_TYPE_STRUCT,DBus类型
系统都是可以递归的,so one can for example have an array of array of strings
(i.e. type signature aas
).可是并不是所有这些类型都是共用的,for example,
at the time of this writing the author knows of no one using
DBUS_TYPE_STRUCT
, or a DBUS_TYPE_ARRAY
containing any non-basic type.
The approach the GLib bindings take is pragmatic;
try to map the most common types in the most obvious way,
and let using less common and more complex types be less "natural".
在D-Bus编程中,container类型和GType的映射表格如下:
D-Bus type signature | Description | GType | C typedef | Free function | Notes |
as | Array of strings | G_TYPE_STRV | char ** | g_strfreev | |
v | Generic value container | G_TYPE_VALUE | GValue * | g_value_unset | The calling conventions for values expect that method callers have allocated return values; see below. |
a{ss} | Dictionary mapping strings to strings | DBUS_TYPE_G_STRING_ | GHashTable * | g_hash_table_destroy | |
Makefile
在D-Bus中,使用了dbus-1 dbus-glib-1 glib-2.0(以Moblin 2.1为例)。Makefile的例子如下:
CC = gcc
CFLAGS += -Wall -g `pkg-config dbus-1 dbus-glib-1 glib-2.0 --cflags`
LIBS += -Wall -g `pkg-config dbus-1 dbus-glib-1 glib-2.0 --libs`
TARGET = sample
OBJ = $(TARGET).o
all: $(TARGET)
%o: %c
$(CC) $(CFLAGS) -c $< -o $@
$(TARGET): $(OBJ)
$(CC) $(LIBS) -o $(TARGET) $(OBJ)
同步的例子
同步即程序发出method call消息,等待method_return消息。下面是一个小例子,如果我们用dbus-send命令,可以使用:
dbus-send --session --print-reply --type=method_call --dest=org.freedesktop.Notifications / org.freedesktop.DBus.Introspectable.Introspect
在上一次学习中学习过。它返回的GType类型为G_TYPE_STRING,在C程序中对应char *。
#include < stdio.h > #include < stdlib.h > #include < dbus / dbus - glib.h > int main( int argc , char ** argv) { GError * error; DBusGConnection * connection; DBusGProxy * proxy; char * string ; /* GType初始化 */ g_type_init(); error = NULL; /* dbus_g_bus_get用来建立连接,这里和session bus连接,也可以通过DBUS_BUS_SYSTEM与系统总线连接 */ connection = dbus_g_bus_get (DBUS_BUS_SESSION, & error); if (connection == NULL){ g_printerr ( " Failed to open connection to bus : %s/n " ,error -> message); g_error_free( error); exit( 1 ); } /* Create a proxy object用来代表远端,org.freedesktop.Notifications是系统自带的,可以使用DBUS_INTERFACE_INTROSPECTABLE等定义来标识它 */ proxy = dbus_g_proxy_new_for_name (connection, " org.freedesktop.Notifications " /* service */ , " / " /* path */ , " org.freedesktop.DBus.Introspectable " /* interface,可以使用宏定义DBUS_INTERFACE_INTROSPECTABLE */ ); error = NULL; /* 采用同步方式,第二个参数是method,第三个参数是error,后面是不定长的参数,显示输入,后为输出,以G_TYPE_INVALID标识接收,每个参数由GType类型和存贮参数值的地址构成。在这个例子中,输入没有参数,输入有一个参数,是string */ if ( ! dbus_g_proxy_call (proxy, " Introspect " , & error, G_TYPE_INVALID,G_TYPE_STRING, & string , G_TYPE_INVALID) ){ if (error -> domain == DBUS_GERROR && error -> code == DBUS_GERROR_REMOTE_EXCEPTION) g_printerr( " Caught remote method exception %s:%s " ,dbus_g_error_get_name(error),error -> message); else g_printerr( " Error : %s/n " , error -> message); g_error_free(error); exit ( 1 ); } g_print( " Message Method return from bus:/n%s/n " , string ); g_free( string ); g_object_unref(proxy); return 0 ; }
异步的例子
异步中,程序将不等返回消息,继续执行,等有返回消息的时候,触发一个回调函数 。下面是同样的操作,但是用异步的方式来实现:
/* Test for invoke method asynchronously */ #include < stdio.h > #include < stdlib.h > #include < dbus / dbus - glib.h > static GMainLoop * main_loop; / * 下面是回调函数,收到method return消息触发函数 * / static void my_callback_func (DBusGProxy * proxy, DBusGProxyCall * call_id, void * user_data) { GError * error = NULL; gchar * string = NULL; /* 结束一个消息的收发,处理收到的消息,获取返回值或者error信息 */ dbus_g_proxy_end_call (proxy,call_id, & error,G_TYPE_STRING, & string ,G_TYPE_INVALID); if (error != NULL){ g_print( " Error in method call : %s/n " , error -> message); g_error_free(error); } else { g_print( " SUCCESS,it is now %s/n " , string ); } g_main_loop_quit(main_loop); } int main( int argc , char ** argv) { GError * error = NULL; DBusGConnection * connection; DBusGProxy * proxy; g_type_init(); main_loop = g_main_loop_new(NULL,TRUE); connection = dbus_g_bus_get (DBUS_BUS_SESSION, & error); if (connection == NULL){ g_printerr ( " Failed to open connection to bus : %s/n " , error -> message); g_error_free( error); exit( 1 ); } /* Create a proxy object for the 'bus driver' named org.freedesktop.DBus */ proxy = dbus_g_proxy_new_for_name (connection, " org.freedesktop.Notifications " , " / " ,DBUS_INTERFACE_INTROSPECTABLE); /* 异步触发,也可以带上一个超时的时间限制,使用dbus_g_proxy_call_with_timeout 。这里的参数只需带上输入的情况。第四个参数为携带到回调函数的user_data,第五个参数标识释放user_data的函数,例如g_free等 */ dbus_g_proxy_begin_call (proxy, " Introspect " ,my_callback_func, NULL,NULL, G_TYPE_INVALID);g_main_loop_run(main_loop); return 0 ; }
在上面的例子中,使用了GMainLoop的方式,对于线程D-Bus还提供了dbus_g_thread_init 来处理。