DBus学习笔记

摘要:DBus做为一个轻量级的IPC被愈来愈多的平台接受,在MeeGo中DBus也是主要的进程间通讯方式,这个笔记将从基本概念开始记录笔者学习DBus的过程

[1] DBus学习笔记一:DBus学习的一些参考资料
[2] DBus学习笔记二:什么是DBus? 
[3] DBus学习笔记三:DBus的一些基本概念 html

 

一些基本概念的解释和翻译:
http://blog.mcuol.com/User/AT91RM9200/Article/12816_1.htm
http://www.cnblogs.com/wzh206/archive/2010/05/13/1734901.html
一个完整的DBus学习教程(强烈推荐,写得至关的全):
http://blog.csdn.net/fmddlmyy/archive/2008/12/23/3585730.aspx
两个DBus的完整示例,至关有参考价值
http://blog.chinaunix.net/u1/58649/showart_462468.html
http://hi.baidu.com/zengzhaonong/blog/item/670b98d6e63ae42c07088bae.html
DBus官方网站,最原滋原味的DBus学习内容
http://www.freedesktop.org/wiki/Software/dbus
http://dbus.freedesktop.org/doc/dbus-tutorial.html
http://dbus.freedesktop.org/doc/dbus-specification.html
欢迎你们提供更多的DBus学习资料,一块儿研究java

 

 

