DBus进程间通信主要有三层架构:
1.底层接口层:主要是通过libdbus这个函数库,给予系统使用DBus的能力。
2.总线层:主 要Message bus daemon这个总线守护进程提供的,在Linux系统启动时运行,负责进程间的消息路由和传递,其中包括Linux内核和Linux桌面环境的消息传 递。总线守护进程可同时与多个应用程序相连,并能把来自一个应用程序的消息路由到0或者多个其他程序。
3.应用封装层:通过一系列基于特定应用程序框架将DBus的底层接口封装成友好的Wrapper库,供不同开发人员使用。比如libdbus-glib, libdbus-python.
如上图所示,Bus Daemon
Process是运行在linux系统中的一个后台守护进程,dbus-daemon运行时会调用libdus的库。Application
Process1代表的就是应用程序进程,通过调用特定的应用程序框架的Wrapper库与dbus-daemon进行通信。
从上图也可以看出来Application和Daemon中其实还是通过socket进行通行。
DBus的三大优点:低延迟,低开销,高可用性。
*低延迟:DBus一开始就是用来设计成避免来回传递和允许异步操作的。因此虽然在Application和Daemon之间是通过socket实现的,但是又去掉了socket的循环等待,保证了操作的实时高效。
*低开销:DBus使用一个二进制的协议,不需要转化成像XML这样的文本格式。因为DBus是主要用来机器内部的IPC,而不是为了网络上的IPC机制而准备的.所以它才能够在本机内部达到最优效果。
*高可用性:DBus是基于消息机制而不是字节流机制。它能自动管理一大堆困难的IPC问题。同样的,DBus库被设计来让程序员能够使用他们已经写好的代码。而不会让他们放弃已经写好的代码,被迫通过学习新的IPC机制来根据新的IPC特性重写这些代码。
在介绍基本概念之前,先介绍一个学习DBus的好工具d-feet,这个工具主要是用来查看DBus的System Bus和Session Bus中的各个消息连接的。当然,你也可以在这里面添加自己创建的消息总线,以便于观察。
下面根据上图介绍一下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
红色的两个是消息总线提供的标准接口,而剩下的一个是需要具体的应用去实现的。
Methods和Signals
Methods表示可以被具体调用的方法
Signals则表示的是信号,此信号可以被广播,而连接了这个信号的对象在接收到信号时就可以进行相应的处理。和Qt中的信号应该是一个意思。
D-BUS 基础
dbus的目的主要是下面两点:
1.在同一个桌面会话中,进行桌面应用程序之间的通讯
2.桌面程序与内核或者守护进程的通信。
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基础知识
1. 进程间使用D-Bus通信
D-Bus是一种高级的进程间通信机制,它由项目提供,使用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.。或者在终端上输入下列指令:
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!的信号。该程序的源代码如下:
#include <glib.h> // 包含glib库
#include <dbus/dbus-glib.h> // 包含
glib库中D-Bus管理库#include <stdio.h>
static gboolean send_ding(DBusConnection *bus);// 定义发送消息函数的原型
int main ()
{
GMainLoop *loop; // 定义一个事件循环对象的指针
DBusConnection *bus; // 定义总线连接对象的指针
DBusError error; // 定义D-Bus错误消息对象
loop = g_main_loop_new(NULL, FALSE); // 创建新事件循环对象
dbus_error_init (&error); // 将错误消息对象连接到D-Bus
// 错误消息对象
bus = dbus_bus_get(DBUS_BUS_SESSION, &error);// 连接到总线
if (!bus) { // 判断是否连接错误
g_warning("连接到D-Bus失败: %s", error.message);
// 使用GLib输出错误警告信息
dbus_error_free(&error); // 清除错误消息
return 1;
}
dbus_connection_setup_with_g_main(bus, NULL);
// 将总线设为接收GLib事件循环
g_timeout_add(1000, (GSourceFunc)send_ding, bus);
// 每隔1000ms调用一次send_ding()函数
// 将总线指针作为参数
g_main_loop_run(loop); // 启动事件循环
return 0;
}
static gboolean send_ding(DBusConnection *bus) // 定义发
送消息函数的细节{
DBusMessage *message; // 创建消息对象指针
message = dbus_message_new_signal("/com/burtonini/dbus/ding",
"com.burtonini.dbus.Signal",
"ding"); // 创建消息对象并标识路径
dbus_message_append_args(message,
DBUS_TYPE_STRING, "ding!",
DBUS_TYPE_INVALID); //将字符串Ding!定义为消息
dbus_connection_send(bus, message, NULL); // 发送该消息
dbus_message_unref(message); // 释放消息对象
g_print("ding!/n"); // 该函数等同与标准输入输出
return TRUE;
}
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程序发送到消息。该程序的源代码如下:
#include <glib.h> // 包含glib库
#include <dbus/dbus-glib.h> // 包含glib库中D-Bus管理库
static DBusHandlerResult signal_filter // 定义接收消息函数的原型
(DBusConnection *connection, DBusMessage *message, void *user_data);
int main()
{
GMainLoop *loop; // 定义一个事件循环对象的指针
DBusConnection *bus; // 定义总线连接对象的指针
DBusError error; // 定义D-Bus错误消息对象
loop = g_main_loop_new(NULL, FALSE); // 创建新事件循环对象
dbus_error_init(&error); // 将错误消息对象连接到D-Bus
// 错误消息对象
bus = dbus_bus_get(DBUS_BUS_SESSION, &error); // 连接到总线
if (!bus) { // 判断是否连接错误
g_warning("连接到D-Bus失败: %s", error.message);
// 使用GLib输出错误警告信息
dbus_error_free(&error); // 清除错误消息
return 1;
}
dbus_connection_setup_with_g_main(bus, NULL);
// 将总线设为接收GLib事件循环
dbus_bus_add_match(bus, "type='signal',interface
='com.burtonini.dbus.Signal'"); // 定义匹配器dbus_connection_add_filter(bus, signal_filter, loop, NULL);
// 调用函数接收消息
g_main_loop_run(loop); // 启动事件循环
return 0;
}
static DBusHandlerResult // 定义接收消息函数的细节
signal_filter (DBusConnection *connection,
DBusMessage *message, void *user_data){
GMainLoop *loop = user_data; // 定义事件循环对象的指针,并与主函数中的同步
if (dbus_message_is_signal // 接收连接成功消息,判断是否连接失败
(message, DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL,
"Disconnected")) {g_main_loop_quit (loop); // 退出主循环
return DBUS_HANDLER_RESULT_HANDLED;
}
if (dbus_message_is_signal(message, "com.burtonini.dbus.Signal",
"Ping")) {
// 指定消息对象路径,判断是否成功
DBusError error; // 定义错误对象
char *s;
dbus_error_init(&error); // 将错误消息对象连接到D-Bus错误
// 消息对象
if (dbus_message_get_args // 接收消息,并判断是否有错误
(message, &error, DBUS_TYPE_STRING, &s,
DBUS_TYPE_INVALID)) {g_print("接收到的消息是: %s/n", s); // 输出接收到的消息
dbus_free (s); // 清除该消息
}
else { // 有错误时执行下列语句
g_print("消息已收到,但有错误提示: %s/n", error.message);
dbus_error_free (&error);
}
return DBUS_HANDLER_RESULT_HANDLED;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
该程序侦听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的文章。本文的目标是补充一些简单的例子。
1、dbus是什么东西?
网上有一篇叫“D-Bus Tutorial”的文章,流传较广。不少介绍dbus的资料,都引用了其中的段落。其实相对于这篇文章,我建议大家直接读“D-Bus Specification”,篇幅不算长,文字也不算枯燥。
D-Bus是针对桌面环境优化的IPC(interprocess communication )机制,用于进程间的通信或进程与内核的通信。最基本的D-Bus协议是一对一的通信协议。但在很多情况下,通信的一方是消息总线。消息总线是一个特殊的 应用,它同时与多个应用通信,并在应用之间传递消息。下面我们会在实例中观察消息总线的作用。消息总线的角色有点类似与X系统中的窗口管理器,窗口管理器 既是X客户,又负责管理窗口。
支持dbus的系统都有两个标准的消息总线:系统总线和会话总线。系统总线用于系统与应用的通信。会话总线用于应用之间的通信。网上有一个叫d-feet的python程序,我们可以用它来观察系统中的dbus世界。
图1、由d-feet观察到的D-Bus世界
D-Bus是一个程序。它提供了API。但我们一般不会直接使用dbus的接口。dbus-glib是GTK版本的dbus接口封装。本文假设读者 安装了dbus-glib,我安装的是dbus-glib-0.76。后面还会看到,通过python操纵dbus是多么简单。
2、D-Bus的基本概念
2.1、从例子开始
我写了一个最简单的dbus服务器,它通过dbus提供了一个加法的接口。大家可以下载这个例子。这是一个autotool工程,大家解包后,执行: