应用程序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协议是一对一的通信协议。与直接使用socket不同,D-Bus是面向消息的协议。 D-Bus的所有功能都是通过在连接上流动的消息完成的。
D-Bus有四种类型的消息:
前面介绍的远程方法调用就用到了method_call和method_return消息。顾名思义,在发生错误时会产生error消息。 如果把method_call看作打电话,那么signal消息就是来电了。后面还会详细讨论。
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。
消息总线是一个特殊的应用,它可以在与它连接的应用之间传递消息。 可以把消息总线看作一台路由器。正是通过消息总线,D-Bus才在一对一的通信协议基础上实现了多对一和一对多的通信。
消息总线虽然有特殊的转发功能,但消息总线也还是一个应用。 其它应用与消息总线的通信也是通过1.1节的基本消息类型完成的。作为一个应用,消息总线也提供了自己的接口,包括方法和信号。
我们可以通过向连接“org.freedesktop.DBus ”上对象“/”发送消息来调用消息总线提供的方法。 事实上,应用程序正是通过这些方法连接到消息总线上的其它应用,完成请求公共名等工作的。
在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 "1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
">
">
" type=" s"/>
">
in " type="s"/>
in " type="u"/>
" type=" u"/>
">
in " type="s"/>
" type=" u"/>
">
in " type="s"/>
in " type="u"/>
" type=" u"/>
">
in " type="a{ss}"/>
">
in " type="s"/>
" type=" b"/>
">
" type=" as"/>
">
" type=" as"/>
">
in " type="s"/>
">
in " type="s"/>
">
in " type="s"/>
" type=" s"/>
">
in " type="s"/>
" type=" as"/>
">
in " type="s"/>
" type=" u"/>
">
in " type="s"/>
" type=" u"/>
">
in " type="s"/>
" type=" ay"/>
">
in " type="s"/>
" type=" ay"/>
">
">
" type=" s"/>
">
in " type="s"/>
" type=" a{sv}"/>
" type=" as" access="read">
" value=" const"/>
" type=" as" access="read">
" value=" const"/>
">
"/>
"/>
"/>
">
"/>
">
"/>
">
">
" type=" s"/>
">
">
" type=" s"/>
">
"/>
"
从输出可以看到会话总线对象支持标准接口“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 "1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
">
"/>
">
" name=" machine_uuid" direction="out"/>
">
">
" type=" s" direction="out"/>
">
">
" direction=" in" type="s"/>
" direction=" in" type="s"/>
" direction=" out" type="v"/>
">
" direction=" in" type="s"/>
" direction=" out" type="a{sv}"/>
">
" direction=" in" type="s"/>
" direction=" in" type="s"/>
" direction=" in" type="v"/>
">
" name=" interface"/>
{ sv}" name="changed_properties"/>
" name=" invalidated_properties"/>
">
">
{ oa{sa{sv}}}" name="object_paths_interfaces_and_properties" direction="out"/>
">
" name=" object_path"/>
{ sa{sv}}" name="interfaces_and_properties"/>
">
" name=" object_path"/>
" name=" interfaces"/>
"/>
"
如上,他的根目录下有一个子节点,查找信息如下,获取到的接口信息与在根目录下获取的信息一致。
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 "1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
">
"/>
">
" name=" machine_uuid" direction="out"/>
">
">
" type=" s" direction="out"/>
">
">
" direction=" in" type="s"/>
" direction=" in" type="s"/>
" direction=" out" type="v"/>
">
" direction=" in" type="s"/>
" direction=" out" type="a{sv}"/>
">
" direction=" in" type="s"/>
" direction=" in" type="s"/>
" direction=" in" type="v"/>
">
" name=" interface"/>
{ sv}" name="changed_properties"/>
" name=" invalidated_properties"/>
"/>
"
这里使用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协议信息:
特定于参考实现dbus的文档:
网络上的文章,包括一些教程: