From: http://blog.csdn.net/cuijpus/article/details/2073962
在Maemo平台中,D-Bus是一个非常重要的中间件(middleware)解决方案,主要用于进程之间的通信。已经有很多服务使用了D-Bus,接下来将介绍不同的方法去实现。D-Bus是贯穿于Maemo平台的,离不开D-Bus。
这部分的内容:
- 介绍一下D-Bus
- D-Bus的架构
- D-Bus的地址和名字
- 在Maemo中D-Bus的作用
- 直接用D-Bus底层库libdbus进行编程
D-Bus
介绍
D-Bus(
其中
D
原先是代表桌面“
Desktop
”的意思
)
,即:用于桌面操作系统的通信总线。现在逐渐被引入到嵌入式系统中,不过名字还是保留原先的叫法而已。
D-Bus
是相对来讲比较新的进程间通信(
IPC
)机制,在桌面操作系统中,扮演着一个统一的中间层的一个角色。有很多的项目都用了
D-Bus,
比如:
GNOME, Hildon, etc.
相对于其它的
IPC, D-Bus
丢掉了一些不必要的、复杂的东西,也正是因为这个原因,
D-Bus
比较快、简单。
D-Bus
不和低层的
IPC
直接竞争,比如
sockets, shared memory or message queues.
这些低层点的
IPC
有它们自己的特点,和
D-Bus
并不冲突。实际上,
D-Bus
的主要目的是提供如下的一些更高层的功能:
- 结构化的名字空间
- 独立于架构的数据格式
- 支持消息中的大部分通用数据元素
- 带有异常处理的通用远程调用接口
- 支持广播类型的通信
- 在系统和用户之间有明确的区分,这对于处理多用户系统非常重要
- 不局限于任何特定的编程语言,同时提供了语言绑定方法,来和一些通用的高级语言绑定,比如C, C++, Python等
D-Bus
的设计得益于在桌面系统中其它
IPC
的长期经验总结,正是有了这些丰富的经验,才使得
D-Bus
的设计得以优化。同时
D-Bus
也不会受累于缓慢的功能改进(
creeping featurism
)。
上面所说的一切,所反映的核心问题:
D-Bus
目的是为了桌面软件提供很容易使用的
IPC,
当然现在也为嵌入式系统服务了。
在
Maemo
中,
D-Bus
起的作用是非常非常大的。因为当使用
Maemo
平台提供的服务时就要用到
D-Bus.
同时,在
D-Bus
的基础之上提供一些服务也是最容易的一种方式,也可以达到模块复用的目的。总之,好处多多。
D-Bus
的架构以及一些术语:
在
D-Bus
中,“
bus”
是核心的概念,它是一个通道:不同的程序可以通过这个通道做些操作,比如方法调用、发送信号和监听特定的信号。有两种不同的通道:
session bus(
会话通道
)
,
system bus(
系统通道
)
:
- 会话通道处理连接到同一桌面任务的不同程序之间的通信,即被同一个用户启动和运行
- 系统通道是为了处于不同会话环境中的不同程序之间提供通信。这种通道的最常用的方面就是发送系统消息,比如:插入一个新的存储设备;有新的网络连接;等等.
通常情况下,只会存在一个系统通道,但是可以有不同的会话通道(每人一个桌面会话)。在
Internet Tablet
产品中,所有的应用程序都是使用一个用户
ID
运行的,所以只有一个会话通道,这一点是和
Linux
桌面系统是有明显区别的,请留意。
在系统中通道的存在形式是什么呢?是以
bus daemon
的形式出现的,
bus daemon
是一个特殊的进程:这个进程可以从一个进程传递消息给另外一个进程。当然了,如果有很多
applications
链接到这个通道上,这个
daemon
进程就会把消息转发给这些链接的所有程序。在最底层,
D-Bus
只支持点对点的通信,一般使用本地套接字(
AF_UNIX
)在应用和
bus daemon
之间通信。
D-Bus
的点对点是经过
bus daemon
抽象过的,由
bus daemon
来完成寻址和发送消息,因此每个应用不必要关心要把消息发给哪个进程。(注:这个寻址定位是通过
service
名字实现的,后面还会讲到)
上面提到的通过
D-Bus
发送消息通常包含如下步骤(正常情况下):
- 创建和发送消息给后台bus daemon进程,这个过程中会有两个上下文的切换
- 后台bus daemon进程会处理该消息,并转发给目标进程,这也会引起上下文的切换
- 目标程序接收到消息,然后根据消息的种类,做不同的响应:要么给个确认、要么应答、还有就是忽略它。最后一种情况对于“通知”类型的消息而言,前两种都会引起进一步的上下文切换。
综上原因,如果你准备在不同的进程之间传递大量的数据,
D-Bus
可能不是最有效的方法,最有效的方法是使用共享内存,但是对共享内存的管理也是相当复杂的。
D-Bus
的地址和名字
为了把消息正确的送给接收者,
IPC
机制需要具有某种或某些寻址能力。
D-Bus
所设计的寻址方案是非常灵活和高效的。每个通道
(bus)
都有它私人的名字空间,和别的通道区分开来。
为了发送一个消息,你需要知道消息的目的地址,在
D-Bus
中,这个地址是由下面的元素构成的分级方式的地址:
- 1 这个消息要送给哪个通道?一个通道对于一个程序来讲,一般只打开一次,之后这个程序就使用这个打开的通道的一个链接(支流),进行消息的发送和接收。这种方式下,这个目的通道就要公开和透明了,就是说,大家都知道,不需要每个消息单独指定目的了。
- 2 接收方还需提供一个“人所共知”的名字,这个名字就类似于Internet中的一个术语:DNS(域名解析),人们通常使用一个好记的网址去上网,然后通过这个DNS转换为特定的IP地址。D-Bus的设计者就把这种思想引入了,这个名字是“人所共知”的,同时也是“唯一”的。那么,这个名字如何起呢?
- 这个名字有A-Z字符(大写或者小写)、”.”、“下划线”_组成。而且名字中还要至少两个点。不想DNS那样,这里的点其实呢没有什么特别的涵义,只是为了说明,这个名字是无级别和层次的。
- 为了降低D-Bus名字空间中所起的名字的重复和冲突,我们推荐你这样组织D-Bus的名字: 名称反序,有点像Java包名字的形式,看下面的例子。
- 例子: org.maemo.Alert和org.freedesktop.Notifications.
- 3 每个service可以包含有多个不同的对象,每个对象又提供不同的服务。为了区分这些对象,使用object path.
- 对象路径很像文件路径,用反斜杠/分开
- 在D-Bus中,按理说,调用一个远程方法,要指定其全路径,由于我们已经知道提供该方法的对象路径,所以,我们可以采用一种偷懒的方法:直接传入方法名,然后方法名再拼凑在object path后面,这样也可以形成一个完整的全路径。不推荐:(/org/maemo/AddressBook/Contacts/ShortName). 而推荐:ShortName(一个UTF-8编码格式的字串).
- 假如说一个service的名字是:org.maemo.alert, 我们可以简单的把点换为反斜线: /org/maemo/alert,这就形成了一个对象路径。
- 和名字类似,object path也没有层次关系。即使我们使用了路径分隔符,让人看起来好像有层次关系,其实没有。
- 4 为了支持面向对象,在D-Bus中,对象是提供服务的单元。这些服务通过对象的接口(Interface)来实现。接口具有合法的(比如说,有定义,有实现)方法调用、参数以及信号等。具有这些特性,使得多个不同的对象为了可以实现类似的服务,可以复用一个接口。或者更普遍点:单个对象实现多个不同的服务。举个例子,接口:org.freedesktop.DBus.Introspectable定义了支持D-Bus自省功能的接口。对于什么是自省,后面详细叙述。如果你想使用D-Bus的Glib封装的接口去产生你自己的D-Bus代码,你所定义的对象会自动支持自省接口的。
- 接口名字:和服务名字一样的规则。这样做,刚开始时你可能会误会,但时间长了,你就会习惯的。
- 为了简化服务,一般用众所周知的名字作为接口名字。
- 5 消息地址的最后一个部分是member。当处理远程调用时,这个member也叫做method name; 当处理信号时,member就是signal name.
- Member names 有字符、数字、下划线组成,例如: RetrieveQuote.
- 如果想对D-Bus有个更加深入的了解,到其官方网址:Introduction to D-Bus page.
上面就是
D-Bus
地址中最重要部分的讲解,也是你今后经常面对的。下面举个例子:
#define SYSNOTE_NAME
"org.freedesktop.Notifications" //服务名字
#define SYSNOTE_OPATH
"/org/freedesktop/Notifications" //对象路径名字
#define SYSNOTE_IFACE
"org.freedesktop.Notifications" //对象接口的名字
#define SYSNOTE_NOTE
"SystemNoteDialog" //接口方法的名字
如果今后你使用
LibOSSO
(封装了很多
D-Bus
机制)远程调用函数,就会有很多机会去处理
D-Bus
的各类名字,别着急,呵呵。
在
Maemo
中
D-Bus
中作用:
在
Maemo
中,
D-Bus
被选做为事实上的
IPC
机制,主要是为了在不同的软件程序之间传递消息。
选择
D-Bus
而不是其他的
IPC
的主要原因在于:在
GNOME
环境下,大量的软件使用了
D-Bus.
使用通用的东西,总是好的。
不过,也有点遗憾:
SDK
本身并没有使用多少带有
D-Bus
的软件项目,不过,我们准备使用
application framework
中的一个组件来演示
D-Bus
的用法。
我们对于使用
notification framework
组件去显示一个通知对话框比较感兴趣,因为这是比较简单的一个例子,而且可以说清楚
D-Bus
的基本用法。这个对话框是模式对话框,学过
VC++
的网友应该都清楚什么是模式
/
非模式对话框,这里不多说了。
有个后台程序:
notification server
一直运行着,它监听
org.freedesktop.Notifications
.
其对应的对象路径是:
/org/freedesktop/Notifications
;其接口是:
org.freedesktop.Notifications
;
该接口提供了函数:
SystemNoteDialog
,由这个函数显示一个模式对话框出来。
D-Bus
提供了工具来体验方法调用和信号调用,这个工具是:
dbus-send.
下面,我们试着用几条语句来显示一个对话框:
[sbox-CHINOOK_X86: ~] > run-standalone.sh dbus-send --print-reply /
--type=method_call --dest=org.freedesktop.Notifications /
/org/freedesktop/Notifications org.freedesktop.Notifications
Error org.freedesktop.DBus.Error.UnknownMethod: Method "Notifications" with
signature "" on interface "org.freedesktop" doesn't exist
[
运行
dbus-send
命令时,这里没有提供正确的
method name (dbus-send
会认为
Notifications
就是
Method
名字,而实际上并没有这个名字,所以会报错
)
dbus-send
的参数
:
- --session: (默认值) 准备把消息发送给哪种通道:会话通道还是系统通道 (另外一个选择:--system)
- --print-reply: 告诉dbus-send要等待回应,并且打印出结果
- --type=method_call: 指明是方法调用,而不是发送一个信号(默认值)
- --dest=org.freedesktop.Notifications: 服务的名字,就认为这个服务会提供一些函数供调用
- /org/freedesktop/Notifications: 目标进程的对象路径
- org.freedesktop.Notifications: (这里故意写错的,实际上没有这个方法),在接口中定义的方法
在是使用
dbus-send
时,要特别留心一个地方:接口和方法名字要接在一起,中间不能有空格!
对上面的例子做些修改,再试试:
[sbox-CHINOOK_X86: ~] > run-standalone.sh dbus-send --print-reply /
--type=method_call --dest=org.freedesktop.Notifications /
/org/freedesktop/Notifications org.freedesktop.Notifications.SystemNoteDialog
Error org.freedesktop.DBus.Error.UnknownMethod: Method "SystemNoteDialog" with
signature "" on interface "org.freedesktop.Notifications" doesn't exist
[
接口和方法是连到一块了,但是为什么还是出错呢?
]
原因是你没有给方法:
SystemNoteDialog
传递参数,就向平常我们做函数调用一样,原型中有参数,但是你调用时没有传参,肯定不对。
函数
SystemNoteDialog
有三个参数:
- string: 要显示的信息
- uint32: 表示不同的对话框风格,0~4表示不同的图标,5是带有进度条的对话框。
- string: 按下OK按钮给出的提示.
参数这样给:类型
:
值
[sbox-CHINOOK_X86: ~] > run-standalone.sh dbus-send --print-reply /
--type=method_call --dest=org.freedesktop.Notifications /
/org/freedesktop/Notifications org.freedesktop.Notifications.SystemNoteDialog /
string:'Hello, world!' uint32:0 string:'NAO OK!'
method return sender=:1.1 -> dest=:1.15
uint32 4
[
指定了正确的函数以及参数
]
由于我们要求
dbus-send
打印出返回值,所以我们看到了其返回值:
4
,
这个返回值可以用于函数
CloseNotifacation
,这个函数是强制提前关闭前面弹出的对话框的。返回值是非常有用的。
假设
Maemo
的
application framwork
已经在后台运行了,你运行上面的命令后,出现的对话框如下所示:
如果你重复运行上面的命令,你可能会注意到
notification service
一次只能显示一个对话框。而且你会发现,这个方法调用会堆积,而不会丢失掉。
D-Bus
不会无缘无故的把一个消息丢失掉的。