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不会无缘无故的把一个消息丢失掉的。