DBus是一种IPC机制,由freedesktop.org项目提供,使用GPL许可证发行,用于进程间通讯或进程与内核的通讯。
:Linux中的IPC通讯机制还包括,管道(fifo),共享内存,信号量,消息队列,Socket等。
DBus进程间通讯主要有三层架构
1.底层接口层:主要是经过libdbus这个函数库,给予系统使用DBus的能力。 
2.总线层:主 要Message bus daemon这个总线守护进程提供的,在Linux系统启动时运行,负责进程间的消息路由和传递,其中包括Linux内核和Linux桌面环境的消息传 递。总线守护进程可同时与多个应用程序相连,并能把来自一个应用程序的消息路由到0或者多个其余程序。 
3.应用封装层:经过一系列基于特定应用程序框架将DBus的底层接口封装成友好的Wrapper库,供不一样开发人员使用(DBus官方主页http://www.freedesktop.org/wiki/Software/dbus,提供了大部分编程语言的DBus库版本)。好比libdbus-glib, libdbus-python.node

 

如上图所示,Bus Daemon Process是运行在linux系统中的一个后台守护进程,dbus-daemon运行时会调用libdus的库。Application Process1表明的就是应用程序进程,经过调用特定的应用程序框架的Wrapper库与dbus-daemon进行通讯。
从上图也能够看出来Application和Daemon中其实仍是经过socket进行通行。python

DBus的三大优势:低延迟,低开销,高可用性。linux

*低延迟:DBus一开始就是用来设计成避免来回传递和容许异步操做的。所以虽然在Application和Daemon之间是经过socket实现的,可是又去掉了socket的循环等待,保证了操做的实时高效。ios

*低开销:DBus使用一个二进制的协议,不须要转化成像XML这样的文本格式。由于DBus是主要用来机器内部的IPC,而不是为了网络上的IPC机制而准备的.因此它才可以在本机内部达到最优效果。程序员

*高可用性:DBus是基于消息机制而不是字节流机制。它能自动管理一大堆困难的IPC问题。一样的,DBus库被设计来让程序员可以使用他们已经写好的代码。而不会让他们放弃已经写好的代码,被迫经过学习新的IPC机制来根据新的IPC特性重写这些代码。shell

 

 

在介绍基本概念以前,先介绍一个学习DBus的好工具d-feet,这个工具主要是用来查看DBus的System Bus和Session Bus中的各个消息链接的。固然,你也能够在这里面添加本身建立的消息总线,以便于观察。apache

 

下面根据上图介绍一下DBus中的一些基本概念。编程

会话总线(Session Buses普通进程建立,可同时存在多条。会话总线属于某个进程私有,它用于进程间传递消息。

系统总线(System Bus在引导时就会启动,它由操做系统和后台进程使用,安全性很是好,以使得任意的应用程序不能欺骗系统事件。固然,若是一个应用程序须要接受来自系统总线的消息,他也能够直接链接到系统总线中,可是他能发送的消息是受限的。

Bus Name按字面理解为总线名称貌似不是很贴切,应该是一个链接名称,主要是用来标识一个应用和消息总线的链接。从上图能够看出来,总线名称主要分为两类

"org.kde.StatusNotifierWatcher"这种形式的称为公共名(well-knownname)

":1.3"这种形式的称为惟一名(Unique Name)

公共名提供众所周知的服务。其余应用经过这个名称来使用名称对应的服务。可能有多个链接要求提供同个公共名的服 务,即多个应用链接到消息总线,要求提供同个公共名的服务。消息总线会把这些链接排在链表中,并选择一个链接提供公共名表明的服务。能够说这个提供服务的 链接拥有了这个公共名。若是这个链接退出了,消息总线会从链表中选择下一个链接提供服务。

惟一名以“:”开头,“:”后面一般是圆点分隔的两个数字,例如“:1.0”。每一个链接都有一个惟一名。在一个 消息总线的生命期内,不会有两个链接有相同的惟一名。拥有公众名的链接一样有惟一名,例如在前面的图 中,“org.kde.StatusNotifierWatcher”的惟一名是“:1.51”。

每一个链接都有一个惟一名,但不必定有公共名。

只有惟一名而没有公共名叫作私有链接,由于它们没有提供能够经过公共名访问的服务。

Object Paths

“org.kde.StatusNotifierWatcher”这个链接中有三个Object Paths,标识这这个链接中提供了三个不一样的服务,每一个Object Paths表示一个服务。这个路径在链接中是惟一的。

Interfaces

在每一个Object Paths下都包含有多个接口(Interfaces),举例以下接口:

org.freedesktop.DBus.Introspectable

org.freedesktop.DBus.Properties

org.kde.StatusNotifierWatcher

红色的两个是消息总线提供的标准接口,而剩下的一个是须要具体的应用去实现的。

MethodsSignals

Methods表示能够被具体调用的方法

Signals则表示的是信号,此信号能够被广播,而链接了这个信号的对象在接收到信号时就能够进行相应的处理。和Qt中的信号应该是一个意思。

 

 

D-BUS 基础

http://blog.mcuol.com/User/AT91RM9200/Article/12816_1.htm

 

 

D-Bus Tutorial进行了一些翻译加上本身的一些理解。

有不少种IPC或者网络通讯系统,如:CORBA, DCE, DCOM, DCOP, XML-RPC, SOAP, MBUS, Internet Communications Engine (ICE)等等,可能会有数百种,dbus的目的主要是下面两点:
1.在同一个桌面会话中,进行桌面应用程序之间的通信
2.桌面程序与内核或者守护进程的通讯。

Dbus是一套进程通讯体系,它有如下几层:
1.libdbus库,提供给各个应用程序调用,使应用程序具备通讯和数据交换的能力,两个应用程序能够直接进行通讯,就像是一条socket通道,两个程序之间创建通道以后,就能够通信了。
2.消息守护进程,在libdbus的基础上建立,能够管理多个应用程序之间的通讯。每一个应用程序都和消息守护进程创建dbus的连接,而后由消息守护进程进行消息的分派。
3.各类包装库,有libdbus-glib,libdbus-qt等等,目的是将dbus的底层api进行一下封装。

下面有一张图能够很方便说明dbus的体系结构。
dbus

dbus中的消息由一个消息头(标识是哪种消息)和消息数据组成,比socket的流式数据更方便一些。bus daemon 就像是一个路由器,与各个应用程序进行链接,分派这些消息。bus daemon 在一台机器上有多个实例,第一个实例是全局的实例,相似于sendmail和或者apache,这个实例有很严格的安全限制,只接受一些特定的系统消息, 用于系统通讯。其余bus daemon是一些会话,用于用户登陆以后,在当前会话(session)中进行的通信。系统的bus daemon 和会话的bus daemon 是分开的,彼此不会互相影响,会话bus daemon 不会去调用系统的bus daemon 。

Native Objects and Object Paths
在不一样的编程语言中,都定义了一些“对象”,如java中的 java.lang.Object,GLIB中的GObject,QT中的QObject等 等。D-BUS的底层接口,和libdbus API相关,是没有这些对象的概念的,它提供的是一种叫对象路径(object path),用于让高层接口绑定到各个对象中去,容许远端应用程序指向它们。object path就像是一个文件路径,能够叫作/org/kde/kspread/sheets/3/cells/4/5等。

Methods and Signals
每一个对象都有一些成员,两种成员:方法(methods)和信号(signals),在对象中,方 法能够被调用。信号会被广播,感兴趣的对象能够处理这个 信号,同时信号中也能够带有相关的数据。每个方法或者信号均可以用一个名字来命名,如”Frobate” 或者 “OnClicked”。

Interfaces
每一个对象都有一个或者多个接口,一个接口就是多个方法和信号的集合。dbus使用简单的命名空间字符串来表示接口,如org.freedesktop.Introspectable。能够说dbus接口至关于C++中的纯虚类。

Proxies
代理对象用于模拟在另外的进程中的远端对象,代理对象像是一个正常的普通对象。d-bus的底层接口必须手动建立方法调用的 消息,而后发送,同时必须手动 接受和处理返回的消息。高层接口可使用代理来替换这些,当调用代理对象的方法时,代理内部会转换成dbus的方法调用,等待消息返回,对返回结果解包, 返回给相应的方法。能够看看下面的例子,使用dbus底层接口编写的代码:
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 = new Proxy(getBusConnection(), "/remote/object/path");
Object returnValue = proxy.MethodName(arg1, arg2);

客户端代码减小不少。

 

 

Bus Names
当一个应用程序链接上bus daemon时,daemon会分配一个惟一的名字给它。以冒号(:)开始,这些名字在daemon的生命周期中是不会改变的,能够认为这些名字就是一个 IP地址。当这个名字映射到应用程序的链接上时,应用程序能够说拥有这个名字。同时应用能够声明额外的容易理解的名字,好比能够取一个名字 com.mycompany.TextEditor,能够认为这些名字就是一个域名。其余应用程序能够往这个名字发送消息,执行各类方法。

名字还有第二个重要的用途,能够用于跟踪应用程序的生命周期。当应用退出(或者崩溃)时,与bus的链接将被OS内核关掉,bus将会发送通知,告诉剩余的应用程序,该程序已经丢失了它的名字。名字还能够检测应用是否已经启动,这每每用于只能启动一个实例的应用。

Addresses
使用d-bus的应用程序既能够是server也能够是client,server监听到来的链接,client链接到 server,一旦链接创建,消息 就能够流转。若是使用dbus daemon,全部的应用程序都是client,daemon监听全部的链接,应用程序初始化链接到daemon。

dbus地址指明server将要监听的地方,client将要链接的地方,例如,地址:unix:path=/tmp/abcdef代表 server将在/tmp/abcdef路径下监听unix域的socket,client也将链接到这个socket。一个地址也能够指明是 TCP/IP的socket,或者是其余的。

当使用bus daemon时,libdbus会从环境变量中(DBUS_SESSION_BUS_ADDRESS)自动认识“会话daemon”的地址。若是是系统 daemon,它会检查指定的socket路径得到地址,也可使用环境变量(DBUS_SESSION_BUS_ADDRESS)进行设定。

当dbus中不使用daemon时,须要定义哪个应用是server,哪个应用是client,同时要指明server的地址,这不是很一般的作法。

Big Conceptual Picture
要在指定的对象中调用指定的方法,须要知道的参数以下:
Address -> [Bus Name] -> Path -> Interface -> Method
bus name是可选的,除非是但愿把消息送到特定的应用中才须要。interface也是可选的,有一些历史缘由,DCOP不须要指定接口,由于DCOP在同一个对象中禁止同名的方法。

Messages - Behind the Scenes
若是使用dbus的高层接口,就能够不用直接操做这些消息。DBUS有四种类型的消息:
1.方法调用(method call) 在对象上执行一个方法
2.方法返回(method return)返回方法执行的结果
3.错误(error)调用方法产生的异常
4.信号(signal)通知指定的信号发生了,能够想象成“事件”。

要执行 D-BUS 对象的方法,须要向对象发送一个方法调用消息。它将完成一些处理并返回一个方法返回消息或者错误消息。信号的不一样之处在于它们不返回任何内容:既没有“信号返回”消息,也没有任何类型的错误消息。

每一个消息都有一个消息头,包含多个字段,有一个消息体,包含多个参数。能够认为消息头是消息的路由信息,消息体做为一个载体。消息头里面的字段包含 发送的bus name,目标bus name,方法或者信号名字等,同时消息头里面定义的字段类型规定了消息体里面的数据格式。例如:字符“i”表明了”32-bit integer”,“ii”就表明了消息体里面有两个”32-bit integer”。

Calling a Method - Behind the Scenes
在dbus中调用一个方法包含了两条消息,进程A向进程B发送 方法调用消息,进程B向进程A发送应答消息。全部的消息都由daemon进行分派,每一个调用 的消息都有一个不一样的序列号,返回消息包含这个序列号,以方便调用者匹配调用消息与应答消息。调用消息包含一些参数,应答消息可能包含错误标识,或者包含 方法的返回数据。

方法调用的通常流程:
1.使用不一样语言绑定的dbus高层接口,都提供了一些代理对象,调用其余进程里面的远端对象就像是在本地进程中的调用同样。应用调用代理上的方法,代理将构造一个方法调用消息给远端的进程。
2.在DBUS的底层接口中,应用须要本身构造方法调用消息(method call message),而不能使用代理。
3.方法调用消息里面的内容有:目的进程的bus name,方法的名字,方法的参数,目的进程的对象路径,以及可选的接口名称。
4.方法调用消息是发送到bus daemon中的。
5.bus daemon查找目标的bus name,若是找到,就把这个方法发送到该进程中,不然,daemon会产生错误消息,做为应答消息给发送进程。
6. 目标进程解开消息,在dbus底层接口中,会当即调用方法,而后发送方法的应答消息给daemon。在dbus高层接口中,会先检测对象路径,接口, 方法名称,而后把它转换成对应的对象(如GObject,QT中的QObject等)的方法,而后再将应答结果转换成应答消息发给daemon。
7.bus daemon接受到应答消息,将把应答消息直接发给发出调用消息的进程。
8.应答消息中能够包容不少返回值,也能够标识一个错误发生,当使用绑定时,应答消息将转换为代理对象的返回值,或者进入异常。

bus daemon不对消息从新排序,若是发送了两条消息到同一个进程,他们将按照发送顺序接受到。接受进程并须要按照顺序发出应答消息,例如在多线程中处理这些消息,应答消息的发出是没有顺序的。消息都有一个序列号能够与应答消息进行配对。

Emitting a Signal - Behind the Scenes
在dbus中一个信号包含一条信号消息,一个进程发给多个进程。也就是说,信号是单向的广播。信号能够包含一些参数,可是做为广播,它是没有返回值的。

信号触发者是不了解信号接受者的,接受者向daemon注册感兴趣的信号,注册规则是”match rules”,记录触发者名字和信号名字。daemon只向注册了这个信号的进程发送信号。

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

 

 

DBUS基础知识

http://www.cnblogs.com/wzh206/archive/2010/05/13/1734901.html

 

 

1.  进程间使用D-Bus通讯

D-Bus是一种高级的进程间通讯机制,它由freedesktop.org项目提供,使用GPL许可证发行。D-Bus最主要的用途是在 Linux桌面环境为进程提供通讯,同时能将Linux桌面环境和Linux内核事件做为消息传递到进程。D-Bus的主要几率为总线,注册后的进程可通 过总线接收或传递消息,进程也可注册后等待内核事件响应,例如等待网络状态的转变或者计算机发出关机指令。目前,D-Bus已被大多数Linux发行版所 采用,开发者可以使用D-Bus实现各类复杂的进程间通讯任务。

2.  D-Bus的基本概念

D-Bus是一个消息总线系统,其功能已涵盖进程间通讯的全部需求,并具有一些特殊的用途。D-Bus是三层架构的进程间通讯系统,其中包括:

接口层:接口层由函数库libdbus提供,进程可经过该库使用D-Bus的能力。

总线层:总线层其实是由D-Bus总线守护进程提供的。它在Linux系统启动时运行,负责进程间的消息路由和传递,其中包括Linux内核和Linux桌面环境的消息传递。

包装层:包装层一系列基于特定应用程序框架的Wrapper库。

D-Bus具有自身的协议,协议基于二进制数据设计,与数据结构和编码方式无关。该协议无需对数据进行序列化,保证了信息传递的高效性。不管是libdbus,仍是D-Bus总线守护进程,均不须要太大的系统开销。

总线是D-Bus的进程间通讯机制,一个系统中一般存在多条总线,这些总线由D-Bus总线守护进程管理。最重要的总线为系统总线(System Bus),Linux内核引导时,该总线就已被装入内存。只有Linux内核、Linux桌面环境和权限较高的程序才能向该总线写入消息,以此保障系统安 全性,防止有恶意进程假冒Linux发送消息。

会话总线(Session Buses)由普通进程建立,可同时存在多条。会话总线属于某个进程私有,它用于进程间传递消息。

进程必须注册后才能收到总线中的消息,而且可同时链接到多条总线中。D-Bus提供了匹配器(Matchers)使进程能够有选择性的接收消息,另 外运行进程注册回调函数,在收到指定消息时进行处理。匹配器的功能等同与路由,用于避免处理无关消息形成进程的性能降低。除此之外,D-Bus机制的重要 概念有如下几个。

对象:对象是封装后的匹配器与回调函数,它以对等(peer-to-peer)协议使每一个消息都有一个源地址和一个目的地址。这些地址又称为对象路 径,或者称之为总线名称。对象的接口是回调函数,它以相似C++的虚拟函数实现。当一个进程注册到某个总线时,都要建立相应的消息对象。

消息:D-Bus的消息分为信号(signals)、方法调用(method calls)、方法返回(method returns)和错误(errors)。信号是最基本的消息,注册的进程可简单地发送信号到总线上,其余进程经过总线读取消息。方法调用是经过总线传递 参数,执行另外一个进程接口函数的机制,用于某个进程控制另外一个进程。方法返回是注册的进程在收到相关信息后,自动作出反应的机制,由回调函数实现。错误是 信号的一种,是注册进程错误处理机制之一。

服务:服务(Services)是进程注册的抽象。进程注册某个地址后,便可得到对应总线的服务。D-Bus提供了服务查询接口,进程可经过该接口查询某个服务是否存在。或者在服务结束时自动收到来自系统的消息。

创建服务的流程:
----------------------------------
    创建一个dbus链接以后 -- dbus_bus_get(),为这个dbus链接(DbusConnection)起名 -- dbus_bus_request_name(),这个名字将会成为咱们在后续进行远程调用的时候的服务名,而后咱们进入监听循环 -- dbus_connection_read_write()。在循环中,咱们从总线上取出消息 -- dbus_connection_pop_message(),并经过比对消息中的方法接口名和方法名 -- dbus_message_is_method_call(),若是一致,那么咱们跳转到相应的处理中去。在相应的处理中,咱们会从消息中取出远程调用的 参数。而且创建起回传结果的通路 -- reply_to_method_call()。回传动做自己等同于一次不须要等待结果的远程调用。
发送信号的流程:
----------------------------------
    创建一个dbus链接以后,为这个dbus链接起名,创建一个发送信号的通道,注意,在创建通道的函数中,须要咱们填写该信号的接口名和信号名 -- dbus_message_new_signal()。而后咱们把信号对应的相关参数压进去 -- dbus_message_iter_init_append(); dbus_message_iter_append_basic()。而后就能够启动发送了 -- dbus_connection_send(); dbus_connection_flush。
进行一次远程调用的流程:
----------------------------------
    创建好dbus链接以后,为这dbus链接命名,申请一个远程调用通道 -- dbus_message_new_method_call(),注意,在申请远程调用通道的时候,须要填写服务器名,本次调用的接口名,和本次调用名 (方法名)。压入本次调用的参数 -- dbus_message_iter_init_append(); dbus_message_iter_append_basic(),其实是申请了一个首地址,咱们就是把咱们真正要传的参数,往这个首地址里面送(送 完以后通常都会判断是否内存越界了)。而后就是启动发送调用并释放发送相关的消息结构 -- dbus_connection_send_with_reply()。这个启动函数中带有一个句柄。咱们立刻会阻塞等待这个句柄给咱们带回总线上回传的 消息。当这个句柄回传消息以后,咱们从消息结构中分离出参数。用dbus提供的函数提取参数的类型和参数 -- dbus_message_iter_init(); dbus_message_iter_next(); dbus_message_iter_get_arg_type(); dbus_message_iter_get_basic()。也就达成了咱们进行本次远程调用的目的了。
信号接收流程:
----------------------------------
    创建一个dbus链接以后,为这个dbus链接起名,为咱们将要进行的消息循环添加匹配条件(就是经过信号名和信号接口名来进行匹配控制的) -- dbus_bus_add_match()。咱们进入等待循环后,只须要对信号名,信号接口名进行判断就能够分别处理各类信号了。在各个处理分支上。咱们 能够分离出消息中的参数。对参数类型进行判断和其余的处理。
dbus_connection_read_write()
--------------------------------------
    As long as the connection is open, this function will block until it can read or write, then read or write, then return #TRUE.
    If the connection is closed, the function returns #FALSE.
dbus_connection_pop_message()
--------------------------------------
    Returns the first-received message from the incoming message queue, removing it from the queue. The caller owns a reference to the returned message. If the queue is empty, returns #NULL.
dbus_connection_send()
--------------------------------------
    Adds a message to the outgoing message queue. Does not block to write the message to the network; that happens asynchronously. To force the message to be written, call dbus_connection_flush(). Because this only queues the message, the only reason it can
fail is lack of memory. Even if the connection is disconnected, no error will be returned.
    @param connection the connection.
    @param message the message to write. 
    @param serial return location for message serial, or #NULL if you don't care
    @returns #TRUE on success.
dbus_connection_send_with_reply()
--------------------------------------
    Queues a message to send, as with dbus_connection_send(), but also returns a #DBusPendingCall used to receive a reply to the message. If no reply is received in the given timeout_milliseconds, this function expires the pending reply and generates a synthetic error reply (generated in-process, not by the remote application) indicating that a timeout occurred.
    A #DBusPendingCall will see a reply message before any filters or registered object path handlers. See dbus_connection_dispatch() for details on when handlers are run.
    A #DBusPendingCall will always see exactly one reply message, unless it's cancelled with dbus_pending_call_cancel().
    If #NULL is passed for the pending_return, the #DBusPendingCall will still be generated internally, and used to track the message reply timeout. This means a timeout error will occur if no reply arrives, unlike with dbus_connection_send().
    If -1 is passed for the timeout, a sane default timeout is used. -1 is typically the best value for the timeout for this reason, unless you want a very short or very long timeout. There is no way to avoid a timeout entirely, other than passing INT_MAX for the
timeout to mean "very long timeout." libdbus clamps an INT_MAX timeout down to a few hours timeout though.
    @warning if the connection is disconnected, the #DBusPendingCall will be set to #NULL, so be careful with this.
    @param connection the connection
    @param message the message to send
    @param pending_return return location for a #DBusPendingCall object, or #NULL if connection is disconnected
    @param timeout_milliseconds timeout in milliseconds or -1 for default
    @returns #FALSE if no memory, #TRUE otherwise.
dbus_message_is_signal()
--------------------------------------
    Checks whether the message is a signal with the given interface and member fields. If the message is not #DBUS_MESSAGE_TYPE_SIGNAL, or has a different interface or member field, returns #FALSE.
dbus_message_iter_init()
--------------------------------------
    Initializes a #DBusMessageIter for reading the arguments of the message passed in.
dbus_message_iter_next()
--------------------------------------
    Moves the iterator to the next field, if any. If there's no next field, returns #FALSE. If the iterator moves forward, returns #TRUE.
dbus_message_iter_get_arg_type()
--------------------------------------
    Returns the argument type of the argument that the message iterator points to. If the iterator is at the end of the message, returns #DBUS_TYPE_INVALID. 
dbus_message_iter_get_basic()
--------------------------------------
    Reads a basic-typed value from the message iterator. Basic types are the non-containers such as integer and string.
dbus_message_new_signal()
--------------------------------------
    Constructs a new message representing a signal emission. Returns #NULL if memory can't be allocated for the message. A signal is identified by its originating object path, interface, and the name of the signal.
    Path, interface, and signal name must all be valid (the D-Bus specification defines the syntax of these fields).
    @param path the path to the object emitting the signal
    @param interface the interface the signal is emitted from
    @param name name of the signal
    @returns a new DBusMessage, free with dbus_message_unref()
dbus_message_iter_init_append()
--------------------------------------
    Initializes a #DBusMessageIter for appending arguments to the end of a message.
    @param message the message
    @param iter pointer to an iterator to initialize
dbus_message_iter_append_basic()
--------------------------------------
    Appends a basic-typed value to the message. The basic types are the non-container types such as integer and string.
    @param iter the append iterator
    @param type the type of the value
    @param value the address of the value
    @returns #FALSE if not enough memory
dbus_message_new_method_call()
--------------------------------------
    Constructs a new message to invoke a method on a remote object. Returns #NULL if memory can't be allocated for the message. The destination may be #NULL in which case no destination is set; this is appropriate when using D-Bus in a peer-to-peer context (no message bus). The interface may be #NULL, which means that if multiple methods with the given name exist it is undefined which one will be invoked.
    The path and method names may not be #NULL.
    Destination, path, interface, and method name can't contain any invalid characters (see the D-Bus specification).
    @param destination name that the message should be sent to or #NULL
    @param path object path the message should be sent to
    @param interface interface to invoke method on, or #NULL
    @param method method to invoke
    @returns a new DBusMessage, free with dbus_message_unref()
dbus_bus_get()
--------------------------------------
    Connects to a bus daemon and registers the client with it. If a connection to the bus already exists, then that connection is returned. The caller of this function owns a reference to the bus.
    @param type bus type
    @param error address where an error can be returned.
    @returns a #DBusConnection with new ref
dbus_bus_request_name()
--------------------------------------
    Asks the bus to assign the given name to this connection by invoking the RequestName method on the bus.
    First you should know that for each bus name, the bus stores a queue of connections that would like to own it. Only one owns it at a time - called the primary owner. If the primary owner releases the name or disconnects, then the next owner in the queue atomically takes over.
    So for example if you have an application org.freedesktop.TextEditor and multiple instances of it can be run, you can have all of them sitting in the queue. The first one to start up will receive messages sent to org.freedesktop.TextEditor, but if that one exits another will become the primary owner and receive messages.
    The queue means you don't need to manually watch for the current owner to disappear and then request the name again.
    @param connection the connection
    @param name the name to request
    @param flags flags
    @param error location to store the error
    @returns a result code, -1 if error is set
    给DBusConnection起名字(命名) -- 两个相互通讯的链接(connection)不能同名
    命名规则: xxx.xxx (zeng.xiaolong)
dbus_bus_add_match()
--------------------------------------
    Adds a match rule to match messages going through the message bus. The "rule" argument is the string form of a match rule.
    @param connection connection to the message bus
    @param rule textual form of match rule
    @param error location to store any errors
dbus_pending_call_block()
--------------------------------------
    Block until the pending call is completed. The blocking is as with dbus_connection_send_with_reply_and_block(); it does not enter the main loop or process other messages, it simply waits for the reply in question.
    If the pending call is already completed, this function returns immediately.
    @todo when you start blocking, the timeout is reset, but it should really only use time remaining since the pending call was created. This requires storing timestamps instead of intervals in the timeout
    @param pending the pending call
dbus_pending_call_steal_reply()
--------------------------------------
    Gets the reply, or returns #NULL if none has been received yet. Ownership of the reply message passes to the caller. This function can only be called once per pending call, since the reply message is tranferred to the caller.
    @param pending the pending call
    @returns the reply message or #NULL.

安装D-Bus可在其官方网站下载源码编译,地址为http://dbus.freedesktop.org。或者在终端上输入下列指令:

  1.  
    1. yum install dbus dbus-devel dbus-doc

安装后,头文件位于"/usr/include/dbus-<版本号>/dbus"目录中,编译使用D-Bus的程序时需加入编译指令"`pkg-config --cflags --libs dbus-1`"。

3.  D-Bus的用例

在使用GNOME桌面环境的Linux系统中,一般用GLib库提供的函数来管理总线。在测试下列用例前,首先须要安装GTK+开发包(见22.3节)并配置编译环境。该用例一共包含两个程序文件,每一个程序文件需单独编译成为可执行文件。

1.消息发送程序

"dbus-ding-send.c"程序每秒经过会话总线发送一个参数为字符串Ding!的信号。该程序的源代码以下:

  1. #include <glib.h>                               // 包含glib库  
  2. #include <dbus/dbus-glib.h>                     // 包含
    glib库中D-Bus管理库  
  3. #include <stdio.h> 
  4. static gboolean send_ding(DBusConnection *bus);// 定义发送消息函数的原型  
  5. int main ()  
  6. {  
  7.    GMainLoop *loop;                             // 定义一个事件循环对象的指针  
  8.    DBusConnection *bus;                         // 定义总线链接对象的指针  
  9.    DBusError error;                             // 定义D-Bus错误消息对象  
  10.    loop = g_main_loop_new(NULL, FALSE);         // 建立新事件循环对象  
  11.    dbus_error_init (&error);                    // 将错误消息对象链接到D-Bus  
  12.                                                 // 错误消息对象  
  13.    bus = dbus_bus_get(DBUS_BUS_SESSION, &error);// 链接到总线  
  14.    if (!bus) {                              // 判断是否链接错误  
  15. g_warning("链接到D-Bus失败: %s", error.message);  
  16.                                         // 使用GLib输出错误警告信息  
  17.       dbus_error_free(&error);              // 清除错误消息  
  18.       return 1;  
  19.    }  
  20.    dbus_connection_setup_with_g_main(bus, NULL);  
  21.                                             // 将总线设为接收GLib事件循环  
  22.    g_timeout_add(1000, (GSourceFunc)send_ding, bus);  
  23.                                     // 每隔1000ms调用一次send_ding()函数  
  24.                                             // 将总线指针做为参数  
  25.    g_main_loop_run(loop);                   // 启动事件循环  
  26.    return 0;  
  27. }  
  28. static gboolean send_ding(DBusConnection *bus)  // 定义发
    送消息函数的细节  
  29. {  
  30.    DBusMessage *message;                        // 建立消息对象指针  
  31.    message = dbus_message_new_signal("/com/burtonini/dbus/ding",   
  32.                                        "com.burtonini.dbus.Signal",  
  33.                                        "ding");     // 建立消息对象并标识路径  
  34.    dbus_message_append_args(message,  
  35.                             DBUS_TYPE_STRING, "ding!",  
  36.                             DBUS_TYPE_INVALID);     //将字符串Ding!定义为消息  
  37.    dbus_connection_send(bus, message, NULL);    // 发送该消息  
  38.    dbus_message_unref(message);                 // 释放消息对象  
  39.    g_print("ding!/n");                          // 该函数等同与标准输入输出                                     
  40.    return TRUE;  
  41. }

main()函数建立一个GLib事件循环,得到会话总线的一个链接,并将D-Bus事件处理集成到GLib事件循环之中。而后它建立了一个名为 send_ding()函数做为间隔为一秒的计时器,并启动事件循环。send_ding()函数构造一个来自于对象路径"/com/burtonini /dbus/ding"和接口"com.burtonini.dbus.Signal"的新的Ding信号。而后,字符串Ding!做为参数添加到信号中 并经过总线发送。在标准输出中会打印一条消息以让用户知道发送了一个信号。

2.消息接收程序

dbus-ding-listen.c程序经过会话总线接收dbus-ding-send.c程序发送到消息。该程序的源代码以下:

  1. #include <glib.h>                               // 包含glib库  
  2. #include <dbus/dbus-glib.h>                     // 包含glib库中D-Bus管理库  
  3. static DBusHandlerResult signal_filter      // 定义接收消息函数的原型  
  4.       (DBusConnection *connection, DBusMessage *message, void *user_data);  
  5. int main()  
  6. {  
  7.    GMainLoop *loop;                             // 定义一个事件循环对象的指针  
  8.    DBusConnection *bus;                         // 定义总线链接对象的指针  
  9.    DBusError error;                             // 定义D-Bus错误消息对象  
  10.    loop = g_main_loop_new(NULL, FALSE);         // 建立新事件循环对象  
  11.    dbus_error_init(&error);                     // 将错误消息对象链接到D-Bus  
  12.                                                 // 错误消息对象  
  13.    bus = dbus_bus_get(DBUS_BUS_SESSION, &error);    // 链接到总线  
  14.    if (!bus) {                              // 判断是否链接错误  
  15. g_warning("链接到D-Bus失败: %s", error.message);  
  16.                                         // 使用GLib输出错误警告信息  
  17.       dbus_error_free(&error);              // 清除错误消息  
  18.       return 1;  
  19.   }  
  20.    dbus_connection_setup_with_g_main(bus, NULL);      
  21.                                             // 将总线设为接收GLib事件循环  
  22.    dbus_bus_add_match(bus, "type='signal',interface
    ='com.burtonini.dbus.Signal'");  // 定义匹配器  
  23.    dbus_connection_add_filter(bus, signal_filter, loop, NULL);  
  24.                                             // 调用函数接收消息  
  25.    g_main_loop_run(loop);                   // 启动事件循环  
  26.    return 0;  
  27. }  
  28. static DBusHandlerResult                    // 定义接收消息函数的细节  
  29. signal_filter (DBusConnection *connection, 
    DBusMessage *message, void *user_data)  
  30. {  
  31.    GMainLoop *loop = user_data;             // 定义事件循环对象的指针,并与主函数中的同步  
  32.    if (dbus_message_is_signal               // 接收链接成功消息,判断是否链接失败  
  33.         (message, DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL,
    "Disconnected")) {  
  34.       g_main_loop_quit (loop);              // 退出主循环  
  35.       return DBUS_HANDLER_RESULT_HANDLED;  
  36.    }  
  37.    if (dbus_message_is_signal(message, "com.burtonini.dbus.Signal",   
  38.    "Ping")) {  
  39.                                             // 指定消息对象路径,判断是否成功  
  40.       DBusError error;                      // 定义错误对象  
  41.       char *s;  
  42. dbus_error_init(&error);                // 将错误消息对象链接到D-Bus错误  
  43.                                         // 消息对象  
  44.       if (dbus_message_get_args                 // 接收消息,并判断是否有错误  
  45.          (message, &error, DBUS_TYPE_STRING, &s, 
    DBUS_TYPE_INVALID)) {  
  46.          g_print("接收到的消息是: %s/n", s);   // 输出接收到的消息  
  47.          dbus_free (s);                     // 清除该消息  
  48.       }   
  49.       else {                                    // 有错误时执行下列语句  
  50.          g_print("消息已收到,但有错误提示: %s/n", error.message);  
  51.          dbus_error_free (&error);  
  52.       }  
  53.       return DBUS_HANDLER_RESULT_HANDLED;  
  54.    }  
  55.    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;  
  56. }

该程序侦听dbus-ping-send.c程序正在发出的信号。main()函数和前面同样启动,建立一个到总线的链接。而后它声明愿意在使用 com.burtonini.dbus.Signal接口的信号被发送时获得通知,将signal_filter()函数设置为通知函数,而后进入事件循 环。当知足匹配的消息被发送时,signal_func()函数会被调用。

若是须要肯定在接收消息时如何处理,可经过检测消息头实现。若收到的消息为总线断开信号,则主事件循环将被终止,由于监听的总线已经不存在了。若收 到其余的消息,首先将收到的消息与期待的消息进行比较,二者相同则输出其中参数,并退出程序。二者不相同则告知总线并无处理该消息,这样消息会继续保留 在总线中供别的程序处理。

 

dbus实例讲解

http://blog.csdn.net/fmddlmyy/archive/2008/12/23/3585730.aspx

 

 

网上有很多介绍dbus的文章。本文的目标是补充一些简单的例子。

一、dbus是什么东西?

网上有一篇叫“D-Bus Tutorial”的文章,流传较广。很多介绍dbus的资料,都引用了其中的段落。其实相对于这篇文章,我建议你们直接读“D-Bus Specification”,篇幅不算长,文字也不算枯燥。

D-Bus是针对桌面环境优化的IPC(interprocess communication )机制,用于进程间的通讯或进程与内核的通讯。最基本的D-Bus协议是一对一的通讯协议。但在不少状况下,通讯的一方是消息总线。消息总线是一个特殊的 应用,它同时与多个应用通讯,并在应用之间传递消息。下面咱们会在实例中观察消息总线的做用。消息总线的角色有点相似与X系统中的窗口管理器,窗口管理器 既是X客户,又负责管理窗口。

支持dbus的系统都有两个标准的消息总线:系统总线和会话总线。系统总线用于系统与应用的通讯。会话总线用于应用之间的通讯。网上有一个叫d-feet的python程序,咱们能够用它来观察系统中的dbus世界。

图一、由d-feet观察到的D-Bus世界

D-Bus是一个程序。它提供了API。但咱们通常不会直接使用dbus的接口。dbus-glib是GTK版本的dbus接口封装。本文假设读者 安装了dbus-glib,我安装的是dbus-glib-0.76。后面还会看到,经过python操纵dbus是多么简单。

二、D-Bus的基本概念

2.一、从例子开始

我写了一个最简单的dbus服务器,它经过dbus提供了一个加法的接口。你们能够下载这个例子。这是一个autotool工程,你们解包后,执行:

./autogen.sh
./configure
make

而后在src目录运行:

./example-service

这时再运行d-feet,链接session bus,在“Bus Name”窗口会看到一个叫“org.fmddlmyy.Test”链接名。

图二、提供D-Bus服务的org.fmddlmyy.Test

选择“org.fmddlmyy.Test”,在右侧窗口点击展开“Object Paths”->“/TestObj”->“Interfaces”->“org.fmddlmyy.Test.Basic”->“Methods”, 能够看到一个Add方法。双击Add方法,弹出下面这个对话框:

图三、经过D-Bus接口计算1+2=3

在Parameters窗口输入“1,2”,点击“Execute”按钮,而后在“Output”窗口咱们看到了输出结果。咱们刚刚建立了一个dbus服务并调用了它。

2.二、名词

咱们来解释一下d-feet中出现的名词。

2.2.一、Bus Name

能够把Bus Name理解为链接的名称,一个Bus Name老是表明一个应用和消息总线的链接。有两种做用不一样的Bus Name,一个叫公共名(well-known names),还有一个叫惟一名(Unique Connection Name)。

2.2.1.一、可能有多个备选链接的公共名

公共名提供众所周知的服务。其余应用经过这个名称来使用名称对应的服务。可能有多个链接要求提供同个公共名的服务,即多个应用链接到消息总线,要求 提供同个公共名的服务。消息总线会把这些链接排在链表中,并选择一个链接提供公共名表明的服务。能够说这个提供服务的链接拥有了这个公共名。若是这个链接 退出了,消息总线会从链表中选择下一个链接提供服务。公共名是由一些圆点分隔的多个小写标志符组成的,例如“org.fmddlmyy.Test”、 “org.bluez”。

2.2.1.二、每一个链接都有一个惟一名

当应用链接到消息总线时,消息总线会给每一个应用分配一个惟一名。惟一名以“:”开头,“:”后面一般是圆点分隔的两个数字,例如“:1.0”。每一个 链接都有一个惟一名。在一个消息总线的生命期内,不会有两个链接有相同的惟一名。拥有公众名的链接一样有惟一名,例如在前面的图 中,“org.fmddlmyy.Test”的惟一名是“:1.17”。

有的链接只有惟一名,没有公众名。能够把这些名称称为私有链接,由于它们没有提供能够经过公共名访问的服务。 d-feet界面上有个“Hide Private”按钮,能够用来隐藏私有链接。

2.2.二、Object Paths

Bus Name肯定了一个应用到消息总线的链接。在一个应用中能够有多个提供服务的对象。这些对象按照树状结构组织起来。每一个对象都有一个惟一的路径(Object Paths)。或者说,在一个应用中,一个对象路径标志着一个惟一的对象。

“org.fmddlmyy.Test”只有一个叫做“/TestObj”的对象。图1中的“org.bluez”有多个对象路径。

2.2.三、Interfaces

经过对象路径,咱们找到应用中的一个对象。每一个对象能够实现多个接口。例如:“org.fmddlmyy.Test”的“/TestObj”实现了如下接口:

  • org.fmddlmyy.Test.Basic
  • org.freedesktop.DBus.Introspectable
  • org.freedesktop.DBus.Properties

后面讲代码时会看到,咱们在代码中其实只实现了“org.fmddlmyy.Test.Basic”这个接口。接口 “org.freedesktop.DBus.Introspectable”和“org.freedesktop.DBus.Properties”是 消息总线提供的标准接口。

2.2.四、Methods和Signals

接口包括方法和信号。例如“org.fmddlmyy.Test”的“/TestObj”对象的“org.fmddlmyy.Test.Basic”接口有一个Add方法。后面的例子中咱们会介绍信号。

标准接口“org.freedesktop.DBus.Introspectable”的Introspect方法是个颇有用的方法。相似于 Java的反射接口,调用Introspect方法能够返回接口的xml描述。咱们双击 “org.fmddlmyy.Test”->“/TestObj”->“org.fmddlmyy.Test.Basic”->“org.freedesktop.DBus.Introspectable” 的Introspect方法。这个方法没有输入参数,咱们直接点击“Execute”按钮,你在“Output”窗口看到了什么?

图四、调用Introspect方法

后面咱们会用另外一种方式调用Introspect方法。

2.3 小结

“org.fmddlmyy.Test”->“/TestObj”->“org.fmddlmyy.Test.Basic”->“org.freedesktop.DBus.Introspectable” 的Introspect方法,这个描述是否是很麻烦。其实前面还要加上“session bus”。

后面在看客户端的C代码时,咱们会看到一样的过程:用dbus_g_bus_get获得到session bus的链接。在这个链接上用dbus_g_proxy_new_for_name函数得到到拥有指定公共名的链接的指定对象的指定接口的代理。最后,用 dbus_g_proxy_call函数经过接口代理调用接口提供的方法。

3 下集预告

d-feet虽然很方便,但它使用了python的gtk模块,在一些嵌入式环境可能使用不了。后面会看到,用一个叫dbus-send的命令行工 具,或者写几行python脚本均可以完成一样的工做。咱们还会用一个叫dbus-monitor的命令行工具观察dbus调用过程当中究竟发生了什么?

 

 

应用程序A和消息总线链接,这个链接获取了一个众所周知的公共名(记做链接A)。应用程序A中有对象A1提供了接口I1,接口I1有方法M1。应用程序B和消息总线链接,要求调用链接A上对象A1的接口I1的方法M1。

在上一讲的加法例子中,上面这段话能够实例化为:应用程序example-service和会话总线链接。这个链接获取了一个众所周知的公共名 “org.fmddlmyy.Test”。应用程序example-servic中有对象“/TestObj”提供了接口 “org.fmddlmyy.Test.Basic”,接口“org.fmddlmyy.Test.Basic”有方法“Add”。应用程序d-feet 和会话总线链接,要求调用链接“org.fmddlmyy.Test”上对象“/TestObj”的接口 “org.fmddlmyy.Test.Basic”的方法“Add”。

应用程序B调用应用程序A的方法,其实就是应用程序B向应用程序A发送了一个类型为“method_call”的消息。应用程序A经过一个类型为“method_retutn”的消息将返回值发给应用程序B。咱们简单介绍一下D-Bus总线上的消息。

一、D-Bus的消息

上一讲说过最基本的D-Bus协议是一对一的通讯协议。与直接使用socket不一样,D-Bus是面向消息的协议。 D-Bus的全部功能都是经过在链接上流动的消息完成的。

1.一、消息类型

D-Bus有四种类型的消息:

  • method_call 方法调用
  • method_return 方法返回
  • error 错误
  • signal 信号

前面介绍的远程方法调用就用到了method_call和method_return消息。顾名思义,在发生错误时会产生error消息。若是把method_call看做打电话,那么signal消息就是来电了。后面还会详细讨论。

1.二、dbus-send和dbus-monitor

dbus提供了两个小工具:dbus-send和dbus-monitor。咱们能够用dbus-send发送消息。用dbus-monitor监 视总线上流动的消息。让咱们经过dbus-send发送消息来调用前面的Add方法,这时dbus-send充当了应用程序B。用dbus- monitor观察调用过程当中的消息。

启动example-service:

$ ./example-service 

在另外一个控制台启动dbus-monitor:

$ dbus-monitor

dbus-monitor默认监视会话总线。执行:

$ dbus-send --session --type=method_call --print-reply --dest=org.fmddlmyy.Test /TestObj org.fmddlmyy.Test.Basic.Add int32:100 int32:999

输出为:

method return sender=:1.21 -> dest=:1.22 reply_serial=2
   int32 1099

dbus-monitor的相关输出包括:

signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.22"
   string ""
   string ":1.22"
method call sender=:1.22 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=Hello
method call sender=:1.22 -> dest=org.fmddlmyy.Test path=/TestObj; interface=org.fmddlmyy.Test.Basic; member=Add
   int32 100
   int32 999
method return sender=:1.21 -> dest=:1.22 reply_serial=2
   int32 1099
signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.22"
   string ":1.22"
   string ""

:1.22就是dbus-send在本次调用中与会话总线所创建链接的惟一名。:1.21是链接“org.fmddlmyy.Test”的惟一名。 在以上输出中咱们能够看到:1.22向“org.fmddlmyy.Test”发送method_call消息,调用Add方法。 :1.21经过method_return消息将调用结果发回:1.22。其它输出信息会在之后说明。

dbus-send的详细用法能够参阅手册。调用远程方法的通常形式是:

$ dbus-send [--system | --session] --type=method_call --print-reply --dest=链接名 对象路径 接口名.方法名 参数类型:参数值 参数类型:参数值

dbus-send支持的参数类型包括:string, int32, uint32, double, byte, boolean。

二、消息总线的方法和信号

2.一、概述

消息总线是一个特殊的应用,它能够在与它链接的应用之间传递消息。能够把消息总线看做一台路由器。正是经过消息总线,D-Bus才在一对一的通讯协议基础上实现了多对一和一对多的通讯。

消息总线虽然有特殊的转发功能,但消息总线也仍是一个应用。其它应用与消息总线的通讯也是经过1.1节的基本消息类型完成的。做为一个应用,消息总线也提供了本身的接口,包括方法和信号。

咱们能够经过向链接“org.freedesktop.DBus ”上对象“/”发送消息来调用消息总线提供的方法。事实上,应用程序正是经过这些方法链接到消息总线上的其它应用,完成请求公共名等工做的。

2.二、清单

消息总线对象支持第一讲中提到的标准接口"org.freedesktop.DBus.Introspectable",咱们能够调用org.freedesktop.DBus.Introspectable.Introspect方法查看消息总线对象支持的接口。例如:

$ dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.Introspectable.Introspect

输出为:

method return sender=org.freedesktop.DBus -> dest=:1.20 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">
    <method name="Hello">
      <arg direction="out" type="s"/>
    </method>
    <method name="RequestName">
      <arg direction="in" type="s"/>
      <arg direction="in" type="u"/>
      <arg direction="out" type="u"/>
    </method>
    <method name="ReleaseName">
      <arg direction="in" type="s"/>
      <arg direction="out" type="u"/>
    </method>
    <method name="StartServiceByName">
      <arg direction="in" type="s"/>
      <arg direction="in" type="u"/>
      <arg direction="out" type="u"/>
    </method>
    <method name="NameHasOwner">
      <arg direction="in" type="s"/>
      <arg direction="out" type="b"/>
    </method>
    <method name="ListNames">
      <arg direction="out" type="as"/>
    </method>
    <method name="ListActivatableNames">
      <arg direction="out" type="as"/>
    </method>
    <method name="AddMatch">
      <arg direction="in" type="s"/>
    </method>
    <method name="RemoveMatch">
      <arg direction="in" type="s"/>
    </method>
    <method name="GetNameOwner">
      <arg direction="in" type="s"/>
      <arg direction="out" type="s"/>
    </method>
    <method name="ListQueuedOwners">
      <arg direction="in" type="s"/>
      <arg direction="out" type="as"/>
    </method>
    <method name="GetConnectionUnixUser">
      <arg direction="in" type="s"/>
      <arg direction="out" type="u"/>
    </method>
    <method name="GetConnectionUnixProcessID">
      <arg direction="in" type="s"/>
      <arg direction="out" type="u"/>
    </method>
    <method name="GetConnectionSELinuxSecurityContext">
      <arg direction="in" type="s"/>
      <arg direction="out" type="ay"/>
    </method>
    <method name="ReloadConfig">
    </method>
    <method name="GetId">
      <arg direction="out" type="s"/>
    </method>
    <signal name="NameOwnerChanged">
      <arg type="s"/>
      <arg type="s"/>
      <arg type="s"/>
    </signal>
    <signal name="NameLost">
      <arg type="s"/>
    </signal>
    <signal name="NameAcquired">
      <arg type="s"/>
    </signal>
  </interface>
</node>
"

从输出能够看到会话总线对象支持标准接口“org.freedesktop.DBus.Introspectable”和接口 “org.freedesktop.DBus”。接口“org.freedesktop.DBus”有16个方法和3个信号。下表列出了 “org.freedesktop.DBus”的12个方法的简要说明:

 

org.freedesktop.DBus.RequestName (in STRING name, in UINT32 flags, out UINT32 reply) 请求公众名。其中flag定义以下:
DBUS_NAME_FLAG_ALLOW_REPLACEMENT 1
DBUS_NAME_FLAG_REPLACE_EXISTING 2
DBUS_NAME_FLAG_DO_NOT_QUEUE 4

返回值reply定义以下:
DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1
DBUS_REQUEST_NAME_REPLY_IN_QUEUE 2
DBUS_REQUEST_NAME_REPLY_EXISTS 3
DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER 4
org.freedesktop.DBus.ReleaseName (in STRING name, out UINT32 reply) 释放公众名。返回值reply定义以下:
DBUS_RELEASE_NAME_REPLY_RELEASED 1
DBUS_RELEASE_NAME_REPLY_NON_EXISTENT 2
DBUS_RELEASE_NAME_REPLY_NOT_OWNER 3 
org.freedesktop.DBus.Hello (out STRING unique_name) 一个应用在经过消息总线向其它应用发消息前必须先调用Hello获取本身这个链接的惟一名。返回值就是链接的惟一名。dbus没有定义专门的切断链接命令,关闭socket就是切断链接。
在1.2节的dbus-monitor输出中能够看到dbus-send调用消息总线的Hello方法。
org.freedesktop.DBus.ListNames (out ARRAY of STRING bus_names) 返回消息总线上已链接的全部链接名,包括全部公共名和惟一名。例如链接“org.fmddlmyy.Test”同时有公共名“org.fmddlmyy.Test”和惟一名“:1.21”,这两个名称都会被返回。
org.freedesktop.DBus.ListActivatableNames (out ARRAY of STRING bus_names) 返回全部能够启动的服务名。dbus支持按需启动服务,即根据应用程序的请求启动服务。
org.freedesktop.DBus.NameHasOwner (in STRING name, out BOOLEAN has_owner) 检查是否有链接拥有指定名称。
org.freedesktop.DBus.StartServiceByName (in STRING name, in UINT32 flags, out UINT32 ret_val) 按名称启动服务。参数flags暂未使用。返回值ret_val定义以下:
1 服务被成功启动
2 已经有链接拥有要启动的服务名
org.freedesktop.DBus.GetNameOwner (in STRING name, out STRING unique_connection_name) 返回拥有指定公众名的链接的惟一名。
org.freedesktop.DBus.GetConnectionUnixUser (in STRING connection_name, out UINT32 unix_user_id) 返回指定链接对应的服务器进程的Unix用户id。
org.freedesktop.DBus.AddMatch (in STRING rule) 为当前链接增长匹配规则。
org.freedesktop.DBus.RemoveMatch (in STRING rule) 为当前链接去掉指定匹配规则。
org.freedesktop.DBus.GetId (out STRING id) 返回消息总线的ID。这个ID在消息总线的生命期内是惟一的。

 

接口“org.freedesktop.DBus”的3个信号是:

 

org.freedesktop.DBus.NameOwnerChanged (STRING name, STRING old_owner, STRING new_owner) 指定名称的拥有者发生了变化。
org.freedesktop.DBus.NameLost (STRING name) 通知应用失去了指定名称的拥有权。
org.freedesktop.DBus.NameAcquired (STRING name) 通知应用得到了指定名称的拥有权。

 

2.三、练习

让咱们来试试消息总线提供的方法。

2.3.一、从ListName到d-feet的基本逻辑

用dbus-send调用:

$ dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListNames

输出为:

method return sender=org.freedesktop.DBus -> dest=:1.23 reply_serial=2
   array [
      string "org.freedesktop.DBus"
      string "org.freedesktop.Notifications"
      string "org.freedesktop.Tracker"
      string "org.freedesktop.PowerManagement"
      string ":1.7"
      string ":1.8"
      string "org.gnome.ScreenSaver"
      string ":1.9"
      string ":1.10"
      string ":1.22"
      string ":1.11"
      string "org.gnome.GnomeVFS.Daemon"
      string ":1.23"
      string ":1.12"
      string ":1.13"
      string ":1.0"
      string ":1.14"
      string ":1.1"
      string ":1.15"
      string ":1.2"
      string ":1.16"
      string ":1.3"
      string "org.gnome.GkbdConfigRegistry"
      string ":1.4"
      string "org.fmddlmyy.Test"
      string ":1.5"
      string "org.gnome.SettingsDaemon"
      string ":1.6"
   ]

这是会话总线当前已链接的链接名。在d-feet窗口的左侧窗口显示的就是ListNames返回的链接名。聪明的读者也许已经想到使用消息总线的 “org.freedesktop.DBus.ListNames”方法和各链接的 “org.freedesktop.DBus.Introspectable.Introspect”,咱们就能够像d-feet同样查看总线上全部链接 的全部对象的全部接口的全部方法和信号。

你的想法很好。但有一个问题,咱们必须对链接中的对象调用“org.freedesktop.DBus.Introspectable.Introspect”方法。 ListNames只列出了链接名,咱们怎么获取链接中的对象路径呢?

答案很简单,若是咱们不知道对象路径就从根目录开始吧。链接中的对象是按照树型结构组织的。咱们遍历链接的对象树就能够找到全部的对象。调用对象的 “org.freedesktop.DBus.Introspectable.Introspect”方法就能够查看对象的全部接口的全部方法和信号。例 如:假设咱们不知道链接"org.fmddlmyy.Test"里有什么对象,咱们能够对根对象"/"执行:

$ dbus-send --session --type=method_call --print-reply --dest=org.fmddlmyy.Test / org.freedesktop.DBus.Introspectable.Introspect

输出为:

method return sender=:1.22 -> dest=:1.25 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="TestObj"/>
</node>
"

"org.fmddlmyy.Test"的对象树的根节点只有一个子节点"TestObj",再查看"/TestObj":

$ dbus-send --session --type=method_call --print-reply --dest=org.fmddlmyy.Test /TestObj org.freedesktop.DBus.Introspectable.Introspect

输出为:

method return sender=:1.22 -> dest=:1.26 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.fmddlmyy.Test.Basic">
    <method name="Add">
      <arg name="arg0" type="i" direction="in"/>
      <arg name="arg1" type="i" direction="in"/>
      <arg name="ret" type="i" direction="out"/>
    </method>
  </interface>
</node>
"

做为一个练习,让咱们来查看系统总线的上的bluez接口。执行:

$ dbus-send --system --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListNames

输出为:

method return sender=org.freedesktop.DBus -> dest=:1.30 reply_serial=2
   array [
      string "org.freedesktop.DBus"
      string ":1.7"
      string ":1.8"
      string ":1.9"
      string "org.freedesktop.SystemToolsBackends"
      string ":1.30"
      string "org.freedesktop.NetworkManagerInfo"
      string ":1.20"
      string "org.freedesktop.Avahi"
      string ":1.21"
      string "org.bluez"
      string ":1.22"
      string "org.freedesktop.NetworkManager"
      string "org.freedesktop.ConsoleKit"
      string ":1.23"
      string "com.redhat.dhcp"
      string ":1.13"
      string ":1.0"
      string ":1.14"
      string ":1.1"
      string ":1.15"
      string ":1.2"
      string "org.freedesktop.Hal"
      string "com.redhat.NewPrinterNotification"
      string ":1.16"
      string ":1.3"
      string ":1.17"
      string ":1.4"
      string ":1.18"
      string ":1.5"
      string ":1.19"
      string ":1.6"
   ]

咱们看到链接"org.bluez"。查看它的根对象:

$ dbus-send --system --type=method_call --print-reply --dest=org.bluez / org.freedesktop.DBus.Introspectable.Introspect

输出为:

method return sender=:1.7 -> dest=:1.31 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>
"

接着查对象"/org":

$ dbus-send --system --type=method_call --print-reply --dest=org.bluez /org org.freedesktop.DBus.Introspectable.Introspect

输出为:

method return sender=:1.7 -> dest=:1.32 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="bluez"/>
</node>
"

接着查对象"/org/bluez":

$ dbus-send --system --type=method_call --print-reply --dest=org.bluez /org/bluez org.freedesktop.DBus.Introspectable.Introspect

输出为:

method return sender=:1.7 -> dest=:1.33 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 name="/org/bluez">
        <interface name="org.bluez.Manager">
                <method name="InterfaceVersion">
                        <arg type="u" direction="out"/>
                </method>
                <method name="DefaultAdapter">
                        <arg type="s" direction="out"/>
                </method>
                <method name="FindAdapter">
                        <arg type="s" direction="in"/>
                        <arg type="s" direction="out"/>
                </method>
                <method name="ListAdapters">
                        <arg type="as" direction="out"/>
                </method>
                <method name="FindService">
                        <arg type="s" direction="in"/>
                        <arg type="s" direction="out"/>
                </method>
                <method name="ListServices">
                        <arg type="as" direction="out"/>
                </method>
                <method name="ActivateService">
                        <arg type="s" direction="in"/>
                        <arg type="s" direction="out"/>
                </method>
                <signal name="AdapterAdded">
                        <arg type="s"/>
                </signal>
                <signal name="AdapterRemoved">
                        <arg type="s"/>
                </signal>
                <signal name="DefaultAdapterChanged">
                        <arg type="s"/>
                </signal>
                <signal name="ServiceAdded">
                        <arg type="s"/>
                </signal>
                <signal name="ServiceRemoved">
                        <arg type="s"/>
                </signal>
        </interface>
        <interface name="org.bluez.Database">
                <method name="AddServiceRecord">
                        <arg type="ay" direction="in"/>
                        <arg type="u" direction="out"/>
                </method>
                <method name="AddServiceRecordFromXML">
                        <arg type="s" direction="in"/>
                        <arg type="u" direction="out"/>
                </method>
                <method name="UpdateServiceRecord">
                        <arg type="u" direction="in"/>
                        <arg type="ay" direction="in"/>
                </method>
                <method name="UpdateServiceRecordFromXML">
                        <arg type="u" direction="in"/>
                        <arg type="s" direction="in"/>
                </method>
                <method name="RemoveServiceRecord">
                        <arg type="u" direction="in"/>
                </method>
                <method name="RegisterService">
                        <arg type="s" direction="in"/>
                        <arg type="s" direction="in"/>
                        <arg type="s" direction="in"/>
                </method>
                <method name="UnregisterService">
                        <arg type="s" direction="in"/>
                </method>
                <method name="RequestAuthorization">
                        <arg type="s" direction="in"/>
                        <arg type="s" direction="in"/>
                </method>
                <method name="CancelAuthorizationRequest">
                        <arg type="s" direction="in"/>
                        <arg type="s" direction="in"/>
                </method>
        </interface>
        <interface name="org.bluez.Security">
                <method name="RegisterDefaultPasskeyAgent">
                        <arg type="s" direction="in"/>
                </method>
                <method name="UnregisterDefaultPasskeyAgent">
                        <arg type="s" direction="in"/>
                </method>
                <method name="RegisterPasskeyAgent">
                        <arg type="s" direction="in"/>
                        <arg type="s" direction="in"/>
                </method>
                <method name="UnregisterPasskeyAgent">
                        <arg type="s" direction="in"/>
                        <arg type="s" direction="in"/>
                </method>
                <method name="RegisterDefaultAuthorizationAgent">
                        <arg type="s" direction="in"/>
                </method>
                <method name="UnregisterDefaultAuthorizationAgent">
                        <arg type="s" direction="in"/>
                </method>
        </interface>
        <node name="service_audio"/>
        <node name="service_input"/>
        <node name="service_network"/>
        <node name="service_serial"/>
</node>
"

咱们看到了对象"/org/bluez"的全部接口。对象"/org/bluez"还有子节 点"service_audio"、"service_input"、"service_network"和"service_serial"。必要时我 们能够接着查下去。d-feet的基本逻辑就是这样。后面咱们会本身实现一个dteeth。dteeth是命令行程序,能够遍历指定链接的对象树,列出所 有对象的全部接口的方法和信号。

 

 

 

2.3.二、ListActivatableNames和服务器的自动启动

运行:

$ dbus-send --system --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListActivatableNames

$ dbus-send --session --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListActivatableNames

返回的数据是同样的。在个人电脑上返回的数据是:

   array [
      string "org.freedesktop.DBus"
      string "org.freedesktop.Notifications"
      string "net.ekiga.helper"
      string "org.freedesktop.PowerManagement"
      string "org.freedesktop.Tracker"
      string "org.freedesktop.SystemToolsBackends.GroupsConfig"
      string "org.freedesktop.SystemToolsBackends.NTPConfig"
      string "org.gnome.Tomboy"
      string "org.freedesktop.SystemToolsBackends.HostsConfig"
      string "org.freedesktop.SystemToolsBackends.NFSConfig"
      string "org.freedesktop.SystemToolsBackends"
      string "net.ekiga.instance"
      string "org.gnome.GnomeVFS.Daemon"
      string "com.redhat.dhcp"
      string "org.freedesktop.SystemToolsBackends.TimeConfig"
      string "org.freedesktop.SystemToolsBackends.IfacesConfig"
      string "org.freedesktop.SystemToolsBackends.ServicesConfig"
      string "org.gnome.Rhythmbox"
      string "org.freedesktop.SystemToolsBackends.Platform"
      string "org.freedesktop.SystemToolsBackends.UsersConfig"
      string "org.freedesktop.SystemToolsBackends.SMBConfig"
      string "org.gnome.SettingsDaemon"
   ]

咱们也能够用python脚本调用ListActivatableNames。例如:写一个叫dls.py的脚本:

$ cat dls.py
#!/usr/bin/env python
import dbus
bus=dbus.SystemBus()
bus_obj=bus.get_object('org.freedesktop.DBus', '/')
iface=dbus.Interface(bus_obj, 'org.freedesktop.DBus')
names=iface.ListActivatableNames()
for n in names:
    print n

运行:

$ ./dls.py |sort

输出为:

com.redhat.dhcp
net.ekiga.helper
net.ekiga.instance
org.freedesktop.DBus
org.freedesktop.Notifications
org.freedesktop.PowerManagement
org.freedesktop.SystemToolsBackends
org.freedesktop.SystemToolsBackends.GroupsConfig
org.freedesktop.SystemToolsBackends.HostsConfig
org.freedesktop.SystemToolsBackends.IfacesConfig
org.freedesktop.SystemToolsBackends.NFSConfig
org.freedesktop.SystemToolsBackends.NTPConfig
org.freedesktop.SystemToolsBackends.Platform
org.freedesktop.SystemToolsBackends.ServicesConfig
org.freedesktop.SystemToolsBackends.SMBConfig
org.freedesktop.SystemToolsBackends.TimeConfig
org.freedesktop.SystemToolsBackends.UsersConfig
org.freedesktop.Tracker
org.gnome.GnomeVFS.Daemon
org.gnome.Rhythmbox
org.gnome.SettingsDaemon
org.gnome.Tomboy

使用python脚本调用dbus接口是否是很简单。若是你看过dbus-glib的代码(后面会讲解),你对python的简洁会有更深入的感触。若是你执行:

$ cat /usr/share/dbus-1/services/*|grep Name|awk -F= '{print $2}'|sort

你会获得:

com.redhat.dhcp
net.ekiga.helper
net.ekiga.instance
org.freedesktop.Notifications
org.freedesktop.PowerManagement
org.freedesktop.SystemToolsBackends
org.freedesktop.SystemToolsBackends.GroupsConfig
org.freedesktop.SystemToolsBackends.HostsConfig
org.freedesktop.SystemToolsBackends.IfacesConfig
org.freedesktop.SystemToolsBackends.NFSConfig
org.freedesktop.SystemToolsBackends.NTPConfig
org.freedesktop.SystemToolsBackends.Platform
org.freedesktop.SystemToolsBackends.ServicesConfig
org.freedesktop.SystemToolsBackends.SMBConfig
org.freedesktop.SystemToolsBackends.TimeConfig
org.freedesktop.SystemToolsBackends.UsersConfig
org.freedesktop.Tracker
org.gnome.GnomeVFS.Daemon
org.gnome.Rhythmbox
org.gnome.SettingsDaemon
org.gnome.Tomboy

这条命令的输出与ListActivatableNames的输出是否是基本相同?你能看懂上面这条命令吗?它将"/usr/share /dbus-1/services/"下全部文件交给grep筛选出包含“Name”的行。将包含“Name”的行交给awk处理,awk用"="做为列 分隔符,取出第二列而后交给sort排序后输出。 "/usr/share/dbus-1/services/"目录就是dbus放service文件的地方。须要自动启动的服务器会在这个目录放一个 service文件,例如:

$ cat /usr/share/dbus-1/services/dhcdbd.service
[D-BUS Service]
Name=com.redhat.dhcp
Exec=/usr/sbin/dhcdbd

Name是服务器的公共名,Exec是服务器的执行路径。在客户请求一个服务,但该服务尚未启动时。dbus会根据service文件自动启动服务。咱们再写一个调用“org.fmddlmyy.Test”的Add接口的python脚本:

$ cat add.py
#!/usr/bin/env python
import dbus
bus = dbus.SessionBus()
obj = bus.get_object( 'org.fmddlmyy.Test', '/TestObj' )
iface = dbus.Interface(obj, 'org.fmddlmyy.Test.Basic')
sum = iface.Add(100, 999)
print sum

在启动“org.fmddlmyy.Test”服务器前调用这个脚本

$ ./add.py

会获得错误输出:

...
dbus.exceptions.DBusException: org.freedesktop.DBus.Error.ServiceUnknown: The name org.fmddlmyy.Test was not provided by any .service files

咱们编辑一个service文件:

$ cat org.fmddlmyy.Test.service
[D-BUS Service]
Name=org.fmddlmyy.Test
Exec=/home/lvjie/work/dbus/hello-dbus3-0.1/src/example-service

把这个文件放到"/usr/share/dbus-1/services/"目录后,再执行add.py:

$ sudo cp org.fmddlmyy.Test.service /usr/share/dbus-1/services/
$ cd ../../py
$ ./add.py
1099

此次dbus自动启动了服务器,咱们的客户脚本获得了正确的输出,你有没有感到dbus的神奇?dbus在自动启动服务器后,不会自动关闭。若是没人管它,这个服务器会一直开着。

2.3.三、其它方法

再演示几个“org.freedesktop.DBus”接口的方法。NameHasOwner判断有没有链接拥有指定的公共名:

$ dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.NameHasOwner string:"org.fmddlmyy.Test"

输出为:

method return sender=org.freedesktop.DBus -> dest=:1.31 reply_serial=2
   boolean true

GetNameOwner返回公共名对应的惟一名:

$ dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.GetNameOwner string:"org.fmddlmyy.Test"

输出为:

method return sender=org.freedesktop.DBus -> dest=:1.32 reply_serial=2
   string ":1.30"

GetConnectionUnixUser返回指定链接对应的服务器进程的Unix用户id:

$ dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.GetConnectionUnixUser string:":1.30"

输出为:

method return sender=org.freedesktop.DBus -> dest=:1.33 reply_serial=2
   uint32 1000

这就是个人用户id:

$ id -u lvjie
1000

GetId返回消息总线的ID:

$ dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.GetId

输出为:

method return sender=org.freedesktop.DBus -> dest=:1.34 reply_serial=2
   string "dc209fee5f8ce01b0c23da0049668f11"

3 结束语

这一集有一些python代码。即便你没有学过python,我也建议你看一看、试一试。其实我也没有学过python。小时候,好像听过什么德国 人一边看说明书一边开飞机的笑话。这或许是吹牛,但对于程序员来讲一边查手册一边用新语言写一些简单程序应该不算困难。下一讲咱们要写一个略大点的 python程序。虽然我习惯于C/C++的事必躬亲,但不能否认python确实是颇有魅力的语言,难怪在嵌入式环境也有那么多人用python做原型 开发。

 

 

 

我想在freerunner(一个开源linux手机)上查看fso(openmoko的诸多软件版本之一)的dbus信 息。但fso的python没有gtk模块,跑不了d-feet。在上一讲我介绍了d-feet的基本思路:用 “org.freedesktop.DBus.ListNames”枚举消息总线上的链接,用 “org.freedesktop.DBus.Introspectable.Introspect” 从"/"开始遍历链接的对象树。上一讲咱们手工查看了两个链接,那么咱们能不能写一个程序自动遍历链接的对象树,输出指定链接的全部对象的全部接口的全部 方法和信号?

固然能够,为此我写了一个叫dteeth的python脚本。不过在介绍这个脚本前,让咱们先看看dbus的数据类型。

一、dbus的数据类型

dbus用xml描述接口,例如:

<?xml version="1.0" encoding="UTF-8" ?>

<node name="/org/freesmartphone/GSM/Device">
  <interface name="org.freesmartphone.GSM.SMS">
    <method name="SendMessage">
       <arg name="number" type="s"/>
       <arg name="contents" type="s"/>
       <arg name="featuremap" type="a{sv}"/>
       <arg type="i" direction="out"/>
    </method>
    <signal name="IncomingMessage">
       <arg name="address" type="s"/>
       <arg name="contents" type="s"/>
       <arg name="features" type="a{sv}"/>
    </signal>
  </interface>
</node>

其实前两讲已经看过不少例子了。node就是接口中的对象,node能够包含node,构成对象树。 dbus的接口描述文件统一采用utf-8编码。我相信读者很容易理解这个接口描述文件。我只想解释一下描述参数数据类型的type域。 dbus的数据类型是由"s"或"a{sv}"这样的类型签名(Type Signatures)定义的。类型签名中可使用如下标记:

a ARRAY 数组
b BOOLEAN 布尔值
d DOUBLE IEEE 754双精度浮点数
g SIGNATURE 类型签名
i INT32 32位有符号整数
n INT16 16位有符号整数
o OBJECT_PATH 对象路径
q UINT16 16位无符号整数
s STRING 零结尾的UTF-8字符串
t UINT64 64位无符号整数
u UINT32 32位无符号整数
v VARIANT 能够听任意数据类型的容器,数据中包含类型信息。例如glib中的GValue。
x INT64 64位有符号整数
y BYTE 8位无符号整数
() 定义结构时使用。例如"(i(ii))"
{} 定义键-值对时使用。例如"a{us}"

a表示数组,数组元素的类型由a后面的标记决定。例如:

  • "as"是字符串数组。
  • 数组"a(i(ii))"的元素是一个结构。用括号将成员的类型括起来就表示结构了,结构能够嵌套。
  • 数组"a{sv}"的元素是一个键-值对。"{sv}"表示键类型是字符串,值类型是VARIANT。

在之后的例子中,咱们会亲手实现上面这个xml描述的接口,包括服务器和客户程序。到时候,读者会对dbus的数据类型有更直观的认识。

二、dteeth

2.一、运行dteeth

能够从这里下载dteeth的源代码。其中包含两个python脚本:dteeth.py和_introspect_parser.py。 dteeth.py是我写的。_introspect_parser.py是个开源模块,能够分析Introspect返回的xml数据。

dteeth用法以下:

$ ./dteeth.py -h
Usage: dteeth [--system] <name of a connection on the bus >

默认链接session总线,除非你加上--system。能够一次指定同一消息总线的多个链接。先在PC上试一试:

$ ./dteeth.py org.fmddlmyy.Test
org.fmddlmyy.Test
    /TestObj
        org.fmddlmyy.Test.Basic
            methods
                Add( in i arg0 , in i arg1 , out i ret )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s data )
        org.freedesktop.DBus.Properties
            methods
                Set( in s interface , in s propname , in v value )
                GetAll( in s interface , out a{sv} props )
                Get( in s interface , in s propname , out v value )

我也在fso版本的freerunner手机上运行了一下,获得了org.freesmartphone.ogsmd的全部对象的全部的接口的全部方法和信号:

org.freesmartphone.ogsmd
    /org/freedesktop/Gypsy
        org.freedesktop.Gypsy.Time
            signals
                TimeChanged( i time )
            methods
                GetTime( out i )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freedesktop.Gypsy.Device
            signals
                FixStatusChanged( i fixstatus )
                ConnectionStatusChanged( b constatus )
            methods
                GetConnectionStatus( out b )
                Stop( )
                Start( )
                GetFixStatus( out i )
        org.freedesktop.Gypsy.Course
            signals
                CourseChanged( i fields , i tstamp , d speed , d heading , d climb )
            methods
                GetCourse( out i , out i , out d , out d , out d )
        org.freedesktop.Gypsy.Position
            signals
                PositionChanged( i fields , i tstamp , d lat , d lon , d alt )
            methods
                GetPosition( out i , out i , out d , out d , out d )
        org.freedesktop.Gypsy.Accuracy
            signals
                AccuracyChanged( i fields , d pdop , d hdop , d vdop )
            methods
                GetAccuracy( out i , out d , out d , out d )
        org.freesmartphone.Resource
            methods
                Enable( )
                Disable( )
                Suspend( )
                Resume( )
        org.freedesktop.Gypsy.Satellite
            signals
                SatellitesChanged( a(ubuuu) satellites )
            methods
                GetSatellites( out a(ubuuu) )
        org.freesmartphone.GPS.UBX
            signals
                DebugPacket( s clid , i length , aa{sv} data )
            methods
                SendDebugPacket( in s clid , in i length , in aa{sv} data )
                GetDebugFilter( in s clid , out b )
                SetDebugFilter( in s clid , in b state )
        org.freedesktop.Gypsy.Server
            methods
                Create( in s device , out o )
                Shutdown( in o path )
    /org/freesmartphone/Device/Audio
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.Audio
            signals
                SoundStatus( s name , s status , a{sv} properties )
                Scenario( s scenario , s reason )
            methods
                SetScenario( in s name )
                GetInfo( out s )
                GetAvailableScenarios( out as )
                PushScenario( in s name )
                GetScenario( out s )
                PullScenario( out s )
                StopSound( in s name )
                StopAllSounds( )
                PlaySound( in s name )
                StoreScenario( in s name )
    /org/freesmartphone/Device/Display/pcf50633_bl
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.Display
            methods
                SetBrightness( in i brightness )
                GetName( out s )
                SetBacklightPower( in b power )
                GetBrightness( out i )
                GetBacklightPower( out b )
    /org/freesmartphone/Device/IdleNotifier/0
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.IdleNotifier
            signals
                State( s state )
            methods
                SetState( in s state )
                GetState( out s )
                SetTimeout( in s state , in i timeout )
                GetTimeouts( out a{si} )
    /org/freesmartphone/Device/Info
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.Info
            methods
                GetCpuInfo( out a{sv} )
    /org/freesmartphone/Device/Input
        org.freesmartphone.Device.Input
            signals
                Event( s name , s action , i seconds )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
    /org/freesmartphone/Device/LED/gta02_aux_red
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.LED
            methods
                SetBrightness( in i brightness )
                GetName( out s )
                SetBlinking( in i delay_on , in i delay_off )
    /org/freesmartphone/Device/LED/gta02_power_blue
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.LED
            methods
                SetBrightness( in i brightness )
                GetName( out s )
                SetBlinking( in i delay_on , in i delay_off )
    /org/freesmartphone/Device/LED/gta02_power_orange
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.LED
            methods
                SetBrightness( in i brightness )
                GetName( out s )
                SetBlinking( in i delay_on , in i delay_off )
    /org/freesmartphone/Device/LED/neo1973_vibrator
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.LED
            methods
                SetBrightness( in i brightness )
                GetName( out s )
                SetBlinking( in i delay_on , in i delay_off )
    /org/freesmartphone/Device/PowerControl/Bluetooth
        org.freesmartphone.Device.PowerControl
            signals
                Power( s device , b power )
            methods
                Reset( )
                GetName( out s )
                SetPower( in b power )
                GetPower( out b )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Resource
            methods
                Resume( )
                Enable( )
                Disable( )
                Suspend( )
    /org/freesmartphone/Device/PowerControl/UsbHost
        org.freesmartphone.Device.PowerControl
            signals
                Power( s device , b power )
            methods
                Reset( )
                GetName( out s )
                SetPower( in b power )
                GetPower( out b )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
    /org/freesmartphone/Device/PowerControl/WiFi
        org.freesmartphone.Device.PowerControl
            signals
                Power( s device , b power )
            methods
                Reset( )
                GetName( out s )
                SetPower( in b power )
                GetPower( out b )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Resource
            methods
                Resume( )
                Enable( )
                Disable( )
                Suspend( )
    /org/freesmartphone/Device/PowerSupply/apm
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.PowerSupply
            methods
                GetName( out s )
                GetEnergyPercentage( out i )
                GetOnBattery( out b )
                GetInfo( out a{sv} )
    /org/freesmartphone/Device/PowerSupply/bat
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.PowerSupply
            signals
                PowerStatus( s status )
                Capacity( i percent )
            methods
                GetEnergyPercentage( out i )
                GetInfo( out a{sv} )
                IsPresent( out b )
                GetName( out s )
                GetCapacity( out i )
                GetPowerStatus( out s )
    /org/freesmartphone/Device/RealTimeClock/rtc0
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.RealTimeClock
            methods
                GetWakeupReason( out s )
                SetCurrentTime( in s t )
                Suspend( )
                GetWakeupTime( out s )
                GetName( out s )
                GetCurrentTime( out s )
                SetWakeupTime( in s t )
    /org/freesmartphone/Events
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Events
            methods
                AddRule( in s rule_str )
                TriggerTest( in s name , in b value )
    /org/freesmartphone/Framework
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Framework
            methods
                GetDebugLevel( in s logger , out s )
                GetDebugDestination( out s , out s )
                ListDebugLoggers( out as )
                ListObjectsInSubsystem( in s subsystem , out as )
                SetDebugDestination( in s category , in s destination )
                SetDebugLevel( in s logger , in s levelname )
                ListObjectsByInterface( in s interface , out ao )
                ListSubsystems( out as )
    /org/freesmartphone/GSM/Device
        org.freesmartphone.GSM.Call
            signals
                CallStatus( i index , s status , a{sv} properties )
            methods
                Activate( in i index )
                Emergency( in s number )
                SendDtmf( in s tones )
                ReleaseHeld( )
                HoldActive( )
                ReleaseAll( )
                Initiate( in s number , in s type_ , out i )
                ListCalls( out a(isa{sv}) )
                Transfer( in s number )
                Release( in i index )
                ActivateConference( in i index )
        org.freesmartphone.GSM.Debug
            methods
                DebugInjectString( in s channel , in s string )
                DebugCommand( in s command , out as )
                DebugEcho( in s echo , out s )
                DebugListChannels( out as )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.GSM.Device
            methods
                CancelCommand( )
                GetInfo( out a{sv} )
                GetAntennaPower( out b )
                SetSimBuffersSms( in b sim_buffers_sms )
                GetFeatures( out a{sv} )
                SetAntennaPower( in b power )
                GetSimBuffersSms( out b )
        org.freesmartphone.GSM.SMS
            signals
                IncomingMessage( s address , s text , a{sv} features )
            methods
                SendMessage( in s number , in s contents , in a{sv} featuremap , out i )
        org.freesmartphone.GSM.SIM
            signals
                ReadyStatus( b status )
                MemoryFull( )
                AuthStatus( s status )
                IncomingStoredMessage( i index )
            methods
                RetrievePhonebook( in s category , out a(iss) )
                SendAuthCode( in s code )
                ChangeAuthCode( in s old_pin , in s new_pin )
                SendGenericSimCommand( in s command , out s )
                ListPhonebooks( out as )
                SetServiceCenterNumber( in s number )
                GetHomeZones( out a(siii) )
                RetrieveEntry( in s category , in i index , out s , out s )
                DeleteMessage( in i index )
                SendRestrictedSimCommand( in i command , in i fileid , in i p1 , in i p2 , in i p3 , in s data , out i , out i , out s )
                GetMessagebookInfo( out a{sv} )
                GetSimReady( out b )
                GetPhonebookInfo( in s category , out a{sv} )
                GetSimInfo( out a{sv} )
                SendStoredMessage( in i index , out i )
                SetAuthCodeRequired( in b required , in s pin )
                GetAuthStatus( out s )
                StoreMessage( in s number , in s contents , in a{sv} featuremap , out i )
                GetAuthCodeRequired( out b )
                RetrieveMessage( in i index , out s , out s , out s , out a{sv} )
                StoreEntry( in s category , in i index , in s name , in s number )
                Unlock( in s puk , in s new_pin )
                GetServiceCenterNumber( out s )
                RetrieveMessagebook( in s category , out a(isssa{sv}) )
                DeleteEntry( in s category , in i index )
        org.freesmartphone.GSM.Network
            signals
                Status( a{sv} status )
                SignalStrength( i strength )
                IncomingUssd( s mode , s message )
            methods
                EnableCallForwarding( in s reason , in s class_ , in s number , in i timeout )
                ListProviders( out a(isss) )
                GetCallForwarding( in s reason , out a{sv} )
                Unregister( )
                SetCallingIdentification( in s status )
                Register( )
                SendUssdRequest( in s request )
                DisableCallForwarding( in s reason , in s class_ )
                GetSignalStrength( out i )
                GetCallingIdentification( out s )
                RegisterWithProvider( in i operator_code )
                GetNetworkCountryCode( out s )
                GetStatus( out a{sv} )
        org.freesmartphone.Resource
            methods
                Enable( )
                Disable( )
                Suspend( )
                Resume( )
        org.freesmartphone.GSM.CB
            signals
                IncomingCellBroadcast( i channel , s data )
            methods
                GetCellBroadcastSubscriptions( out s )
                SetCellBroadcastSubscriptions( in s channels )
        org.freesmartphone.GSM.PDP
            signals
                ContextStatus( i index , s status , a{sv} properties )
            methods
                SetCurrentGprsClass( in s class_ )
                ActivateContext( in s apn , in s user , in s password )
                DeactivateContext( )
                ListAvailableGprsClasses( out as )
                GetContextStatus( out s )
                GetCurrentGprsClass( out s )
    /org/freesmartphone/GSM/Server
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.GSM.HZ
            signals
                HomeZoneStatus( s zone )
            methods
                GetHomeZoneStatus( out s )
                GetKnownHomeZones( out as )
    /org/freesmartphone/PIM/Contacts
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.PIM.Contacts
            methods
                Query( in a{sv} query , out s )
                Add( in a{sv} contact_data , out s )
                GetSingleContactSingleField( in a{sv} query , in s field_name , out s )
        org.freesmartphone.PIM.Contact
            methods
                GetContent( out a{sv} )
                GetMultipleFields( in s field_list , out a{sv} )
    /org/freesmartphone/PIM/Contacts/Queries
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.PIM.ContactQuery
            methods
                GetContactPath( out s )
                Skip( in i num_entries )
                Dispose( )
                GetResult( out a{sv} )
                GetResultCount( out i )
                Rewind( )
                GetMultipleResults( in i num_entries , out aa{sv} )
    /org/freesmartphone/PIM/Messages
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.PIM.Messages
            signals
                NewMessage( s message_URI )
            methods
                GetSingleMessageSingleField( in a{sv} query , in s field_name , out s )
                Query( in a{sv} query , out s )
                Add( in a{sv} message_data , out s )
                GetFolderURIFromName( in s folder_name , out s )
                GetFolderNames( out as )
        org.freesmartphone.PIM.Message
            methods
                GetContent( out a{sv} )
                MoveToFolder( in s new_folder_name )
                GetMultipleFields( in s field_list , out a{sv} )
    /org/freesmartphone/PIM/Messages/Folders
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.PIM.Messages
            signals
                NewMessage( s message_URI )
            methods
                GetSingleMessageSingleField( in a{sv} query , in s field_name , out s )
                Query( in a{sv} query , out s )
                Add( in a{sv} message_data , out s )
                GetFolderURIFromName( in s folder_name , out s )
                GetFolderNames( out as )
        org.freesmartphone.PIM.Message
            methods
                GetContent( out a{sv} )
                MoveToFolder( in s new_folder_name )
                GetMultipleFields( in s field_list , out a{sv} )
    /org/freesmartphone/PIM/Messages/Folders/0
        org.freesmartphone.PIM.MessageFolder
            signals
                MessageMoved( s message_uri , s new_folder_name )
            methods
                GetMessageCount( out i )
                GetMessageURIs( in i first_message_id , in i message_count , out as )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
    /org/freesmartphone/PIM/Messages/Folders/1
        org.freesmartphone.PIM.MessageFolder
            signals
                MessageMoved( s message_uri , s new_folder_name )
            methods
                GetMessageCount( out i )
                GetMessageURIs( in i first_message_id , in i message_count , out as )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
    /org/freesmartphone/PIM/Messages/Queries
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.PIM.MessageQuery
            methods
                Skip( in i num_entries )
                Dispose( )
                GetResult( out a{sv} )
                GetResultCount( out i )
                Rewind( )
                GetMultipleResults( in i num_entries , out a{ia{sv}} )
                GetMessageURI( out s )
    /org/freesmartphone/PIM/Sources
        org.freesmartphone.PIM.Sources
            methods
                GetEntryCount( out i )
                InitAllEntries( )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.PIM.Source
            methods
                GetSupportedPIMDomains( out as )
                GetName( out s )
                GetStatus( out s )
    /org/freesmartphone/Phone
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Phone
            signals
                Incoming( o call )
            methods
                InitProtocols( out as )
                CreateCall( in s number , in s protocol , in b force , out o )
    /org/freesmartphone/Preferences
        org.freesmartphone.Preferences
            methods
                GetProfiles( out as )
                GetService( in s name , out o )
                GetServices( out as )
                SetProfile( in s profile )
                GetProfile( out s )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
    /org/freesmartphone/Preferences/rules
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Preferences.Service
            signals
                Notify( s key , v value )
            methods
                GetType( in s key , out s )
                SetValue( in s key , in v value )
                GetKeys( out as )
                IsProfilable( in s key , out b )
                GetValue( in s key , out v )
    /org/freesmartphone/Time
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Time
            signals
                Minute( i year , i mon , i day , i hour , i min , i sec , i wday , i yday , i isdst )
            methods
                GetLocalTime( in i seconds , out i , out i , out i , out i , out i , out i , out i , out i , out i )
    /org/freesmartphone/Time/Alarm
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Time.Alarm
            methods
                ClearAlarm( in s busname )
                SetAlarm( in s busname , in i timestamp )
    /org/freesmartphone/Usage
        org.freesmartphone.Usage
            signals
                ResourceAvailable( s resourcename , b state )
                ResourceChanged( s resourcename , b state , a{sv} attributes )
            methods
                ReleaseResource( in s resourcename )
                Suspend( )
                GetResourceState( in s resourcename , out b )
                SetResourcePolicy( in s resourcename , in s policy )
                GetResourcePolicy( in s resourcename , out s )
                GetResourceUsers( in s resourcename , out as )
                ListResources( out as )
                RegisterResource( in s resourcename , in o path )
                RequestResource( in s resourcename )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )

2.二、源代码

下面是dteeth的源代码:

$ cat -n dteeth.py
     1  #!/usr/bin/env python
     2  # -*- coding: utf-8 -*-
     3
     4  import dbus
     5  import _introspect_parser
     6  import getopt, sys
     7
     8  MARGIN_WIDTH = 4
     9  ONE_MARGIN = ' ' * MARGIN_WIDTH
    10
    11  # signal是个元组,它有一个元素,是一个列表。列表的元素是signal的参数
    12  # 列表的每一个元素都是字典。它有两个元素,键值分别是'type'和'name'
    13  def show_signal(name, signal, margin):
    14      print margin+name+'(',
    15      args = signal[0]
    16      for i, arg in enumerate(args):
    17          if i > 0:
    18              print ',',
    19          if arg['name']:
    20              print '%s %s' % (arg['type'], arg['name']),
    21          else:
    22              print '%s' % arg['type'],
    23      print  ')'
    24
    25  # method是个元组,它有两个元素,都是列表。前一个列表的元素是输入参数,后一个列表的元素是输出参数
    26  def show_method(name, method, margin):
    27      print margin+name+'(',
    28  # 输入参数
    29      args = method[0]
    30      in_num = len(args)
    31      out_num = len(method[1])
    32      for i, arg in enumerate(args):
    33          if i > 0:
    34              print ',',
    35          if arg['name']:
    36              print 'in %s %s' % (arg['type'], arg['name']),
    37          else:
    38              print 'in %s' % arg['type'],
    39  # 输出参数
    40      if (in_num > 0) and (out_num > 0) :
    41          print ',',
    42      args = method[1]
    43      for i, arg in enumerate(args):
    44          if i > 0:
    45              print ',',
    46          if arg['name']:
    47              print 'out %s %s' % (arg['type'], arg['name']),
    48          else:
    49              print 'out %s' % arg['type'],
    50      print  ')'
    51
    52  def show_property(name, property, margin):
    53      print margin+name
    54      print margin,
    55      print property
    56
    57  # interfaces是个字典,它有三个元素,键值分别是'signals'、'methods'和'properties'
    58  def show_iface(name, iface, margin):
    59      print margin + name
    60      margin += ONE_MARGIN
    61      signals=iface['signals']
    62      l = len(signals)
    63      if l > 0:
    64          print margin+'signals'
    65          for node in signals:
    66              show_signal(node, signals[node], margin+ONE_MARGIN)
    67
    68      methods=iface['methods']
    69      l = len(methods)
    70      if l > 0:
    71          print margin+'methods'
    72          for node in methods:
    73              show_method(node, methods[node], margin+ONE_MARGIN)
    74
    75      properties=iface['properties']
    76      l = len(properties)
    77      if l > 0:
    78          print margin+'properties'
    79          for node in properties:
    80              show_property(node, properties[node], margin+ONE_MARGIN)
    81
    82  def show_obj(bus, name, obj_name, margin):
    83      obj=bus.get_object(name, obj_name)
    84      iface=dbus.Interface(obj, 'org.freedesktop.DBus.Introspectable')
    85      xml=iface.Introspect();
    86      data = _introspect_parser.process_introspection_data(xml)
    87
    88      # data是个字典,它有两个元素,键值分别是'child_nodes'和'interfaces'
    89      if len(data['interfaces']) > 0:
    90          print margin + obj_name
    91
    92      for node in data['interfaces']:
    93          iface=data['interfaces'][node]
    94          show_iface(node, iface, margin+ONE_MARGIN)
    95
    96      for node in data['child_nodes']:
    97          if obj_name == '/':
    98              show_obj(bus, name, '/' + node, margin)
    99          else:
   100              show_obj(bus, name, obj_name + '/' + node, margin)
   101
   102  def show_connection(bus, name, margin):
   103      print margin + name
   104      show_obj(bus, name, '/', margin+ONE_MARGIN)
   105
   106  def usage():
   107      print "Usage: dteeth [--system] "
   108
   109  def main():
   110      try:
   111          opts, args = getopt.getopt(sys.argv[1:], "h", ["help", "system"])
   112      except getopt.GetoptError, err:
   113          # print help information and exit:
   114          print str(err) # will print something like "option -a not recognized"
   115          usage()
   116          sys.exit(2)
   117
   118      if len(args) == 0:
   119          usage()
   120          sys.exit(2)
   121
   122      use_system = False
   123      for o, a in opts:
   124          if o in ("-h", "--help"):
   125              usage()
   126              sys.exit()
   127          if o == "--system":
   128              use_system = True
   129          else:
   130              assert False, "unhandled option"
   131
   132      if use_system:
   133          bus=dbus.SystemBus()
   134      else:
   135          bus=dbus.SessionBus()
   136
   137      for arg in args:
   138          show_connection(bus, arg, "")
   139
   140  if __name__ == "__main__":
   141      main()

dteeth是我写的第一个超过10行的python脚本。对于熟悉python的读者,dteeth应该是很简单的。不过我仍是简单解释一下dteeth的主要逻辑。

2.三、dteeth的主要逻辑

main函数分析命令行,对命令行上指定的每一个链接调用show_connection函数。 show_connection在打印链接名后调用show_obj从根对象"/"开始遍历链接的对象树。

show_obj对输入对象调用Introspect方法,返回的xml数据交由_introspect_parser处理。 _introspect_parser会从xml数据中分出inerface和node。 show_obj对inerface调用show_iface显示。 show_obj对node会递归调用show_obj,实现对象树的遍历。

2.四、_introspect_parser的输出格式

_introspect_parser.process_introspection_data函数分析Introspect方法返回的xml数据。为了了解_introspect_parser的输出格式,咱们能够写个小脚本:

$ cat ti.py
#!/usr/bin/env python
import dbus
import _introspect_parser
bus=dbus.SessionBus()
obj=bus.get_object('org.freesmartphone.ogsmd', '/org/freesmartphone/GSM/Device')
iface=dbus.Interface(obj, 'org.freedesktop.DBus.Introspectable')
xml=iface.Introspect();
data = _introspect_parser.process_introspection_data(xml)
print data

能够用这个脚本直接打印process_introspection_data返回的数据。下面是整理后的输出:

{
	'interfaces': 
	{
		u'org.freedesktop.DBus.Introspectable': 
		{
			'signals': {}, 
			'methods': 
			{
				u'Introspect': 
				(
					[], 
					[{'type': u's', 'name': u'data'}]
				)
			}, 
			'properties': {}
		}, 
		u'org.freedesktop.DBus.Properties': 
		{
			'signals': {}, 
			'methods': 
			{
				u'Set': 
				(
					[{'type': u's', 'name': u'interface'}, {'type': u's', 'name': u'propname'}, {'type': u'v', 'name': u'value'}], 
					[]
				), 
				u'GetAll': 
				(
					[{'type': u's', 'name': u'interface'}], 
					[{'type': u'a{sv}', 'name': u'props'}]
				), 
				u'Get': 
				(
					[{'type': u's', 'name': u'interface'}, {'type': u's', 'name': u'propname'}], 
					[{'type': u'v', 'name': u'value'}]
				)
			}, 
			'properties': {}
		}, 
		u'org.freesmartphone.GSM.SMS': 
		{
			'signals': 
			{
				u'IncomingMessage': 
				(
					[{'type': u's', 'name': None}, {'type': u's', 'name': None}, {'type': u'a{sv}', 'name': None}],
				)
			},
			'methods': 
			{
				u'SendMessage': 
				(
					[{'type': u's', 'name': u'number'}, {'type': u's', 'name': u'contents'}, {'type': u'a{sv}', 'name': u'featuremap'}], 
					[{'type': u'i', 'name': u'arg3'}]
				)
			},
			'properties': {}
		}
	}, 
	'child_nodes': []
}

全部字符串前面都有前缀u,表示这些字符串都是Unicode编码。在python中,字典用{},元组用(),列表用[]。从括号咱们就能看出数据格式。

咱们看到process_introspection_data返回返回一个字典。这个字典有两个映射。一个映射的键值是"interfaces",另外一个映射的键值是"child_nodes"。

  • 映射"child_nodes"的值是一个列表,列出全部子节点的名称。

  • 映射"interfaces"的值仍是一个字典。这个字典的每一个映射的键值是一个接口名称。每一个映射的值类型仍是字典, 这个字典有3个映射,映射的键值分别是'signals'、'methods'和'properties',映射的值类型都是字典。

    • 'signals'对应字典的每一个键值是一个信号名称。每一个映射的值类型是元组。这个元组只有一个元素,类型是列表, 即信号的参数列表。

      • 参数列表的元素类型是字典。这个字典有2个映射,映射的键值分别是'type'和'name'。'type'是参数类型,'name'是参数名称。 映射的值类型都是字符串。

    • 'methods'对应字典的每一个键值是一个方法名称。每一个映射的值类型是元组。这个元组有两个元素,类型是列表, 分别是方法的输入参数列表和输出参数列表。参数列表的元素类型和信号的参数列表相同。

    • 我看到'properties'映射都是空的,就没有研究。

三、python基础

简单介绍一下与dteeth有关的python语法。

3.一、代码块和缩进

python用缩进来区分语句所属的代码块,从类定义、函数到for、if的代码块都是用缩进来去区分的。没有缩进的代码块是脚本的主体代码。一个 脚本文件也被称做一个模块。无论模块被直接运行仍是被其它模块导入,主体代码都会在载入时被执行。例如dteeth的主体代码只有两句:

   140  if __name__ == "__main__":
   141      main()

__xxx__这样的标志符一般是python的系统变量。若是模块被导入,__name__的值是模块的名字。若是模块被直接执行,__name__的值是"__main__"。咱们一般在模块被直接执行时,调用主函数或模块的测试函数。

3.二、脚本文件格式

python脚本的起始行一般是:/p>

     1  #!/usr/bin/env python

env是一个能够修改环境变量并执行程序的工具,它能够自动在系统路径中搜索要执行的程序。 python脚本文件必须以0A为换行符,默认仅支持ASCII字符。若是要写中文注释(显然是不提倡的),能够在起始行后用如下语句将文件指定为utf-8编码:

     2  # -*- coding: utf-8 -*-

这时,文件必须被保存为没有BOM的utf-8编码文件。

3.三、列表、元组和字典

列表相似于C的数组,列表元素用[]包括。元组是不可变的列表,元组元素用()包括。元组的元素个数和类型在建立后就不能改变了。元组中基本类型的值是不能改变的,但若是元组的一个元素是列表,咱们能够改变列表内容,即咱们能够改变元组中可变元素的内容。例如:

>>> a=(1,2,['abc'])
>>> a[2]='def'
Traceback (most recent call last):
  File "", line 1, in 
TypeError: 'tuple' object does not support item assignment
>>> a[2][0]='def'
>>> a
(1, 2, ['def'])

字典是键-值对的集合,字典元素用{}包括。

四、结束语

本文介绍了一个叫做dteeth的python脚本。这个脚本逻辑很简单,读者能够根据须要修改或扩充。讲了这么多dbus,咱们尚未接触C代码。下一讲,咱们讨论dbus的C实例。

 

 

 

dbus-glib是dbus底层接口的一个封装。本讲咱们用dbus-glib作一个dus接口,并写一个客户程序。

一、接口

1.一、编写接口描述文件

首先编写接口描述文件。咱们要实现的链接的公共名是"org.freesmartphone.ogsmd",接口描述文件以下:

$ cat smss.xml
<?xml version="1.0" encoding="UTF-8" ?>

<node name="/org/freesmartphone/GSM/Device">
  <interface name="org.freesmartphone.GSM.SMS">
    <method name="SendMessage">
       <arg name="number" type="s"/>
       <arg name="contents" type="s"/>
       <arg name="featuremap" type="a{sv}"/>
       <arg type="i" direction="out"/>
    </method>
    <signal name="IncomingMessage">
       <arg name="address" type="s"/>
       <arg name="contents" type="s"/>
       <arg name="features" type="a{sv}"/>
    </signal>
  </interface>
</node>

咱们要在链接"org.freesmartphone.ogsmd"中实现对象"/org/freesmartphone/GSM /Device"。这个对象有接口"org.freesmartphone.GSM.SMS"。这个接口有一个SendMessage方法和一个 IncomingMessage信号。

SendMessage方法和IncomingMessage信号除了两个字符串参数外,还有一个a{sv}参数,这是一个哈希表,即python 的字典。键-值对的键类型是字符串,值类型是VARIANT。这个接口是openmoko fso接口的一部分。但为简单起见,本例在哈希表部分,只用三个键值。

  • 键"alphabet"对应的值类型是字符串。
  • 键"csm_num"对应的值类型是INT32。
  • 键"csm_seq"对应的值类型是INT32。

请注意方法和信号名应采用单词连写,首字母大写的格式。

1.二、由接口描述文件生成绑定文件

有一个叫dbus-binding-tool的工具,它读入接口描述文件,产生一个绑定文件。这个文件包含了dbus对象的接口信息。在主程序中我 们经过dbus_g_object_type_install_info函数向dbus-glib登记对象信息(DBusGObjectInfo结构)。

本例使用了autotool,在Makefile.am中能够这样调用dbus-binding-tool:

smss-glue.h: smss.xml
	$(LIBTOOL) --mode=execute dbus-binding-tool --prefix=gsm_sms --mode=glib-server --output=smss-glue.h $(srcdir)/smss.xml

"--prefix"参数定义了对象前缀。设对象前缀是$(prefix),则生成的DBusGObjectInfo结构变量名就是 dbus_glib_$(prefix)_object_info。绑定文件会为接口方法定义回调函数。回调函数的名称是这样的:首先将xml中的方法名 称转换到所有小写,下划线分隔的格式,而后增长前缀"$(prefix)_"。例如:若是xml中有方法SendMessage,绑定文件就会引用一个名 称为$(prefix)_send_message的函数。

绑定文件还会为接口方法生成用于散集(Unmarshaling)的函数。在dbus消息中,方法参数是以流格式存在的。该函数将方法参数由数据流 还原到glib的数据格式,并传入方法的回调函数。本例中,dbus-binding-tool生成如下的smss-glue.h:

$ cat smss-glue.h
/* Generated by dbus-binding-tool; do not edit! */


#ifndef __dbus_glib_marshal_gsm_sms_MARSHAL_H__
#define __dbus_glib_marshal_gsm_sms_MARSHAL_H__

#include        <glib-object.h>

G_BEGIN_DECLS

#ifdef G_ENABLE_DEBUG
#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
#define g_marshal_value_peek_char(v)     g_value_get_char (v)
#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
#define g_marshal_value_peek_int(v)      g_value_get_int (v)
#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
#define g_marshal_value_peek_long(v)     g_value_get_long (v)
#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
#define g_marshal_value_peek_float(v)    g_value_get_float (v)
#define g_marshal_value_peek_double(v)   g_value_get_double (v)
#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
#define g_marshal_value_peek_param(v)    g_value_get_param (v)
#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
#define g_marshal_value_peek_object(v)   g_value_get_object (v)
#else /* !G_ENABLE_DEBUG */
/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
 *          Do not access GValues directly in your code. Instead, use the
 *          g_value_get_*() functions
 */
#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
#endif /* !G_ENABLE_DEBUG */


/* BOOLEAN:STRING,STRING,BOXED,POINTER,POINTER (/tmp/dbus-binding-tool-c-marshallers.3YAGNU:1) */
extern void dbus_glib_marshal_gsm_sms_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER (GClosure     *closure,
                                                                                    GValue       *return_value,
                                                                                    guint         n_param_values,
                                                                                    const GValue *param_values,
                                                                                    gpointer      invocation_hint,
                                                                                    gpointer      marshal_data);
void
dbus_glib_marshal_gsm_sms_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER (GClosure     *closure,
                                                                        GValue       *return_value,
                                                                        guint         n_param_values,
                                                                        const GValue *param_values,
                                                                        gpointer      invocation_hint,
                                                                        gpointer      marshal_data)
{
  typedef gboolean (*GMarshalFunc_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER) (gpointer     data1,
                                                                                 gpointer     arg_1,
                                                                                 gpointer     arg_2,
                                                                                 gpointer     arg_3,
                                                                                 gpointer     arg_4,
                                                                                 gpointer     arg_5,
                                                                                 gpointer     data2);
  register GMarshalFunc_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER callback;
  register GCClosure *cc = (GCClosure*) closure;
  register gpointer data1, data2;
  gboolean v_return;

  g_return_if_fail (return_value != NULL);
  g_return_if_fail (n_param_values == 6);

  if (G_CCLOSURE_SWAP_DATA (closure))
    {
      data1 = closure->data;
      data2 = g_value_peek_pointer (param_values + 0);
    }
  else
    {
      data1 = g_value_peek_pointer (param_values + 0);
      data2 = closure->data;
    }
  callback = (GMarshalFunc_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);

  v_return = callback (data1,
                       g_marshal_value_peek_string (param_values + 1),
                       g_marshal_value_peek_string (param_values + 2),
                       g_marshal_value_peek_boxed (param_values + 3),
                       g_marshal_value_peek_pointer (param_values + 4),
                       g_marshal_value_peek_pointer (param_values + 5),
                       data2);

  g_value_set_boolean (return_value, v_return);
}

G_END_DECLS

#endif /* __dbus_glib_marshal_gsm_sms_MARSHAL_H__ */

#include 
static const DBusGMethodInfo dbus_glib_gsm_sms_methods[] = {
  { (GCallback) gsm_sms_send_message, dbus_glib_marshal_gsm_sms_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER, 0 },
};

const DBusGObjectInfo dbus_glib_gsm_sms_object_info = {
  0,
  dbus_glib_gsm_sms_methods,
  1,
"org.freesmartphone.GSM.SMS/0SendMessage/0S/0number/0I/0s/0contents/0I/0s/0featuremap/0I/0a{sv}/0arg3/0O/0F/0N/0i/0/0/0",
"org.freesmartphone.GSM.SMS/0IncomingMessage/0/0",
"/0"
};

在包含绑定文件前,咱们必须声明绑定文件要引用的回调函数。

2 对象

2.1 对象定义

dbus-glib用GObject实现dbus对象。因此咱们首先要实现一个对象。在本例中,咱们实现一个GsmSms对象,它继承了GObject:

$ cat gsm_sms.h
#ifndef GSM_SMS_H
#define GSM_SMS_H

typedef struct GsmSms GsmSms;
typedef struct GsmSmsClass GsmSmsClass;

struct GsmSms
{
  GObject parent;
};

struct GsmSmsClass
{
  GObjectClass parent;
};

#define GSM_SMS_TYPE                  (gsm_sms_get_type ())

GType gsm_sms_get_type (void);
gboolean gsm_sms_send_message (GsmSms *obj, const char *number, const char *contents, GHashTable *featuremap, int *ret, GError **error);
void gsm_sms_emit_incoming_message(GsmSms *obj, const char * address, const char * contents, GHashTable *hash);

#endif

GObject的对象定义虽然繁琐,但有固定的套路。依样画葫芦,画多了就习惯了。咱们在gsm_sms.h中声明了 gsm_sms_send_message函数。 gsm_sms_send_message函数是在gsm_sms.c中实现的,在绑定文件smss-glue.h中用到。由于主程序要使用绑定文件中的 对象信息,因此应由主程序包含绑定文件。主程序只要在包含绑定文件前包含gsm_sms.h,编译器就不会抱怨gsm_sms_send_message 函数未声明。

2.2 信号的列集函数

列集(Marshaling)是将数据从某种格式存为流格式的操做;散集(Unmarshaling)则是列集的反操做,将信息从流格式中还原出 来。在绑定文件中,dbus-binding-tool自动生成函数将方法参数从dbus消息中还原出来,即实现了散集。那么咱们怎么把信号参数由 glib的数据结构转换到消息中的数据流呢?

由于GsmSms对象有一个信号,因此在对象类初始化函数gsm_sms_class_init中,咱们要调用g_signal_new建立信号。 g_signal_new要求咱们提供一个列集函数。

glib有一些标准的列集函数,在gmarshal.h中定义。例如g_cclosure_marshal_VOID__STRING,这个函数适 合只有一个字符串参数的信号。若是gmarshal.h没有提供适合的列集函数,咱们能够用一个叫glib-genmarshal的工具自动生成列集函 数。后面咱们会看到,不管是标准列集函数仍是生成的列集函数都是既能够用于列集也能够用于散集,这些函数一般都被称做列集函数。

使用glib-genmarshal前,咱们一样要准备一个输入文件:

$ cat sms-marshal.list
# see glib-genmarshal(1) for a detailed description of the file format,
# possible parameter types are:
#   VOID        indicates   no   return   type,  or  no  extra
#               parameters. if VOID is used as  the  parameter
#               list, no additional parameters may be present.
#   BOOLEAN     for boolean types (gboolean)
#   CHAR        for signed char types (gchar)
#   UCHAR       for unsigned char types (guchar)
#   INT         for signed integer types (gint)
#   UINT        for unsigned integer types (guint)
#   LONG        for signed long integer types (glong)
#   ULONG       for unsigned long integer types (gulong)
#   ENUM        for enumeration types (gint)
#   FLAGS       for flag enumeration types (guint)
#   FLOAT       for single-precision float types (gfloat)
#   DOUBLE      for double-precision float types (gdouble)
#   STRING      for string types (gchar*)
#   PARAM       for GParamSpec or derived types  (GParamSpec*)
#   BOXED       for boxed (anonymous but reference counted) types (GBoxed*)
#   POINTER     for anonymous pointer types (gpointer)
#   OBJECT      for GObject or derived types (GObject*)
#   NONE        deprecated alias for VOID
#   BOOL        deprecated alias for BOOLEAN
VOID:STRING,STRING,BOXED

咱们须要的函数返回类型是VOID,参数是STRING,STRING,BOXED。在Makefile.am中能够这样调用glib-genmarshal:

sms-marshal.h: sms-marshal.list
	$(LIBTOOL) --mode=execute glib-genmarshal --header sms-marshal.list --prefix=sms_marshal > sms-marshal.h

sms-marshal.c: sms-marshal.list
	$(LIBTOOL) --mode=execute glib-genmarshal --body sms-marshal.list --prefix=sms_marshal > sms-marshal.c

"--prefix"和函数原型决定输出函数名。若是"--prefix=sms_marshal",函数原型 是"OID:STRING,STRING,BOXED",生成的列集函数名就必然是 sms_marshal_VOID__STRING_STRING_BOXED。

在本例中自动生成的文件内容以下:

$ cat sms-marshal.h

#ifndef __sms_marshal_MARSHAL_H__
#define __sms_marshal_MARSHAL_H__

#include        <glib-object.h>

G_BEGIN_DECLS

/* VOID:STRING,STRING,BOXED (sms-marshal.list:24) */
extern void sms_marshal_VOID__STRING_STRING_BOXED (GClosure     *closure,
                                                   GValue       *return_value,
                                                   guint         n_param_values,
                                                   const GValue *param_values,
                                                   gpointer      invocation_hint,
                                                   gpointer      marshal_data);

G_END_DECLS

#endif /* __sms_marshal_MARSHAL_H__ */

$ cat sms-marshal.c

#include        <glib-object.h>


#ifdef G_ENABLE_DEBUG
#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
#define g_marshal_value_peek_char(v)     g_value_get_char (v)
#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
#define g_marshal_value_peek_int(v)      g_value_get_int (v)
#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
#define g_marshal_value_peek_long(v)     g_value_get_long (v)
#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
#define g_marshal_value_peek_float(v)    g_value_get_float (v)
#define g_marshal_value_peek_double(v)   g_value_get_double (v)
#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
#define g_marshal_value_peek_param(v)    g_value_get_param (v)
#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
#define g_marshal_value_peek_object(v)   g_value_get_object (v)
#else /* !G_ENABLE_DEBUG */
/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
 *          Do not access GValues directly in your code. Instead, use the
 *          g_value_get_*() functions
 */
#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
#endif /* !G_ENABLE_DEBUG */


/* VOID:STRING,STRING,BOXED (sms-marshal.list:24) */
void
sms_marshal_VOID__STRING_STRING_BOXED (GClosure     *closure,
                                       GValue       *return_value,
                                       guint         n_param_values,
                                       const GValue *param_values,
                                       gpointer      invocation_hint,
                                       gpointer      marshal_data)
{
  typedef void (*GMarshalFunc_VOID__STRING_STRING_BOXED) (gpointer     data1,
                                                          gpointer     arg_1,
                                                          gpointer     arg_2,
                                                          gpointer     arg_3,
                                                          gpointer     data2);
  register GMarshalFunc_VOID__STRING_STRING_BOXED callback;
  register GCClosure *cc = (GCClosure*) closure;
  register gpointer data1, data2;

  g_return_if_fail (n_param_values == 4);

  if (G_CCLOSURE_SWAP_DATA (closure))
    {
      data1 = closure->data;
      data2 = g_value_peek_pointer (param_values + 0);
    }
  else
    {
      data1 = g_value_peek_pointer (param_values + 0);
      data2 = closure->data;
    }
  callback = (GMarshalFunc_VOID__STRING_STRING_BOXED) (marshal_data ? marshal_data : cc->callback);

  callback (data1,
            g_marshal_value_peek_string (param_values + 1),
            g_marshal_value_peek_string (param_values + 2),
            g_marshal_value_peek_boxed (param_values + 3),
            data2);
}

2.3 对象实现

准备好列集函数后,咱们来实现GsmSms。

$ cat -n gsm_sms.c
     1  #include <dbus/dbus-glib.h>
     2  #include <stdio.h>
     3  #include <stdlib.h>
     4  #include <string.h>
     5  #include "gsm_sms.h"
     6  #include "sms-marshal.h"
     7  #include "sms_features.h"
     8
     9  enum
    10  {
    11      INCOMING_MESSAGE,
    12      LAST_SIGNAL
    13  };
    14
    15  static guint signals[LAST_SIGNAL];
    16
    17  G_DEFINE_TYPE(GsmSms, gsm_sms, G_TYPE_OBJECT)
    18
    19  static void gsm_sms_init (GsmSms *obj)
    20  {
    21  }
    22
    23  static void gsm_sms_class_init (GsmSmsClass *klass)
    24  {
    25      signals[INCOMING_MESSAGE] = g_signal_new (
    26          "incoming_message",
    27          G_OBJECT_CLASS_TYPE (klass),
    28          G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
    29          0,
    30          NULL, NULL,
    31          sms_marshal_VOID__STRING_STRING_BOXED,
    32          G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING,
    33          sms_get_features_type());
    34  }
    35
    36  gboolean gsm_sms_send_message (GsmSms *obj, const char *number, const char *contents,
    37      GHashTable *featuremap, int *ret, GError **error)
    38  {
    39      printf("number=%s/n", number);
    40      printf("contents=%s/n", contents);
    41      sms_show_features(featuremap);
    42      *ret = strlen(contents);
    43      return TRUE;
    44  }
    45
    46  void gsm_sms_emit_incoming_message(GsmSms *obj, const char * address,
    47      const char * contents, GHashTable *hash)
    48  {
    49      g_signal_emit (obj, signals[INCOMING_MESSAGE], 0, address, contents, hash);
    50  }

在类初始化函数gsm_sms_class_init中,咱们调用g_signal_new建立了信号。g_signal_new函数的原型是:

guint g_signal_new (const gchar *signal_name,
    GType itype,
    GSignalFlags signal_flags,
    guint class_offset,
    GSignalAccumulator accumulator,
    gpointer accu_data,
    GSignalCMarshaller c_marshaller,
    GType return_type,
    guint n_params,
    ...);

31行提供了列集函数。32-33行是返回值类型和参数类型。其中第三个参数调用了函数sms_get_features_type(在 sms_features.h中声明)。由于a{sv}类型的参数处理起来比较繁琐,我专门写了一个sms_features模块处理,后面会介绍。

在主程序中登记对象信息时,对象信息把SendMessage方法和gsm_sms_send_message函数以及自动生成的散集函数联系起 来。当客户程序调用SendMessage方法时,dbus-glib会经过对象信息表格找到回调函数和散集函数,用散集函数从method_call消 息中取出参数传入回调函数gsm_sms_send_message。 gsm_sms_send_message调用sms_show_features函数处理a{sv}参数。 sms_show_features也在sms_features模块定义,后面会介绍。

gsm_sms模块提供了一个gsm_sms_emit_incoming_message函数供外部模块调用。调用这个函数能够发射一个信号。在真实环境中,只有外部事件发生后才会发射信号。本例中会有测试代码发射信号。

3 主程序

3.1 登记dbus服务器

下面就是主程序

$ cat -n smss.c
     1  #include <dbus/dbus-glib.h>
     2  #include <stdio.h>
     3  #include <stdlib.h>
     4  #include <glib/giochannel.h>
     5  #include "gsm_sms.h"
     6  #include "smss-glue.h"
     7  #include "sms_features.h"
     8
     9  #define SMSS_DEBUG
    10
    11  static void lose (const char *str, ...)
    12  {
    13      va_list args;
    14      va_start (args, str);
    15      vfprintf (stderr, str, args);
    16      fputc ('/n', stderr);
    17      va_end (args);
    18      exit (1);
    19  }
    20
    21  static void lose_gerror (const char *prefix, GError *error)
    22  {
    23      if (error) {
    24          lose ("%s: %s", prefix, error->message);
    25      }
    26      else {
    27          lose ("%s", prefix);
    28      }
    29  }
    30
    31  static void shell_help(void)
    32  {
    33      printf( "/ts/tsend signal/n"
    34          "/tq/tQuit/n"
    35          );
    36  }
    37
    38  void emit_signal(GsmSms *obj)
    39  {
    40      GHashTable *features = sms_create_features("ucs2", 3, 1);
    41      gsm_sms_emit_incoming_message(obj, "12345678901", "hello signal!", features);
    42      sms_release_features(features);
    43  }
    44
    45  #define STDIN_BUF_SIZE    1024
    46  static gboolean channel_cb(GIOChannel *source, GIOCondition condition, gpointer data)
    47  {
    48      int rc;
    49      char buf[STDIN_BUF_SIZE+1];
    50      GsmSms *obj = (GsmSms *)data;
    51
    52      if (condition != G_IO_IN) {
    53          return TRUE;
    54      }
    55
    56      /* we've received something on stdin.    */
    57      printf("# ");
    58      rc = fscanf(stdin, "%s", buf);
    59      if (rc <= 0) {
    60          printf("NULL/n");
    61          return TRUE;
    62      }
    63
    64      if (!strcmp(buf, "h")) {
    65          shell_help();
    66      } else if (!strcmp(buf, "?")) {
    67          shell_help();
    68      } else if (!strcmp(buf, "s")) {
    69          emit_signal(obj);
    70      } else if (!strcmp(buf, "q")) {
    71          exit(0);
    72      } else {
    73          printf("Unknown command `%s'/n", buf);
    74      }
    75      return TRUE;
    76  }
    77
    78  int main (int argc, char **argv)
    79  {
    80      DBusGConnection *bus;
    81      DBusGProxy *bus_proxy;
    82      GError *error = NULL;
    83      GsmSms *obj;
    84      GMainLoop *mainloop;
    85      guint request_name_result;
    86      GIOChannel *chan;
    87
    88  #ifdef SMSS_DEBUG
    89      g_slice_set_config(G_SLICE_CONFIG_ALWAYS_MALLOC, TRUE);
    90  #endif
    91      g_type_init ();
    92
    93      dbus_g_object_type_install_info (GSM_SMS_TYPE, &dbus_glib_gsm_sms_object_info);
    94
    95      mainloop = g_main_loop_new (NULL, FALSE);
    96
    97      bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
    98      if (!bus)
    99          lose_gerror ("Couldn't connect to system bus", error);
   100
   101      bus_proxy = dbus_g_proxy_new_for_name (bus, "org.freedesktop.DBus",
   102          "/", "org.freedesktop.DBus");
   103
   104      if (!dbus_g_proxy_call (bus_proxy, "RequestName", &error,
   105          G_TYPE_STRING, "org.freesmartphone.ogsmd",
   106          G_TYPE_UINT, 0,
   107          G_TYPE_INVALID,
   108          G_TYPE_UINT, &request_name_result,
   109          G_TYPE_INVALID))
   110          lose_gerror ("Failed to acquire org.freesmartphone.ogsmd", error);
   111
   112      obj = g_object_new (GSM_SMS_TYPE, NULL);
   113      dbus_g_connection_register_g_object (bus, "/org/freesmartphone/GSM/Device", G_OBJECT (obj));
   114
   115      printf ("service is running/n");
   116      chan = g_io_channel_unix_new(0);
   117      g_io_add_watch(chan, G_IO_IN, channel_cb, obj);
   118      g_main_loop_run (mainloop);
   119
   120      exit (0);
   121  }

93行调用dbus_g_object_type_install_info登记GsmSms类的接口信息。97行链接会话总线。 101-102行在会话总线上为链接"org.freedesktop.DBus"的"/"对象的接口"org.freedesktop.DBus"创建 代理。 104-109行经过接口代理调用"RequestName"方法,请求公共名"org.freesmartphone.ogsmd"。

请求公共名成功后,112行创建GsmSms对象。113行登记GsmSms对象,登记时指定对象路径"/org/freesmartphone/GSM/Device",并传入对象指针。118行进入主循环等待客户消息。

3.2 IO Channel

我想增长一个敲键测试信号发射。但我又必须在glib主循环里等待dbus消息。怎样才能既等待dbus消息,又等待敲键呢?这种状况可使用 glib的IO Channels。glib的IO Channels容许咱们在glib主循环等待指定的文件或socket句柄。

要使用IO Channels,首先包含"glib/giochannel.h"。116行用句柄0(即标准输入)建立一个GIOChannel。 117行为咱们建立的GIOChannel登记回调函数。咱们在回调函数channel_cb中处理敲键,发射信号。

3.3 编译运行

读者能够从这里下载完整的示例程序。下集会介绍本例的autotool工程。目前,咱们先编译运行一下,解压后执行:

$ ./configure
$ make
$ cd src
$ ./smss
service is running
h
#       s       send signal
        q       Quit

键入h后回车,能够看到敲键的帮助信息。

我想找个客户程序测试一下,dbus-send不能发a{sv}这样复杂的参数。咱们能够用d-feet测试,或者写个python脚本:

$ cat ./smsc.py 
#!/usr/bin/env python
import dbus
bus=dbus.SessionBus()
bus_obj=bus.get_object('org.freesmartphone.ogsmd', '/org/freesmartphone/GSM/Device')
iface=dbus.Interface(bus_obj, 'org.freesmartphone.GSM.SMS')
ret=iface.SendMessage('1234567890', 'hello from python',
{'alphabet':'gsm','csm_num':8,'csm_seq':2})
print "SendMessage return %d" % (ret)

执行smsc.py,在服务器端看到:

$ ./smss
service is running
h
#       s       send signal
        q       Quit
number=1234567890
contents=hello from python
csm_num=8
alphabet=gsm
csm_seq=2

说明服务器能够正常工做。主程序的89行要求glib直接用malloc分配内存,这样用valgrind才能检查到内存泄漏(若是有的话)。咱们能够这样运行smss以检查是否有内存泄漏:

$ valgrind --tool=memcheck --leak-check=full ./smss

四、复杂的数据类型

在dbus中怎样处理复杂的数据类型?第一个建议是尽可能不要使用复杂的数据类型。但若是确实须要呢?有的网友建议用GArray做为容器,无论什么参数,在客户端都手工放入GArray,在服务器端再本身取出来。这确实是个思路,比较适合服务器和客户端都是本身开发的状况。还有一篇"How to pass a variant with dbus-glib" 介绍了怎样用GValue传递复杂的数据类型,读者能够参考。

下面看看在咱们的例子中是怎样处理a{sv}参数的:

$ cat sms_features.h
#ifndef SMS_FEATURES_H
#define SMS_FEATURES_H

#include <glib-object.h>

GHashTable *sms_create_features(const char * alphabet, int csm_num, int csm_seq);

GType sms_get_features_type(void);

void sms_release_features(GHashTable *features);

void sms_show_features(GHashTable *features);

#endif

sms_features.h声明了几个函数。这个例子的服务器、客户端都会调用。如下是这些函数的实现:

$ cat -n sms_features.c
     1  #include "sms_features.h"
     2
     3  static void release_val(gpointer data)
     4  {
     5      GValue *val = (GValue *)data;
     6      g_value_unset(val);
     7      g_free(val);
     8  }
     9
    10  GHashTable *sms_create_features(const char * alphabet, int csm_num, int csm_seq)
    11  {
    12      GHashTable *hash;
    13      GValue *val;
    14
    15      hash = g_hash_table_new_full  (g_str_hash, NULL, NULL, release_val);
    16
    17      val = g_new0(GValue, 1);
    18      g_value_init (val, G_TYPE_STRING);
    19      g_value_set_string (val, alphabet);
    20      g_hash_table_insert(hash, "alphabet", val);
    21
    22      val = g_new0(GValue, 1);
    23      g_value_init (val, G_TYPE_INT);
    24      g_value_set_int (val, csm_num);
    25      g_hash_table_insert(hash, "csm_num", val);
    26
    27      val = g_new0(GValue, 1);
    28      g_value_init (val, G_TYPE_INT);
    29      g_value_set_int (val, csm_seq);
    30      g_hash_table_insert(hash, "csm_seq", val);
    31
    32      return hash;
    33  }
    34
    35  GType sms_get_features_type(void)
    36  {
    37      return dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE);
    38  }
    39
    40  void sms_show_features(GHashTable *features)
    41  {
    42      GList *keys = g_hash_table_get_keys(features);
    43      gint len = g_list_length(keys);
    44      gint i;
    45
    46      for (i = 0; i < len; i++) {
    47          gchar  *key = g_list_nth_data(keys, i);
    48          GValue *val = g_hash_table_lookup(features, key);
    49
    50          g_print("%s=", key);
    51          switch (G_VALUE_TYPE(val)) {
    52          case G_TYPE_STRING:
    53              g_print("%s/n", g_value_get_string(val));
    54              break;
    55          case G_TYPE_INT:
    56              g_print("%d/n", g_value_get_int(val));
    57              break;
    58          default:
    59              g_print("Value is of unmanaged type!/n");
    60          }
    61      }
    62
    63      g_list_free(keys);
    64  }
    65
    66  void sms_release_features(GHashTable *features)
    67  {
    68      g_hash_table_destroy(features);
    69  }
    70

sms_get_features_type调用dbus_g_type_get_map建立a{sv}类型。服务器在建立信号时用到。客户端在调用方法和注册信号时都会用到。 sms_create_features调用g_hash_table_new_full建立哈希表,在建立的同时登记了值对象的清理函数。在sms_release_features调用g_hash_table_destroy销毁哈希表时,建立时登记的值对象清理函数会被调用。

五、客户端

5.一、代码

客户端程序以下:

$ cat -n smsc.c
     1  #include <dbus/dbus-glib.h>
     2  #include <stdio.h>
     3  #include <stdlib.h>
     4  #include <string.h>
     5  #include <glib/giochannel.h>
     6  #include "sms-marshal.h"
     7  #include "sms_features.h"
     8
     9  #define SMSC_DEBUG
    10
    11  static void lose (const char *str, ...)
    12  {
    13      va_list args;
    14      va_start (args, str);
    15      vfprintf (stderr, str, args);
    16      fputc ('/n', stderr);
    17      va_end (args);
    18      exit (1);
    19  }
    20
    21  static void lose_gerror (const char *prefix, GError *error)
    22  {
    23      if (error) {
    24          lose ("%s: %s", prefix, error->message);
    25      }
    26      else {
    27          lose ("%s", prefix);
    28      }
    29  }
    30
    31  static void incoming_message_handler (DBusGProxy *proxy, const char *address, const char *contents, GHashTable *features, gpointer user_data)
    32  {
    33      printf ("Received message with addree /"%s/" and it says: /n%s/n", address, contents);
    34      sms_show_features(features);
    35  }
    36
    37  static void send_message(DBusGProxy *remote_object)
    38  {
    39      GError *error = NULL;
    40      GHashTable *features;
    41      int ret;
    42
    43      features = sms_create_features ("gsm", 8, 2);
    44      printf("SendMessage ");
    45
    46      if (!dbus_g_proxy_call (remote_object, "SendMessage", &error,
    47          G_TYPE_STRING, "10987654321", G_TYPE_STRING, "hello world",
    48          sms_get_features_type(), features, G_TYPE_INVALID,
    49          G_TYPE_INT, &ret, G_TYPE_INVALID))
    50          lose_gerror ("Failed to complete SendMessage", error);
    51
    52      printf("return %d/n", ret);
    53      sms_release_features(features);
    54  }
    55
    56  static void shell_help(void)
    57  {
    58      printf( "/ts/tsend message/n"
    59          "/tq/tQuit/n"
    60          );
    61  }
    62
    63  #define STDIN_BUF_SIZE    1024
    64  static gboolean channel_cb(GIOChannel *source, GIOCondition condition, gpointer data)
    65  {
    66      int rc;
    67      char buf[STDIN_BUF_SIZE+1];
    68      DBusGProxy *remote_object = (DBusGProxy *)data;
    69
    70      if (condition != G_IO_IN) {
    71          return TRUE;
    72      }
    73
    74      /* we've received something on stdin.    */
    75      printf("# ");
    76      rc = fscanf(stdin, "%s", buf);
    77      if (rc <= 0) {
    78          printf("NULL/n");
    79          return TRUE;
    80      }
    81
    82      if (!strcmp(buf, "h")) {
    83          shell_help();
    84      } else if (!strcmp(buf, "?")) {
    85          shell_help();
    86      } else if (!strcmp(buf, "s")) {
    87          send_message(remote_object);
    88      } else if (!strcmp(buf, "q")) {
    89          exit(0);
    90      } else {
    91          printf("Unknown command `%s'/n", buf);
    92      }
    93      return TRUE;
    94  }
    95
    96  int main (int argc, char **argv)
    97  {
    98      DBusGConnection *bus;
    99      DBusGProxy *remote_object;
   100      GError *error = NULL;
   101      GMainLoop *mainloop;
   102      GIOChannel *chan;
   103      guint source;
   104      GType features_type;
   105
   106  #ifdef SMSC_DEBUG
   107      g_slice_set_config(G_SLICE_CONFIG_ALWAYS_MALLOC, TRUE);
   108  #endif
   109      g_type_init ();
   110      mainloop = g_main_loop_new (NULL, FALSE);
   111
   112      bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
   113      if (!bus)
   114          lose_gerror ("Couldn't connect to session bus", error);
   115
   116      remote_object = dbus_g_proxy_new_for_name (bus, "org.freesmartphone.ogsmd",
   117          "/org/freesmartphone/GSM/Device",
   118          "org.freesmartphone.GSM.SMS");
   119      if (!remote_object)
   120          lose_gerror ("Failed to get name owner", NULL);
   121
   122      features_type = sms_get_features_type();
   123      dbus_g_object_register_marshaller (sms_marshal_VOID__STRING_STRING_BOXED, G_TYPE_NONE, G_TYPE_STRING, G_TYPE_STRING,
   124          features_type, G_TYPE_INVALID);
   125      dbus_g_proxy_add_signal (remote_object, "IncomingMessage", G_TYPE_STRING, G_TYPE_STRING, features_type, G_TYPE_INVALID);
   126      dbus_g_proxy_connect_signal (remote_object, "IncomingMessage", G_CALLBACK (incoming_message_handler), NULL, NULL);
   127
   128      chan = g_io_channel_unix_new(0);
   129      source = g_io_add_watch(chan, G_IO_IN, channel_cb, remote_object);
   130      g_main_loop_run (mainloop);
   131      exit (0);
   132  }

112行链接会话总线。116-118行在会话总线上获取链接"org.freesmartphone.ogsmd"的对象"/org/freesmartphone/GSM/Device" 的接口"org.freesmartphone.GSM.SMS"的接口代理对象。

123行调用dbus_g_object_register_marshaller向dbus-glib登记列集函数。 125行调用dbus_g_proxy_add_signal增长对信号IncomingMessage的监听。126行登记信号IncomingMessage的回调函数。 123行登记的仍是咱们用glib-genmarshal生成的函数sms_marshal_VOID__STRING_STRING_BOXED。 dbus-glib使用这个函数从signal消息中取出信号参数,传递给回调函数,即执行散集操做。这说明glib-genmarshal生成的列集函数既能够用于列集,也能够用于散集。

客户端程序一样用IO Channel接受用户输入。129行在登记回调函数时将指向接口代理对象的指针做为参数传入。回调函数channel_cb在用户键入's'命令后经过send_message函数调用org.freesmartphone.GSM.SMS接口对象的SendMessage方法。 107行的设置G_SLICE_CONFIG_ALWAYS_MALLOC一样是为了用valgrind检查内存泄漏。

5.二、执行

咱们先运行 dbus-monitor,而后运行smss,再运行smsc。先在smsc中键入's'回车调用SendMessage方法。而后在smss中键入's'回车发送IncomingMessage信号。而后在smsc中键入'q'回车退出。最后在smss中键入'q'回车退出。

$ ./smss
service is running
number=10987654321
contents=hello world
csm_num=8
alphabet=gsm
csm_seq=2
h
#       s       send signal
        q       Quit
s
# q
$ ./smsc
h
#       s       send message
        q       Quit
s
# SendMessage return 11
Received message with addree "12345678901" and it says: 
hello signal!
csm_num=3
alphabet=ucs2
csm_seq=1
q

咱们能够看到打印出来的信号和消息。对于同一件事情,不一样的层次的观察者会看到不一样的细节,下表是dbus-monitor看到的东西:

 

smss链接会话总线。会话总线发NameOwnerChanged信号,通知惟一名":1.21"被分配。 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.21"
   string ""
   string ":1.21"
smss向会话总线发送Hello取得本身的惟一名":1.21"。 method call sender=:1.21 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=Hello
smss调用AddMatch要求接收会话总线的NameOwnerChanged信号。 method call sender=:1.21 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='signal',sender='org.freedesktop.DBus',path='/org/freedesktop/DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
smss调用AddMatch要求接收会话总线发送的全部信号。 method call sender=:1.21 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='signal',sender='org.freedesktop.DBus',path='/',interface='org.freedesktop.DBus'"
smss调用GetNameOwner获取链接"org.freedesktop.DBus"的惟一名。 method call sender=:1.21 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=GetNameOwner
   string "org.freedesktop.DBus"
会话总线发送NameOwnerChanged信号,通知惟一名为":1.21"的链接得到了公众名"org.freesmartphone.ogsmd"。 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string "org.freesmartphone.ogsmd"
   string ""
   string ":1.21"
smss请求公众名"org.freesmartphone.ogsmd"。分配公众名在前,请求公众名在后,应该是监控过程颠倒了消息次序。 method call sender=:1.21 -> dest=org.freedesktop.DBus path=/; interface=org.freedesktop.DBus; member=RequestName
   string "org.freesmartphone.ogsmd"
   uint32 0
smsc链接会话总线。会话总线发NameOwnerChanged信号,通知惟一名":1.22"被分配。 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.22"
   string ""
   string ":1.22"
smss向会话总线发送Hello取得本身的惟一名":1.22"。 method call sender=:1.22 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=Hello
smsc调用AddMatch要求接收会话总线的NameOwnerChanged信号。 method call sender=:1.22 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='signal',sender='org.freedesktop.DBus',path='/org/freedesktop/DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
smsc调用AddMatch要求接收链接'org.freesmartphone.ogsmd'中对象'/org/freesmartphone/GSM/Device'的'org.freesmartphone.GSM.SMS'接口的信号。 method call sender=:1.22 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='signal',sender='org.freesmartphone.ogsmd',path='/org/freesmartphone/GSM/Device',interface='org.freesmartphone.GSM.SMS'"
smsc调用GetNameOwner获取链接"org.freesmartphone.ogsmd"的惟一名。 method call sender=:1.22 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=GetNameOwner
   string "org.freesmartphone.ogsmd"
smsc调用链接'org.freesmartphone.ogsmd'中对象'/org/freesmartphone/GSM/Device'的'org.freesmartphone.GSM.SMS'接口的SendMessage方法。 method call sender=:1.22 -> dest=org.freesmartphone.ogsmd path=/org/freesmartphone/GSM/Device; interface=org.freesmartphone.GSM.SMS; member=SendMessage
   string "10987654321"
   string "hello world"
   array [
      dict entry(
         string "csm_seq"
         variant int32 2
      )
      dict entry(
         string "alphabet"
         variant string "gsm"
      )
      dict entry(
         string "csm_num"
         variant int32 8
      )
   ]
smss向smsc发送method return消息,返回SendMessage方法的输出参数。 method return sender=:1.21 -> dest=:1.22 reply_serial=5
   int32 11
smss发送IncomingMessage信号。 signal sender=:1.21 -> dest=(null destination) path=/org/freesmartphone/GSM/Device; interface=org.freesmartphone.GSM.SMS; member=IncomingMessage
   string "12345678901"
   string "hello signal!"
   array [
      dict entry(
         string "csm_seq"
         variant int32 1
      )
      dict entry(
         string "alphabet"
         variant string "ucs2"
      )
      dict entry(
         string "csm_num"
         variant int32 3
      )
   ]
会话总线通知链接":1.22",即smsc的链接已经切断。 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.22"
   string ":1.22"
   string ""
会话总线通知拥有公共名"org.freesmartphone.ogsmd"的链接已经切断。 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string "org.freesmartphone.ogsmd"
   string ":1.21"
   string ""
会话总线通知拥有惟一名":1.21"的链接已经切断。即smss已经终止。 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.21"
   string ":1.21"
   string ""

 

六、工程

我提供下载的文件要用make distcheck制做的,其中包含了一些自动生成的文件。执行./clean.sh能够删掉自动生成的文件,只留下我建立的文件:

$ find . -type f
./clean.sh
./Makefile.am
./autogen.sh
./src/gsm_sms.h
./src/Makefile.am
./src/sms-marshal.list
./src/smss.xml
./src/smss.c
./src/gsm_sms.c
./src/sms_features.h
./src/sms_features.c
./src/smsc.c
./configure.ac

前面已经介绍过全部的源文件。咱们再看看工程文件:

$ cat autogen.sh
#! /bin/sh
touch `find .`
aclocal
autoconf
autoheader
touch NEWS README AUTHORS ChangeLog
automake --add-missing

$ cat Makefile.am
SUBDIRS = src
EXTRA_DIST = autogen.sh clean.sh

autogen.sh创建工程环境。在执行clean.sh后,执行autogen.sh从新生成configure等工程文件。其中的touch命令是为了防止文件有未来的时间戳。由于我在虚拟机中运行ubuntu,因此可能会出现这类问题。 Makefile.am将autogen.sh clean.sh也做为发布文件。最重要的工程文件是"configure.ac"和"src/Makefile.am"。

6.一、configure.ac

$ cat -n configure.ac
     1  AC_INIT()
     2  AM_INIT_AUTOMAKE(hello-dbus5, 0.1)
     3  AM_CONFIG_HEADER(config.h)
     4
     5  AC_PROG_CC
     6
     7
     8  # Dbus detection
     9  PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.1, have_dbus=yes, have_dbus=no)
    10
    11  if test x$have_dbus = xno ; then
    12      AC_MSG_ERROR([DBus development libraries not found])
    13  fi
    14  AM_CONDITIONAL(HAVE_DBUS, test x$have_dbus = xyes)
    15
    16  AC_SUBST(DBUS_CFLAGS)
    17  AC_SUBST(DBUS_LIBS)
    18
    19
    20  # Glib detection
    21  PKG_CHECK_MODULES(DBUS_GLIB, gobject-2.0 >= 2.6, have_glib=yes, have_glib=no)
    22
    23  if test x$have_glib = xno ; then
    24      AC_MSG_ERROR([GLib development libraries not found])
    25  fi
    26
    27  AM_CONDITIONAL(HAVE_GLIB, test x$have_glib = xyes)
    28
    29  AC_SUBST(DBUS_GLIB_CFLAGS)
    30  AC_SUBST(DBUS_GLIB_LIBS)
    31
    32
    33  AC_OUTPUT([Makefile
    34             src/Makefile])

8-17行检查dbus库,它们会生成编译常数DBUS_CFLAGS和DBUS_LIBS。 20-30行检查dbus-glib库,它们会生成编译常数DBUS_GLIB_CFLAGS和DBUS_GLIB_LIBS。

6.二、src/Makefile.am

$ cat -n src/Makefile.am
     1  INCLUDES = /
     2          $(DBUS_CFLAGS)                          /
     3          $(DBUS_GLIB_CFLAGS)                     /
     4          -DDBUS_COMPILATION
     5
     6  LIBS = /
     7          $(DBUS_LIBS)                            /
     8          $(DBUS_GLIB_LIBS)                       /
     9          -ldbus-glib-1
    10
    11  # smss
    12  noinst_PROGRAMS = smss
    13
    14  BUILT_SOURCES = smss-glue.h sms-marshal.h sms-marshal.c
    15  smss_SOURCES = $(BUILT_SOURCES) smss.c gsm_sms.c sms_features.c
    16  noinst_HEADERS = gsm_sms.h sms_features.h
    17
    18
    19  smss-glue.h: smss.xml
    20          $(LIBTOOL) --mode=execute dbus-binding-tool --prefix=gsm_sms --mode=glib-server --output=smss-glue.h $(srcdir)/smss.xml
    21
    22  sms-marshal.h: sms-marshal.list
    23          $(LIBTOOL) --mode=execute glib-genmarshal --header sms-marshal.list --prefix=sms_marshal > sms-marshal.h
    24
    25  sms-marshal.c: sms-marshal.list
    26          $(LIBTOOL) --mode=execute glib-genmarshal --body sms-marshal.list --prefix=sms_marshal > sms-marshal.c
    27
    28  CLEANFILES = $(BUILT_SOURCES)
    29
    30  EXTRA_DIST = smss.xml sms-marshal.list
    31
    32  # smss
    33  noinst_PROGRAMS += smsc
    34  smsc_SOURCES= smsc.c sms-marshal.c sms_features.c

19-20行由接口描述文件smss.xml生成存根文件smss-glue.h。22-26行由列集接口定义生成包含列集函数的代码。

七、结束语

本文介绍了一个简单的dbus-glib的例子,包括服务器和客户端。第一讲中还有一个加法例子,若是你理解了本文的例子,那个例子就更简单了。 dbus-glib源代码中有两个例子:

  • example-service和example-client演示方法调用。这个例子的接口描述文件中有个参数类型写错了,将(us)写成(ss),运行时会出错。可能做者想演示一下接口定义与代码实现不一致的后果吧。读者能够从这里下载我修改过的代码。
  • example-signal-emitter和example-signal-recipient演示信号发射。这个例子中,example-signal-recipient调用example-signal-emitter的方法请求发送信号。实际上信号应该是来自服务器侧的信息。我将其改为在example-signal-emitter中敲键发送信号。读者能够从这里下载我修改过的代码。

好了,《dbus实例讲解》到此结束。其实个人全部文章只是但愿能让这复杂的世界简单一点。

相关文章
相关标签/搜索