Maemo平台开发之D-Bus

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的地址和名字
  • MaemoD-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 中,这个地址是由下面的元素构成的分级方式的地址:
  • 这个消息要送给哪个通道?一个通道对于一个程序来讲,一般只打开一次,之后这个程序就使用这个打开的通道的一个链接(支流),进行消息的发送和接收。这种方式下,这个目的通道就要公开和透明了,就是说,大家都知道,不需要每个消息单独指定目的了。
  • 接收方还需提供一个“人所共知”的名字,这个名字就类似于Internet中的一个术语:DNS(域名解析),人们通常使用一个好记的网址去上网,然后通过这个DNS转换为特定的IP地址。D-Bus的设计者就把这种思想引入了,这个名字是“人所共知”的,同时也是“唯一”的。那么,这个名字如何起呢?
    • 这个名字有A-Z字符(大写或者小写)、”.”、“下划线”_组成。而且名字中还要至少两个点。不想DNS那样,这里的点其实呢没有什么特别的涵义,只是为了说明,这个名字是无级别和层次的。
    • 为了降低D-Bus名字空间中所起的名字的重复和冲突,我们推荐你这样组织D-Bus的名字名称反序,有点像Java包名字的形式,看下面的例子。
    • 例子org.maemo.Alertorg.freedesktop.Notifications.
  • 每个service可以包含有多个不同的对象,每个对象又提供不同的服务。为了区分这些对象,使用object path.
    • 对象路径很像文件路径,用反斜杠/分开
    • D-Bus中,按理说,调用一个远程方法,要指定其全路径,由于我们已经知道提供该方法的对象路径,所以,我们可以采用一种偷懒的方法:直接传入方法名,然后方法名再拼凑在object path后面,这样也可以形成一个完整的全路径。不推荐:(/org/maemo/AddressBook/Contacts/ShortName). 而推荐:ShortName(一个UTF-8编码格式的字串).
    • 假如说一个service的名字是:org.maemo.alert, 我们可以简单的把点换为反斜线: /org/maemo/alert,这就形成了一个对象路径。
    • 和名字类似,object path也没有层次关系。即使我们使用了路径分隔符,让人看起来好像有层次关系,其实没有。
  • 为了支持面向对象,在D-Bus中,对象是提供服务的单元。这些服务通过对象的接口(Interface)来实现。接口具有合法的(比如说,有定义,有实现)方法调用、参数以及信号等。具有这些特性,使得多个不同的对象为了可以实现类似的服务,可以复用一个接口。或者更普遍点:单个对象实现多个不同的服务。举个例子,接口:org.freedesktop.DBus.Introspectable定义了支持D-Bus自省功能的接口。对于什么是自省,后面详细叙述。如果你想使用D-BusGlib封装的接口去产生你自己的D-Bus代码,你所定义的对象会自动支持自省接口的。
    • 接口名字:和服务名字一样的规则。这样做,刚开始时你可能会误会,但时间长了,你就会习惯的。
    • 为了简化服务,一般用众所周知的名字作为接口名字。
  • 消息地址的最后一个部分是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 已经在后台运行了,你运行上面的命令后,出现的对话框如下所示:
  Maemo平台开发之D-Bus
 
如果你重复运行上面的命令,你可能会注意到 notification service  一次只能显示一个对话框。而且你会发现,这个方法调用会堆积,而不会丢失掉。 D-Bus 不会无缘无故的把一个消息丢失掉的。

你可能感兴趣的:(Maemo)