D-Bus的方式在移动手机操作系统中非常重要,包括Maemo,Moblin等以Linux为基础的操作系统。
参考链接:D-bus简介 , 使用 D-BUS 连接桌面应用程序 , D-总线规格
D-bus简介
什么是D-Bus
有很多IPC(interprocess communication ) ,用于不同的解决方案:CORBA 是用于面向对象编程中复杂的 IPC 的一个强大的解决方案。DCOP 是一个较轻量级的 IPC 框架,功能较少,但是可以很好地集成到 K 桌面环境中。SOAP 和 XML-RPC 设计用于 Web 服务,因而使用 HTTP 作为其传输协议。D-BUS 设计用于桌面应用程序和 OS 通信。D-Bus(其中D原先是代表桌面“Desktop” 的意思),即:用于桌面操作系统的通信总线。现在逐渐被引入到嵌入式系统中,不过名字还是保留原先的叫法而已。
D-Bus的特性
- 低延迟、低开小,设计小而高效
- 二进制协议而不是文本,去除了序列化过程
- 结构化的名字空间
- 独立于架构的数据格式
- 支持消息中的大部分通用数据元素
- 带有异常处理的通用远程调用接口
- 支持广播类型的通信
D-Bus的内容
D-Bus是一个为应用程序间通信的消息总线系统, 用于进程之间的通信。它是个3层架构的IPC 系统,包括:
- 函数库libdbus ,用于两个应用程序互相联系和交互消息。
- 一个基于libdbus构造的消息总线守护进程,可同时与多个应用程序相连,并能把来自一个应用程序的消息路由到0或者多个其他程序。
- 基于特定应用程序框架的封装库或捆绑(wrapper libraries or bindings )。例如,libdbus-glib和libdbus-qt,还有绑定在其他语言,例如Python的。大多数开发者都是使用这些封装库的API,因为它们简化了D-Bus编程细节。libdbus被有意设计成为更高层次绑定的底层后端(low-level backend )。大部分libdbus的 API仅仅是为了用来实现绑定。
D-Bus的内部工作方式
典型的 D-BUS 设置将由几个总线构成。将有一个持久的 系统总线(system bus),它在 引导时就会启动。这个总线由操作系统和后台进程使用,安全性非常好,以使得任意的应用程序 不能欺骗系统事件。还将有很多 会话总线(session buses),这些总线当用户登录后启动,属于 那个用户私有。它是用户的应用程序用来通信的一个会话总线。当然,如果一个应用程序需要接收 来自系统总线的消息,它不如直接连接到系统总线 —— 不过,它可以发送的消息将是受限的。
一旦应用程序连接到了一个总线,它们就必须通过添加 匹配器(matchers) 来声明它们希望 收到哪种消息。匹配器为可以基于接口、对象路径和方法进行接收的消息指定一组规则(见后)。 这样就使得应用程序可以集中精力去处理它们想处理的内容,以实现消息的高效路由,并保持总线 上消息的预期数量,以使得不会因为这些消息导致所有应用程序的性能下降并变得很慢。
消息类型
在 D-BUS 中有四种类型的消息:方法调用(method calls)、方法返回(method returns)、信号(signals) 和错误(errors)。要执行D-BUS
对象的方法,您需要向对象发送一个方法调用消息。它将完成一些处理并返回 一个方法返回消息或者错误消息。信号的不同之处在于它们不返回任何内容:既没有“信号返回”消息,也没有 任何类型的错误消息。
消息也可以有任意的参数。参数是强类型的,类型的范围是从基本的非派生类型(布尔(booleans)、 字节(bytes)、整型(integers))到高层次数据结构(字符串(strings)、数组( arrays)和字典(dictionaries))。
DBusBusType
Name | Type |
---|---|
DBUS_BUS_SESSION | 登录的会话总线 |
DBUS_BUS_SYSTEM | 系统范围的总线 |
DBUS_BUS_STARTER | 启动的线程(如果有的话) |
DBusHandlerResult
Name | Result |
---|---|
DBUS_HANDLER_RESULT_HANDLED | 消息已产生效果-无需运行更多处理程序。 |
DBUS_HANDLER_RESULT_NOT_YET_HANDLED | 消息没有任何效果-查看其他处理程序是否需要它。 |
DBUS_HANDLER_RESULT_NEED_MEMORY | 需要更多内存才能返回DBUS_HANDLER_RESULT_HANDLED 或DBUS_HANDLER_RESULT_NOT_YET_HANDLED 。请稍后再尝试使用更多内存。 |
DBusError
Usage | Meanings |
---|---|
const char* name | 公共的error名称字段 |
const char* message | 公共的error消息字段 |
D-bus应用
D-Bus文档:D-Bus 1.13.16
常用D-Bus函数
1 | dbus_g_bus_get (DBusBusType type, |
Function:
r给指定的总线返回一个连接,这个连接是与该函数的其他调用者共享的全局变量。
Parameters:
Type
:DBusBusType
error
:错误可以被返回的地址
1 | dbus_g_thread_init(void); |
初始化D-Bus
线程系统。此函数只能被调用一次,并且必须调用在D-Bus
的API中任何其他函数之前。
1 | dbus_error_init() |
Function:
初始化DBusError结构。
Parameters:
error
:DBusError
1 | dbus_error_free (&error); |
Function:
释放已设置或者刚刚初始化的错误,然后像dbus_error_init()一样重新初始化该错误
Parameters:
&error
:存储错误的内存
1 | dbus_connection_setup_with_g_main (DBusConnection *connection, |
Function:
设置DBusConnection的监视和超时功能,将连接与GLib主循环集成在一起。
Parameters:
connection
:GMainContext内容,如果为NULL,将会使用默认的context;
context
:设置为True
表示循环正在运行;True
或者FLASE
都无所谓,在调用g_main_loop_run()
之后,该设置都会被设置为True
1 | dbus_message_new_signal ( |
Function:
构造一个表示消息发射的新消息。
如果无法为消息分配内存,则返回NULL。
Parameters:
Path
:发出signal的对象路径
iface
:发出signal的接口
name
:signal的名称
1 | dbus_g_proxy_new_for_name (DBusGConnection *connection, |
为通过消息总线上的连接导出的远程接口创建新的代理。通过该代理的方法调用和信号连接将转到名称所有者;该名称的所有者应支持给定的接口名称。所有者的名称可能会随着时间变化,例如在两个不同的方法调用之间,除非名称是唯一的名称。如果需要固定所有者,则需要请求当前所有者,并将代理绑定到其唯一名称而不是通用名称,参考 dbus_g_proxy_new_for_name_owner ()
。
与名称相关的代理仅对消息总线有意义,而不适用于应用程序对应用程序的直接dbus连接。
仅当DBusConnection
断开连接,该代理没有剩余引用或该名称是唯一名称并且其所有者消失时,此代理才会发出“销毁”信号。 如果一个“众所周知”的名称更改了所有者,则代理将仍然有效。
1 | dbus_g_proxy_new_for_name_owner( ,DBusGConnection *connection, |
和dbus_g_proxy_new_for_name()
类似,但却是想消息总线发出一个往返请求来获取当前名称所有者,然后将代理绑定到当前所有者的唯一名称,而不是well-known name
。结果,名称所有者将不随时间改变,并且当所有者从消息总线中消失时,代理将发出“销毁”的信号。
两者区别举例:
如果你使用了org.freedesktop.Database
作为一个“well-known name”,哪怕所有者更改,dbus_g_proxy_new_for_name
依然会绑定到该名称;而dbus_g_proxy_new_for_name_owner
则会绑定到该所有者的唯一名称,而不是通用名称。
1 | dbus_connection_send_with_reply ( |
Function:
对要发送的消息进行排队,与dbus_connection_send()
相同,但也返回一个用于接收对消息的答复的DBusPendingCall
。
如果在给定的超时时间内没有收到任何回复,则此函数会使挂起的回复过期,并生成一个合成错误回复(进程中生成,而不是由远程应用程序生成),指示发生了超时。
Parameters:
connection
:the connection
message
:the message to send
pending_return
:返回DBusPendingCall
对象的位置;如果连接断或者尝试在不支持Unix文件描述符的连接数发送它们,则返回NULL
timeout_milliseconds
:超时以毫秒为单位,-1(或DBUS_timeout_USE_DEFAULT
),或DBUS_timeout_INFINITE
表示无超时
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_bus_get()
- 为这个dbus连接(
DbusConnection
)起名:dbus_bus_request_name()
- 将总线设置为接收Glib事件循环:
dbus_connection_setup_with_g_main (bus, NULL)
- 总指针设为参数,定时调用一次
send()
函数:g_timeout_add(time,send(),bus)
- 启动事件循环:
g_main_loop_run (loop)
send()
函数
- 建立一个发送信号的通道(包括信号的路径、接口名和信号名):
dbus_message_new_signal()
- 将信号对应的相关参数压进去
- 将参数附加到消息末尾:
dbus_message_iter_init_append(DBusMessage * message, DBusMessageIter * iter )
- 向消息追加基本类型值:
dbus_message_iter_append_basic(DBusMessageIter * iter, int type, const void * value)
- 在给定变量参数列表的消息中追加字段:
dbus_message_append_args(DBusMessage * message, int first_arg_type, ... )
- 发送信号:
dbus_connection_send()
,dbus_connection_flush()
- 释放消息对象:
dbus_message_unref (message)
- return TRUE;
进行一次远程调用的流程:
- 建立一个dbus连接:
dbus_bus_get()
- 为这个dbus连接(
DbusConnection
)起名:dbus_bus_request_name()
- 申请一个远程调用通道(填入服务器名、接口名、调用名):
dbus_message_new_method_call()
- 压入本次调用的参数:
dbus_message_iter_init_append()
、dbus_message_iter_append_basic()
、dbus_message_append_args()
- 启动发送调用并释放相关的消息:
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()
信号接收流程:
main函数
- 建立一个dbus连接:
dbus_bus_get()
- 为这个dbus连接(
DbusConnection
)起名:dbus_bus_request_name()
- 将总线设置为接收Glib事件循环:
dbus_connection_setup_with_g_main (bus, NULL)
- 为将要进行的消息循环添加匹配条件(类型和接口):
dbus_bus_add_match()
- 设置通知函数:
dbus_connection_add_filter(connection, function, user_data, free_data_function )
- 运行这个事件循环:
g_main_loop_run (loop)
通知函数
- 检测是否连接成功:
dbus_message_is_signal(message, DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL, "Disconnected")
- 若连接成功,进入匹配判断:
dbus_message_is_signal(message, Interface, Name)
- error初始化:
dbus_error_init(&eror)
- 接收消息,并判断是否有错误:
dbus_message_get_args (message, &error, DBUS_TYPE_STRING, the message address(&s), DBUS_TYPE_INVALID)
- 打印输出相应的结果:
g_print()
- 根据结果返回
HANDLER
类型:
- return DBUS_HANDLER_RESULT_HANDLED;
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
D-Bus示例——通过会话总线收发信号
- dbus-ping-send.c:每秒通过会话总线发送一个参数为字符串“Ping!”的信号
1 |
|
main函数:创建一个 GLib
事件循环,获得会话总线的一个连接, 并将D-BUS
事件处理集成到 Glib
事件循环之中。然后它创建了一个名为 send_ping
间隔为一秒的计时器,并启动事件循环。send_ping
构造一个来自于对象路径 /com/burtonini/dbus/ping 和接口 com.burtonini.dbus.Signal
的新的 Ping 信号。然后,字符串 “Ping!”作为参数添加到信号中并通过总线发送。在标准输出中会打印一条消息以让用户知道发送了 一个信号。
- dbus-ping-listen.c:这个程序侦听
dbus-ping-send.c
正在发出的信号
1 |
|
这个程序侦听 dbus-ping-send.c 正在发出的信号。 main
函数 和前面一样启动,创建一个到总线的连接。然后它声明愿意当具有 com.burtonini.dbus.Signal
接口的信号被发送时得到通知,将 signal_filter
设置为通知函数, 然后进入事件循环。
当满足匹配的消息被发送时, signal_func
会被调用。不过,它也将会 收到来自总线本身的总线管理信号。要确定接收到消息时应该做些什么,仅仅需要检验消息头。如果消息是 总线断开信号,则事件循环终止,因为侦听一个不存在的总线是没有意义的。(告知总线信号已经处理)。 然后,将到来的消息与期望的消息相比较,如果成功,则解出参数并输出。如果到来的消息不是其中的任何一个, 则告知总线没有处理那个消息。