基本信息
D-Bus是针对桌面环境优化的IPC(interprocess communication )机制,用于进程间的通信或进程与内核的通信。最基本的D-Bus协议是一对一的通信协议。但在很多情况下,通信的一方是消息总线。消息总线是一个特殊的应用,它同时与多个应用通信,并在应用之间传递消息。下面我们会在实例中观察消息总线的作用。消息总线的角色有点类似与X系统中的窗口管理器,窗口管理器既是X客户,又负责管理窗口。
基本名词有
bus type:有两种类型的bus,system和session
bus name :一个Bus Name总是代表一个应用和消息总线的连接。比如org.fmddlmyy.Test
object path:比如org.fmddlmyy.Test总线下的/TestObj
interface: 比如/TestObj对象下的org.fmddlmyy.Test.Basic
method:比如org.fmddlmyy.Test.Basic接口下的Add方法
框架对给每个对象自动添加一个标准接口org.freedesktop.DBus.Introspectable,这个标准接口下有一个方法Introspect。调用Introspect方法可以返回所有接口的xml描述。
命令测试
linux下主要有如下命令来测试
dbus-send
dbus-monitor
另外还有一个python实现的gui界面 d-feet
遍历dbus上的所有对象
root@ubuntu:/opt/ss/dteeth# dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListNames
method return sender=org.freedesktop.DBus -> dest=:1.204 reply_serial=2
array [
string "org.freedesktop.DBus"
string "org.freedesktop.Notifications"
string "org.fmddlmyy.Test"
]
向某个对象发送消息
root@ubuntu:/opt/ss/dteeth# dbus-send --session --type=method_call --print-reply --dest=org.fmddlmyy.Test / org.freedesktop.DBus.Introspectable.Introspect
method return sender=:1.197 -> dest=:1.210 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>
"
root@ubuntu:/opt/ss/dteeth# dbus-send --session --type=method_call --print-reply --dest=org.fmddlmyy.Test/TestObj org.freedesktop.DBus.Introspectable.Introspect
method return sender=:1.197 -> dest=:1.211 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>
"
root@ubuntu:/opt/ss/dteeth# 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.96 -> dest=:1.160 reply_serial=2
int32 1099
Usage: dbus-send [--help] [--system | --session | --address=ADDRESS] [--dest=NAME] [--type=TYPE] [--print-reply=(literal)] [--reply-timeout=MSEC] <destination object path> <message name> [contents ...]
destination object path指的是object path
message name指的是 interface / method
root@imx6ulevk:~# dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListNames
method return sender=org.freedesktop.DBus -> dest=:1.29 reply_serial=2
array [
string "org.freedesktop.DBus"
string ":1.3"
string "org.freedesktop.Avahi"
string ":1.0"
string ":1.29"
string "org.bluez"
string ":1.1"
string ":1.2"
string "org.ofono"
]
如果出现如下error
Failed to open connection to session bus: Using X11 for dbus-daemon autolaunch was disabled at compile time, set your DBUS_SESSION_BUS_ADDRESS instead
执行如下命令来添加一个环境变量DBUS_SESSION_BUS_ADDRESS
export DBUS_SESSION_BUS_ADDRESS="unix:path=/var/run/dbus/system_bus_socket"
root@imx6ulevk:/mnt# dbus-send --session --type=method_call --print-reply --dest=org.bluez / org.freedesktop.DBus.Introspectable.Introspect
method return sender=:1.15 -> dest=:1.27 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="xml" type="s" direction="out"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.ObjectManager">
<method name="GetManagedObjects">
<arg name="objects" type="a{oa{sa{sv}}}" direction="out"/>
</method>
<signal name="InterfacesAdded">
<arg name="object" type="o"/>
<arg name="interfaces" type="a{sa{sv}}"/>
</signal>
<signal name="InterfacesRemoved">
<arg name="object" type="o"/>
<arg name="interfaces" type="as"/>
</signal>
</interface>
<node name="org"/></node>"
root@imx6ulevk:/mnt# dbus-send --session --type=method_call --print-reply --dest=org.bluez /org org.freedesktop.DBus.Introspectable.Introspect
method return sender=:1.15 -> dest=:1.28 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>
"
root@imx6ulevk:/mnt# dbus-send --session --type=method_call --print-reply --dest=org.bluez /org/bluez org.freedesktop.DBus.Introspectable.Introspect
method return sender=:1.15 -> dest=:1.29 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="xml" type="s" direction="out"/>
</method>
</interface>
<interface name="org.bluez.AgentManager1">
<method name="RegisterAgent">
<arg name="agent" type="o" direction="in"/>
<arg name="capability" type="s" direction="in"/>
</method>
<method name="UnregisterAgent">
<arg name="agent" type="o" direction="in"/>
</method>
<method name="RequestDefaultAgent">
<arg name="agent" type="o" direction="in"/>
</method>
</interface>
<interface name="org.bluez.ProfileManager1">
<method name="RegisterProfile">
<arg name="profile" type="o" direction="in"/>
<arg name="UUID" type="s" direction="in"/>
<arg name="options" type="a{sv}" direction="in"/>
</method>
<method name="UnregisterProfile">
<arg name="profile" type="o" direction="in"/>
</method>
</interface>
<node name="hci0"/></node>"
dbus-send --session --type=method_call --print-reply --dest=org.bluez /org/bluez/hci0 org.freedesktop.DBus.Introspectable.Introspect
method return sender=:1.15 -> dest=:1.36 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="xml" type="s" direction="out"/>
</method>
</interface>
<interface name="org.bluez.Adapter1">
<method name="StartDiscovery"></method>
<method name="StopDiscovery"></method>
<method name="RemoveDevice">
<arg name="device" type="o" direction="in"/>
</method>
<property name="Address" type="s" access="read"></property>
<property name="Name" type="s" access="read"></property>
<property name="Alias" type="s" access="readwrite"></property>
<property name="Class" type="u" access="read"></property>
<property name="Powered" type="b" access="readwrite"></property>
<property name="Discoverable" type="b" access="readwrite"></property>
<property name="DiscoverableTimeout" type="u" access="readwrite"></property>
<property name="Pairable" type="b" access="readwrite"></property>
<property name="PairableTimeout" type="u" access="readwrite"></property>
<property name="Discovering" type="b" access="read"></property>
<property name="UUIDs" type="as" access="read"></property>
<property name="Modalias" type="s" access="read"></property>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg name="interface" type="s" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="value" type="v" direction="out"/>
</method>
<method name="Set">
<arg name="interface" type="s" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
</method>
<method name="GetAll">
<arg name="interface" type="s" direction="in"/>
<arg name="properties" type="a{sv}" direction="out"/>
</method>
<signal name="PropertiesChanged">
<arg name="interface" type="s"/>
<arg name="changed_properties" type="a{sv}"/>
<arg name="invalidated_properties" type="as"/>
</signal>
</interface>
<interface name="org.bluez.Media1">
<method name="RegisterEndpoint">
<arg name="endpoint" type="o" direction="in"/>
<arg name="properties" type="a{sv}" direction="in"/>
</method>
<method name="UnregisterEndpoint">
<arg name="endpoint" type="o" direction="in"/>
</method>
<method name="RegisterPlayer">
<arg name="player" type="o" direction="in"/>
<arg name="properties" type="a{sv}" direction="in"/>
</method>
<method name="UnregisterPlayer">
<arg name="player" type="o" direction="in"/>
</method>
</interface>
<interface name="org.bluez.NetworkServer1">
<method name="Register">
<arg name="uuid" type="s" direction="in"/>
<arg name="bridge" type="s" direction="in"/>
</method>
<method name="Unregister">
<arg name="uuid" type="s" direction="in"/>
</method>
</interface>
<node name="dev_00_1A_7D_DA_71_04"/>
<node name="dev_00_1A_7D_DA_71_0D"/>
<node name="dev_10_2A_27_34_19_D4"/>
<node name="dev_58_71_33_33_71_7D"/>
<node name="dev_68_A0_F6_B8_EB_09"/>
<node name="dev_68_A0_F6_E3_99_CB"/>
<node name="dev_F4_8E_92_78_83_70"/></node>"
dbus-send --session --type=method_call --print-reply --dest=org.bluez /org/bluez/hci0/dev_58_71_33_33_71_7D org.freedesktop.DBus.Introspectable.Introspect
method return sender=:1.15 -> dest=:1.37 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="xml" type="s" direction="out"/>
</method>
</interface>
<interface name="org.bluez.Device1">
<method name="Disconnect"></method>
<method name="Connect"></method>
<method name="ConnectProfile">
<arg name="UUID" type="s" direction="in"/>
</method>
<method name="DisconnectProfile">
<arg name="UUID" type="s" direction="in"/>
</method>
<method name="Pair"></method>
<method name="CancelPairing"></method>
<property name="Address" type="s" access="read"></property>
<property name="Name" type="s" access="read"></property>
<property name="Alias" type="s" access="readwrite"></property>
<property name="Class" type="u" access="read"></property>
<property name="Appearance" type="q" access="read"></property>
<property name="Icon" type="s" access="read"></property>
<property name="Paired" type="b" access="read"></property>
<property name="Trusted" type="b" access="readwrite"></property>
<property name="Blocked" type="b" access="readwrite"></property>
<property name="LegacyPairing" type="b" access="read"></property>
<property name="RSSI" type="n" access="read"></property>
<property name="Connected" type="b" access="read"></property>
<property name="UUIDs" type="as" access="read"></property>
<property name="Modalias" type="s" access="read"></property>
<property name="Adapter" type="o" access="read"></property>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg name="interface" type="s" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="value" type="v" direction="out"/>
</method>
<method name="Set">
<arg name="interface" type="s" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
</method>
<method name="GetAll">
<arg name="interface" type="s" direction="in"/>
<arg name="properties" type="a{sv}" direction="out"/>
</method>
<signal name="PropertiesChanged">
<arg name="interface" type="s"/>
<arg name="changed_properties" type="a{sv}"/>
<arg name="invalidated_properties" type="as"/>
</signal>
</interface>
<interface name="org.bluez.Input1">
<property name="ReconnectMode" type="s" access="read"></property>
</interface>
</node>"
dbus-monitor
用于监视dbus上消息
c库代码
//example-service.c
#include <dbus/dbus-glib.h>
#include <stdio.h>
#include <stdlib.h>
static void lose (const char *fmt, ...) G_GNUC_NORETURN G_GNUC_PRINTF (1, 2);
static void lose_gerror (const char *prefix, GError *error) G_GNUC_NORETURN;
static void
lose (const char *str, ...)
{
va_list args;
va_start (args, str);
vfprintf (stderr, str, args);
fputc ('\n', stderr);
va_end (args);
exit (1);
}
static void
lose_gerror (const char *prefix, GError *error)
{
lose ("%s: %s", prefix, error->message);
}
typedef struct TestObj TestObj;
typedef struct TestObjClass TestObjClass;
GType test_obj_get_type (void);
struct TestObj
{
GObject parent;
};
struct TestObjClass
{
GObjectClass parent;
};
#define TEST_TYPE_OBJECT (test_obj_get_type ())
#define TEST_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TEST_TYPE_OBJECT, TestObj))
#define TEST_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_OBJECT, TestObjClass))
#define TEST_IS_OBJECT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TEST_TYPE_OBJECT))
#define TEST_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_OBJECT))
#define TEST_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_OBJECT, TestObjClass))
G_DEFINE_TYPE(TestObj, test_obj, G_TYPE_OBJECT)
gboolean test_obj_add (TestObj *obj, int num1, int num2, int *sum, GError **error);
#include "example-service-glue.h"
static void test_obj_init (TestObj *obj)
{
}
static void test_obj_class_init (TestObjClass *klass)
{
}
gboolean test_obj_add (TestObj *obj, int num1, int num2, int *sum, GError **error)
{
*sum = num1 + num2;
return TRUE;
}
int main (int argc, char **argv)
{
DBusGConnection *bus;
DBusGProxy *bus_proxy;
GError *error = NULL;
TestObj *obj;
GMainLoop *mainloop;
guint request_name_result;
g_type_init ();
{
GLogLevelFlags fatal_mask;
fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
g_log_set_always_fatal (fatal_mask);
}
dbus_g_object_type_install_info (TEST_TYPE_OBJECT, &dbus_glib_test_obj_object_info);
mainloop = g_main_loop_new (NULL, FALSE);
bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
if (!bus){
lose_gerror ("Couldn't connect to session bus", error);
}
bus_proxy = dbus_g_proxy_new_for_name (bus, "org.freedesktop.DBus","/org/freedesktop/DBus", "org.freedesktop.DBus");
if (!dbus_g_proxy_call (bus_proxy, "RequestName", &error,
G_TYPE_STRING, "org.fmddlmyy.Test",
G_TYPE_UINT, 0,
G_TYPE_INVALID,
G_TYPE_UINT, &request_name_result,
G_TYPE_INVALID))
{
lose_gerror ("Failed to acquire org.fmddlmyy.Test", error);
}
obj = g_object_new (TEST_TYPE_OBJECT, NULL);
dbus_g_connection_register_g_object (bus, "/TestObj", G_OBJECT (obj));
printf ("service running\n");
g_main_loop_run (mainloop);
exit (0);
}
dbus_g_bus_get:创建一个总线
dbus_g_proxy_new_for_name:根据参数会创建org.freedesktop.DBus总线的/org/freedesktop/DBus对象的接口方法org.freedesktop.DBus的代理
dbus_g_proxy_call:执行代理函数,将org.fmddlmyy.Test总线注册上
dbus_g_connection_register_g_object:将对象/TestObj注册到总线org.fmddlmyy.Test
//example-client.c
#include <dbus/dbus-glib.h>
#include <stdio.h>
#include <stdlib.h>
static void lose (const char *fmt, ...) G_GNUC_NORETURN G_GNUC_PRINTF (1, 2);
static void lose_gerror (const char *prefix, GError *error) G_GNUC_NORETURN;
static void
lose (const char *str, ...)
{
va_list args;
va_start (args, str);
vfprintf (stderr, str, args);
fputc ('\n', stderr);
va_end (args);
exit (1);
}
static void
lose_gerror (const char *prefix, GError *error)
{
lose ("%s: %s", prefix, error->message);
}
static void
print_hash_value (gpointer key, gpointer val, gpointer data)
{
printf ("%s -> %s\n", (char *) key, (char *) val);
}
int
main (int argc, char **argv)
{
DBusGConnection *bus;
DBusGProxy *remote_object;
DBusGProxy *remote_object_introspectable;
GError *error = NULL;
char *introspect_data;
guint i;
gint sum;
g_type_init ();
{
GLogLevelFlags fatal_mask;
fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
g_log_set_always_fatal (fatal_mask);
}
bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
if (!bus)
{
lose_gerror ("Couldn't connect to session bus", error);
}
remote_object = dbus_g_proxy_new_for_name (bus,"org.fmddlmyy.Test","/TestObj","org.fmddlmyy.Test.Basic");
if (!dbus_g_proxy_call (remote_object, "Add", &error,
G_TYPE_INT, 100, G_TYPE_INT, 999, G_TYPE_INVALID,
G_TYPE_INT, &sum, G_TYPE_INVALID))
{
lose_gerror ("Failed to call Add", error);
}
printf("sum is %d\n", sum);
g_object_unref (G_OBJECT (remote_object));
remote_object_introspectable = dbus_g_proxy_new_for_name (bus,"org.fmddlmyy.Test","/TestObj","org.freedesktop.DBus.Introspectable");
if (!dbus_g_proxy_call (remote_object_introspectable, "Introspect", &error,
G_TYPE_INVALID,
G_TYPE_STRING, &introspect_data, G_TYPE_INVALID)){
lose_gerror ("Failed to complete Introspect", error);
}
//printf ("%s", introspect_data);
g_free (introspect_data);
g_object_unref (G_OBJECT (remote_object_introspectable));
exit(0);
}
调用某个总线的服务很简单,基本是4个函数:
dbus_g_bus_get:获得总线
dbus_g_proxy_new_for_name:创建代理某个总线的某个接口的某个方法的代理
dbus_g_proxy_call:调用代理
g_object_unref :释放代理
编译时需要链接Glib库
root@ubuntu:/opt/ss/hello-dbus3-0.1/src# make
mode=execute dbus-binding-tool --prefix=test_obj --mode=glib-server --output=example-service-glue.h ./example-service.xml
make all-am
make[1]: 正在进入目录 `/opt/ss/hello-dbus3-0.1/src'
gcc -DHAVE_CONFIG_H -I. -I.. -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include -pthread -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -DDBUS_COMPILATION -g -O2 -MT example-service.o -MD -MP -MF .deps/example-service.Tpo -c -o example-service.o example-service.c
mv -f .deps/example-service.Tpo .deps/example-service.Po
gcc -g -O2 -o example-service example-service.o -L/lib -ldbus-1 -lpthread -lrt -pthread -lgobject-2.0 -lgthread-2.0 -lrt -lglib-2.0 -ldbus-glib-1
gcc -DHAVE_CONFIG_H -I. -I.. -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include -pthread -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -DDBUS_COMPILATION -g -O2 -MT example-client.o -MD -MP -MF .deps/example-client.Tpo -c -o example-client.o example-client.c
mv -f .deps/example-client.Tpo .deps/example-client.Po
gcc -g -O2 -o example-client example-client.o -L/lib -ldbus-1 -lpthread -lrt -pthread -lgobject-2.0 -lgthread-2.0 -lrt -lglib-2.0 -ldbus-glib-1
make[1]:正在离开目录 `/opt/ss/hello-dbus3-0.1/src'
源码
参考
http://blog.youkuaiyun.com/fmddlmyy/article/details/3585730
http://www.fmddlmyy.cn/text52.html
https://en.wikipedia.org/wiki/D-Bus
https://www.freedesktop.org/wiki/Software/dbus/