消息和消息总线
应用程序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总线上的消息。
1、D-Bus的消息
上一讲说过最基本的D-Bus协议是一对一的通信协议。与直接使用socket不同,D-Bus是面向消息的协议。 D-Bus的所有功能都是通过在连接上流动的消息完成的。
1.1、消息类型
D-Bus有四种类型的消息:
- method_call 方法调用
- method_return 方法返回
- error 错误
- signal 信号
前面介绍的远程方法调用就用到了method_call和method_return消息。顾名思义,在发生错误时会产生error消息。 如果把method_call看作打电话,那么signal消息就是来电了。后面还会详细讨论。
1.2、dbus-send和dbus-monitor
dbus提供了两个小工具:dbus-send和dbus-monitor。我们可以用dbus-send发送消息。用dbus-monitor监视总线上流动的消息。 让我们通过dbus-send发送消息来调用前面的Add方法,这时dbus-send充当了应用程序B。用dbus-monitor观察调用过程中的消息。
dbus-send的详细用法可以参阅手册。调用远程方法的一般形式是:
$ dbus-send [--system | --session] --type=method_call --print-reply --dest=连接名 对象路径 接口名.方法名 参数类型:参数值 参数类型:参数值
dbus-send支持的参数类型包括:string, int32, uint32, double, byte, boolean。
2、消息总线的方法和信号
2.1、概述
消息总线是一个特殊的应用,它可以在与它连接的应用之间传递消息。 可以把消息总线看作一台路由器。正是通过消息总线,D-Bus才在一对一的通信协议基础上实现了多对一和一对多的通信。
消息总线虽然有特殊的转发功能,但消息总线也还是一个应用。 其它应用与消息总线的通信也是通过1.1节的基本消息类型完成的。作为一个应用,消息总线也提供了自己的接口,包括方法和信号。
我们可以通过向连接“org.freedesktop.DBus ”上对象“/”发送消息来调用消息总线提供的方法。 事实上,应用程序正是通过这些方法连接到消息总线上的其它应用,完成请求公共名等工作的。
2.2、清单
在FCP和AC的板子上使用dbus-send,需要先执行export $(dbus-launch)
.
root@xxxx-obmc:~# dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListNames
Failed to open connection to "session" message bus: Using X11 for dbus-daemon autolaunch was disabled at compile time, set your DBUS_SESSION_BUS_ADDRESS instead
root@xxxx-obmc:~# export $(dbus-launch)
root@xxxx-obmc:~#
root@xxxx-obmc:~# dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListNames
method return time=61305.454012 sender=org.freedesktop.DBus -> destination=:1.0 serial=3 reply_serial=2
array [
string "org.freedesktop.DBus"
string ":1.0"
]
root@xxxx-obmc:~#
消息总线对象支持第一讲中提到的标准接口"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
root@xxxx-obmc:~# dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.Introspectable.Introspect
method return time=109.212525 sender=org.freedesktop.DBus -> destination=:1.1 serial=3 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">
<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="UpdateActivationEnvironment">
<arg direction="in" type="a{ss}"/>
</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="GetAdtAuditSessionData">
<arg direction="in" type="s"/>
<arg direction="out" type="ay"/>
</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>
<method name="GetConnectionCredentials">
<arg direction="in" type="s"/>
<arg direction="out" type="a{sv}"/>
</method>
<property name="Features" type="as" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
</property>
<property name="Interfaces" type="as" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
</property>
<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>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg direction="out" type="s"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Peer">
<method name="GetMachineId">
<arg direction="out" type="s"/>
</method>
<method name="Ping">
</method>
</interface>
<node name="org/freedesktop/DBus"/>
</node>
"
从输出可以看到会话总线对象支持标准接口“org.freedesktop.DBus.Introspectable”和接口“org.freedesktop.DBus”。 接口“org.freedesktop.DBus”有16个方法和3个信号。下表列出了“org.freedesktop.DBus”的12个方法的简要说明
mathod | 说明 |
---|---|
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) | 通知应用获得了指定名称的拥有权。 |
参数session和system的区别:
dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListNames
method return time=875.939015 sender=org.freedesktop.DBus -> destination=:1.2 serial=3 reply_serial=2
array [
string "org.freedesktop.DBus"
string ":1.2"
]
root@obmc:~#
root@obmc:~#
root@obmc:~#
root@obmc:~# dbus-send --system --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListNames
method return time=908.657105 sender=org.freedesktop.DBus -> destination=:1.143 serial=4294967295 reply_serial=2
array [
string "org.freedesktop.DBus"
......
]
以xyz.openbmc_project.Control.Host.NMI
为例子,查看他的根对象。
root@obmc:~# dbus-send --system --type=method_call --print-reply --dest=xyz.openbmc_project.Control.Host.NMI / org.freedesktop.DBus.Introspectable.Introspect
method return time=1482.867385 sender=:1.50 -> destination=:1.144 serial=150 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.Peer">
<method name="Ping"/>
<method name="GetMachineId">
<arg type="s" name="machine_uuid" direction="out"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" type="s" direction="out"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg name="interface" direction="in" type="s"/>
<arg name="property" direction="in" type="s"/>
<arg name="value" direction="out" type="v"/>
</method>
<method name="GetAll">
<arg name="interface" direction="in" type="s"/>
<arg name="properties" direction="out" type="a{sv}"/>
</method>
<method name="Set">
<arg name="interface" direction="in" type="s"/>
<arg name="property" direction="in" type="s"/>
<arg name="value" direction="in" type="v"/>
</method>
<signal name="PropertiesChanged">
<arg type="s" name="interface"/>
<arg type="a{sv}" name="changed_properties"/>
<arg type="as" name="invalidated_properties"/>
</signal>
</interface>
<interface name="org.freedesktop.DBus.ObjectManager">
<method name="GetManagedObjects">
<arg type="a{oa{sa{sv}}}" name="object_paths_interfaces_and_properties" direction="out"/>
</method>
<signal name="InterfacesAdded">
<arg type="o" name="object_path"/>
<arg type="a{sa{sv}}" name="interfaces_and_properties"/>
</signal>
<signal name="InterfacesRemoved">
<arg type="o" name="object_path"/>
<arg type="as" name="interfaces"/>
</signal>
</interface>
<node name="xyz"/>
</node>
"
如上,他的根目录下有一个子节点,查找信息如下,获取到的接口信息与在根目录下获取的信息一致。
root@obmc:~# dbus-send --system --type=method_call --print-reply --dest=xyz.openbmc_project.Control.Host.NMI /xyz org.freedesktop.DBus.Introspectable.Introspect
method return time=1762.447872 sender=:1.50 -> destination=:1.145 serial=151 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.Peer">
<method name="Ping"/>
<method name="GetMachineId">
<arg type="s" name="machine_uuid" direction="out"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" type="s" direction="out"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg name="interface" direction="in" type="s"/>
<arg name="property" direction="in" type="s"/>
<arg name="value" direction="out" type="v"/>
</method>
<method name="GetAll">
<arg name="interface" direction="in" type="s"/>
<arg name="properties" direction="out" type="a{sv}"/>
</method>
<method name="Set">
<arg name="interface" direction="in" type="s"/>
<arg name="property" direction="in" type="s"/>
<arg name="value" direction="in" type="v"/>
</method>
<signal name="PropertiesChanged">
<arg type="s" name="interface"/>
<arg type="a{sv}" name="changed_properties"/>
<arg type="as" name="invalidated_properties"/>
</signal>
</interface>
<node name="openbmc_project"/>
</node>
"
2.3.2、ListActivatableNames和服务器的自动启动
这里使用session和system获得的结果不一致。
root@obmc:~# dbus-send --system --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListActivatableNames
method return time=1939.107887 sender=org.freedesktop.DBus -> destination=:1.146 serial=4294967295 reply_serial=2
array [
string "org.freedesktop.DBus"
string "org.freedesktop.Avahi"
string "org.freedesktop.hostname1"
string "org.freedesktop.login1"
string "org.freedesktop.network1"
string "org.freedesktop.resolve1"
string "org.freedesktop.systemd1"
string "org.freedesktop.timedate1"
string "org.freedesktop.timesync1"
string "xyz.openbmc_project.EntityManager"
]
root@obmc:~# dbus-send --session --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListActivatableNames
method return time=1960.060127 sender=org.freedesktop.DBus -> destination=:1.3 serial=3 reply_serial=2
array [
string "org.freedesktop.DBus"
string "org.freedesktop.systemd1"
]
执行:
root@obmc:~# cat /usr/share/dbus-1/services/*|grep Name|awk -F= '{print $2}'|sort
org.freedesktop.systemd1
它将"/usr/share/dbus-1/services/“下所有文件交给grep筛选出包含“Name”的行。将包含“Name”的行交给awk处理,awk用”="作为列分隔符,取出第二列然后交给sort排序后输出。 "/usr/share/dbus-1/services/"目录就是dbus放service文件的地方。需要自动启动的服务器会在这个目录放一个service文件,例如:
root@obmc:~# cat /usr/share/dbus-1/services/org.freedesktop.systemd1.service
# SPDX-License-Identifier: LGPL-2.1+
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[D-BUS Service]
Name=org.freedesktop.systemd1
Exec=/bin/false
User=root
Name是服务器的公共名,Exec是服务器的执行路径。在客户请求一个服务,但该服务还没有启动时。dbus会根据service文件自动启动服务。
文献资料
doc /子目录中的一些内容是预先构建的,可以在此处浏览。如果您是D-Bus的新手,则该教程可能是最好的入门指南(即使它非常不完整,也涵盖了基础知识)。
通用D-Bus协议信息:
- D-Bus规格
- txdbus文档中的D总线概述
- Jeroen Vermeulen的基础知识简介
- Qt文档中的D-Bus简介
- 常问问题
- 概述图片PNG SVG
- D-Bus教程(不完整,包含一些绑定和重新实现的内容)
- 配置文件DTD
- 如果您对DBus中的某些概念感到困惑,请看一些类比
- 一些用于D-Bus的工具。请注意,D-Bus规范不完整,尤其是在其对消息总线守护程序的描述中。该协议本身的规范相当完整,尽管并不总是清晰或精确的。欢迎您的补丁!同时,您可能需要阅读参考实现源代码来补充对规范的阅读。
特定于参考实现dbus的文档:
- API参考手册,用于参考实现(libdbus)
- 参考实施手册和高级TODO中的@todo项目
- 浏览参考实现源
- dbus-daemon(1)(包括配置文件docs)
- dbus发送(1) dbus监控(1) dbus启动(1) dbus-uuidgen(1)
- HACKING 作者
- 新闻 ChangeLog 自述文件
- 测试计划
- Tarball包含以上大多数文档。请记住,libdbus是一个低级库,旨在用作语言绑定的后端,并具有实现dbus-daemon所需的额外复杂性。如果使用更高级别的包装程序或重新实现,将为您省去很多麻烦。这些文档通常从绑定页面链接。
网络上的文章,包括一些教程:
- Ross Burton的“使用D-BUS连接桌面应用程序”(IBM developerWorks)(2004年7月)
- John Palmieri的“ Get on D-BUS”(红帽杂志)(2005年1月)
- 罗伯特·洛夫(Robert Love )的“ Get on the D-BUS”(Linux期刊)(2005年1月)
- RaphaÃlSlinckx撰写的“ DBus缺少的教程-DBus激活”(2005年)
- 马修·约翰逊(Matthew Johnson)的D-Bus低级API教程(2005年11月)
- Aaron Seigo和KDE社区的D-BUS简介(2007)
- Rony G. Flatscher撰写的“ ooRexx的D-Bus语言绑定简介”(2011年12月,D-Bus概念简介和ooRexx绑定简介)