D-Bus(Desktop Bus)
概述
D-Bus(Desktop Bus)是一种高级的进程间通信机制,主要用于Linux桌面环境和系统服务之间的通信。D-Bus提供了一个消息总线系统,允许应用程序之间进行异步通信,支持方法调用和信号等通信模式。此外,D-Bus还提供了标准化的属性访问机制,通过 org.freedesktop.DBus.Properties 接口的方法调用来实现对象属性的读取和设置。
Ubuntu/Debian 默认集成
D-Bus 在 Ubuntu 和其他主流 Linux 发行版中默认已集成:
- 系统服务 :
dbus-daemon守护进程默认运行,管理系统总线和会话总线 - 运行时库:libdbus-1 运行时库通常已安装
- 开发库:如需开发 D-Bus 应用程序,需要安装开发库:
bash
# Ubuntu/Debian
sudo apt-get install libdbus-1-dev
# 验证安装
# 方法1:使用 pkg-config(推荐)
pkg-config --exists dbus-1 && echo "DBus开发库已安装" || echo "DBus开发库未安装"
# 方法2:检查开发库包是否安装
dpkg -l | grep -q "libdbus-1-dev" && echo "DBus开发库已安装" || echo "DBus开发库未安装"
# 方法3:检查头文件是否存在
test -f /usr/include/dbus-1.0/dbus/dbus.h && echo "DBus开发库已安装" || echo "DBus开发库未安装"
检查 D-Bus 服务状态
bash
# 检查系统总线
dbus-send --system --print-reply --dest=org.freedesktop.DBus \
/org/freedesktop/DBus org.freedesktop.DBus.ListNames
# 检查会话总线
dbus-send --session --print-reply --dest=org.freedesktop.DBus \
/org/freedesktop/DBus org.freedesktop.DBus.ListNames
通信原理
基本概念
D-Bus的特点:
-
消息总线架构:提供系统总线和会话总线两种总线
- 系统总线:系统级服务,所有用户共享
- 会话总线:用户会话级服务,每个用户会话独立
-
对象模型:基于对象、接口、方法的面向对象模型
-
异步通信:支持异步方法调用和信号通知
-
类型系统:强类型系统,支持多种数据类型
-
服务发现:支持服务注册和发现机制
实现机制
-
总线守护进程:
- D-Bus守护进程(dbus-daemon)管理总线
- 负责消息路由、服务注册、权限控制
-
消息类型:
- 方法调用(Method Call):RPC调用,需要回复
- 方法返回(Method Return):方法调用的返回值
- 信号(Signal):事件通知,不需要回复
- 错误(Error):错误响应
-
通信流程:
应用程序A ──> [D-Bus守护进程] ──> 应用程序B (消息路由) -
对象路径 :使用类似文件系统的路径标识对象(如
/org/freedesktop/NetworkManager) -
接口名称 :使用反向域名格式(如
org.freedesktop.DBus.Introspectable)
标准接口:org.freedesktop.DBus.Properties
org.freedesktop.DBus.Properties 是 D-Bus 规范定义的标准接口,用于统一访问对象的属性。该接口提供了标准化的属性访问机制,使得客户端可以通过统一的方式访问不同对象的属性。
接口说明
- 接口名 :
org.freedesktop.DBus.Properties - 对象路径:任何实现了属性的对象都可以提供此接口
- 目的:提供标准化的属性访问方法
主要方法
-
Get:获取属性值
- 参数 :
interface_name(string):属性所属的接口名property_name(string):属性名
- 返回值 :
variant类型,包含属性值 - 说明:返回指定接口的指定属性值,属性值包装在 variant 中
- 参数 :
-
Set:设置属性值
- 参数 :
interface_name(string):属性所属的接口名property_name(string):属性名value(variant):新的属性值
- 返回值:无(成功)或错误
- 说明:设置指定接口的指定属性值,新值需要包装在 variant 中
- 参数 :
-
GetAll:获取所有属性
- 参数 :
interface_name(string):接口名
- 返回值 :
a{sv}类型(字典,键为属性名,值为 variant 类型的属性值) - 说明:返回指定接口的所有属性
- 参数 :
属性变化通知
当属性值发生变化时,对象可以通过发送信号来通知客户端:
- 信号名 :
PropertiesChanged - 接口 :
org.freedesktop.DBus.Properties - 参数 :
interface_name(string):发生变化的接口名changed_properties(a{sv}):发生变化的属性字典invalidated_properties(as):失效的属性名数组(可选)
使用场景
- 统一访问:不同对象可以使用相同的接口访问属性,简化客户端代码
- 类型安全:属性值通过 variant 类型传递,保证类型安全
- 标准化:符合 D-Bus 规范,提高互操作性
- 动态发现:可以通过 GetAll 方法动态获取对象的所有属性
实现要求
实现属性的对象应该:
- 在对象上提供
org.freedesktop.DBus.Properties接口 - 实现
Get、Set和GetAll方法 - 在属性变化时发送
PropertiesChanged信号(可选但推荐)
API说明
头文件和链接库
c
#include <dbus/dbus.h> // D-Bus 头文件
编译时需要链接 libdbus-1 库:
bash
gcc -o program program.c `pkg-config --cflags --libs dbus-1`
主要API函数
连接管理
-
dbus_bus_get():连接到系统总线或会话总线cDBusConnection *dbus_bus_get(DBusBusType type, DBusError *error);type:DBUS_BUS_SESSION(会话总线)或DBUS_BUS_SYSTEM(系统总线)- 返回连接对象,失败返回 NULL
-
dbus_connection_unref():释放连接cvoid dbus_connection_unref(DBusConnection *connection);
服务端API
-
dbus_connection_register_object_path():注册对象路径(服务端)cdbus_bool_t dbus_connection_register_object_path( DBusConnection *connection, const char *path, // 对象路径 const DBusObjectPathVTable *vtable, // 虚拟函数表 void *user_data // 用户数据 );- 用于服务端注册对象路径,当有消息发送到该路径时,会调用
vtable中的处理函数 vtable包含message_function(处理方法调用)和unregister_function(注销函数,可为 NULL)- 成功返回
TRUE,失败返回FALSE - 注意 :必须先请求服务名(
RequestName)再注册对象路径
- 用于服务端注册对象路径,当有消息发送到该路径时,会调用
-
dbus_bus_request_name():请求服务名(服务端)cdbus_uint32_t dbus_bus_request_name( DBusConnection *connection, const char *name, // 服务名 dbus_uint32_t flags, // 标志位 DBusError *error );- 用于服务端请求在总线上的唯一服务名
flags:DBUS_NAME_FLAG_REPLACE_EXISTING(如果已存在则替换)、DBUS_NAME_FLAG_DO_NOT_QUEUE(如果已存在则不排队)等- 返回值:
DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER(成功)、DBUS_REQUEST_NAME_REPLY_IN_QUEUE(排队中)等 - 注意 :也可以通过调用
org.freedesktop.DBus.RequestName方法实现
错误处理
-
dbus_error_init():初始化错误对象cvoid dbus_error_init(DBusError *error); -
dbus_error_is_set():检查是否有错误cdbus_bool_t dbus_error_is_set(DBusError *error); -
dbus_error_free():释放错误对象cvoid dbus_error_free(DBusError *error);
方法调用
-
dbus_message_new_method_call():创建方法调用消息cDBusMessage *dbus_message_new_method_call( const char *bus_name, // 服务名 const char *object_path, // 对象路径 const char *interface, // 接口名 const char *method // 方法名 ); -
dbus_message_append_args():添加方法参数cdbus_bool_t dbus_message_append_args( DBusMessage *message, int first_arg_type, // 第一个参数类型 ... // 参数值和类型对 ); -
dbus_connection_send_with_reply():发送方法调用并等待回复cdbus_bool_t dbus_connection_send_with_reply( DBusConnection *connection, DBusMessage *message, DBusPendingCall **pending_return, int timeout_milliseconds ); -
dbus_pending_call_block():阻塞等待回复cvoid dbus_pending_call_block(DBusPendingCall *pending); -
dbus_pending_call_steal_reply():获取回复消息cDBusMessage *dbus_pending_call_steal_reply(DBusPendingCall *pending); -
dbus_message_get_args():从回复消息中读取返回值cdbus_bool_t dbus_message_get_args( DBusMessage *message, DBusError *error, int first_arg_type, // 第一个返回值类型 ... // 返回值指针和类型对 );
信号处理
-
dbus_message_new_signal():创建信号消息cDBusMessage *dbus_message_new_signal( const char *path, // 对象路径 const char *interface, // 接口名 const char *name // 信号名 ); -
dbus_connection_send():发送信号(不需要回复)cdbus_bool_t dbus_connection_send( DBusConnection *connection, DBusMessage *message, dbus_uint32_t *serial ); -
dbus_bus_add_match():添加信号匹配规则cdbus_bool_t dbus_bus_add_match( DBusConnection *connection, const char *rule, // 匹配规则字符串 DBusError *error ); -
dbus_connection_add_filter():注册信号处理函数cdbus_bool_t dbus_connection_add_filter( DBusConnection *connection, DBusHandleMessageFunction function, void *user_data, DBusFreeFunction free_data_function ); -
dbus_connection_read_write_dispatch():处理消息(事件循环)cdbus_bool_t dbus_connection_read_write_dispatch( DBusConnection *connection, int timeout_milliseconds );
消息管理
-
dbus_message_unref():释放消息对象cvoid dbus_message_unref(DBusMessage *message); -
dbus_connection_flush():刷新连接,确保消息发送cvoid dbus_connection_flush(DBusConnection *connection);
示例代码
所有示例代码位于 examples/09-dbus/ 目录,可直接编译运行。
示例 1:发送方法调用
c
#include <dbus/dbus.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
DBusConnection *connection;
DBusError error;
DBusMessage *message;
DBusMessage *reply;
DBusPendingCall *pending;
char *result;
// 初始化错误
dbus_error_init(&error);
// 连接到会话总线
connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error)) {
fprintf(stderr, "Connection error: %s\n", error.message);
dbus_error_free(&error);
return 1;
}
// 创建方法调用消息
message = dbus_message_new_method_call(
"com.example.Service", // 服务名
"/com/example/Object", // 对象路径
"com.example.Interface", // 接口名
"MethodName" // 方法名
);
if (message == NULL) {
fprintf(stderr, "Failed to create message\n");
dbus_connection_unref(connection);
return 1;
}
// 添加参数
const char *arg = "argument";
if (!dbus_message_append_args(message,
DBUS_TYPE_STRING, &arg,
DBUS_TYPE_INVALID)) {
fprintf(stderr, "Failed to append arguments\n");
dbus_message_unref(message);
dbus_connection_unref(connection);
return 1;
}
// 发送消息并等待回复
if (!dbus_connection_send_with_reply(connection, message,
&pending, -1)) {
fprintf(stderr, "Failed to send message\n");
dbus_message_unref(message);
dbus_connection_unref(connection);
return 1;
}
dbus_connection_flush(connection);
dbus_message_unref(message);
// 等待回复
dbus_pending_call_block(pending);
reply = dbus_pending_call_steal_reply(pending);
dbus_pending_call_unref(pending);
if (reply == NULL) {
fprintf(stderr, "No reply received\n");
dbus_connection_unref(connection);
return 1;
}
// 读取返回值
if (dbus_message_get_args(reply, &error,
DBUS_TYPE_STRING, &result,
DBUS_TYPE_INVALID)) {
printf("Result: %s\n", result);
dbus_free(result);
} else {
fprintf(stderr, "Failed to get reply arguments\n");
}
dbus_message_unref(reply);
dbus_connection_unref(connection);
return 0;
}
示例 2:监听信号
c
#include <dbus/dbus.h>
#include <stdio.h>
// 信号处理函数
DBusHandlerResult signal_handler(DBusConnection *connection,
DBusMessage *message,
void *user_data) {
if (dbus_message_is_signal(message,
"com.example.Interface",
"SignalName")) {
char *data;
if (dbus_message_get_args(message, NULL,
DBUS_TYPE_STRING, &data,
DBUS_TYPE_INVALID)) {
printf("Received signal: %s\n", data);
dbus_free(data);
}
}
return DBUS_HANDLER_RESULT_HANDLED;
}
int main(void) {
DBusConnection *connection;
DBusError error;
dbus_error_init(&error);
connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error)) {
fprintf(stderr, "Connection error: %s\n", error.message);
dbus_error_free(&error);
return 1;
}
// 添加信号匹配规则
dbus_bus_add_match(connection,
"type='signal',interface='com.example.Interface'",
&error);
if (dbus_error_is_set(&error)) {
fprintf(stderr, "Failed to add match: %s\n", error.message);
dbus_error_free(&error);
dbus_connection_unref(connection);
return 1;
}
// 注册信号处理函数
dbus_connection_add_filter(connection, signal_handler, NULL, NULL);
// 进入事件循环
printf("Listening for signals...\n");
while (dbus_connection_read_write_dispatch(connection, -1)) {
// 处理消息
}
dbus_connection_unref(connection);
return 0;
}
示例 3:发送信号
c
#include <dbus/dbus.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
DBusConnection *connection;
DBusError error;
DBusMessage *signal;
dbus_error_init(&error);
connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error)) {
fprintf(stderr, "Connection error: %s\n", error.message);
dbus_error_free(&error);
return 1;
}
// 创建信号消息
signal = dbus_message_new_signal(
"/com/example/Object", // 对象路径
"com.example.Interface", // 接口名
"SignalName" // 信号名
);
if (signal == NULL) {
fprintf(stderr, "Failed to create signal\n");
dbus_connection_unref(connection);
return 1;
}
// 添加信号数据
const char *data = "signal data";
if (!dbus_message_append_args(signal,
DBUS_TYPE_STRING, &data,
DBUS_TYPE_INVALID)) {
fprintf(stderr, "Failed to append signal arguments\n");
dbus_message_unref(signal);
dbus_connection_unref(connection);
return 1;
}
// 发送信号
if (!dbus_connection_send(connection, signal, NULL)) {
fprintf(stderr, "Failed to send signal\n");
dbus_message_unref(signal);
dbus_connection_unref(connection);
return 1;
}
dbus_connection_flush(connection);
dbus_message_unref(signal);
dbus_connection_unref(connection);
printf("Signal sent\n");
return 0;
}
示例 4:属性访问(Get)
c
#include <dbus/dbus.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
DBusConnection *connection;
DBusError error;
DBusMessage *message;
DBusMessage *reply;
DBusPendingCall *pending;
DBusMessageIter iter, variant_iter;
int value;
dbus_error_init(&error);
connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error)) {
fprintf(stderr, "Connection error: %s\n", error.message);
dbus_error_free(&error);
return 1;
}
// 创建方法调用:获取属性
message = dbus_message_new_method_call(
"com.example.Service",
"/com/example/Object",
"org.freedesktop.DBus.Properties", // 标准属性接口
"Get" // Get 方法
);
// 添加参数:接口名和属性名
const char *interface = "com.example.Interface";
const char *property = "PropertyName";
if (!dbus_message_append_args(message,
DBUS_TYPE_STRING, &interface,
DBUS_TYPE_STRING, &property,
DBUS_TYPE_INVALID)) {
fprintf(stderr, "Failed to append arguments\n");
dbus_message_unref(message);
dbus_connection_unref(connection);
return 1;
}
// 发送并等待回复
if (!dbus_connection_send_with_reply(connection, message, &pending, -1)) {
fprintf(stderr, "Failed to send message\n");
dbus_message_unref(message);
dbus_connection_unref(connection);
return 1;
}
dbus_connection_flush(connection);
dbus_message_unref(message);
dbus_pending_call_block(pending);
reply = dbus_pending_call_steal_reply(pending);
dbus_pending_call_unref(pending);
if (reply == NULL) {
fprintf(stderr, "No reply received\n");
dbus_connection_unref(connection);
return 1;
}
// 读取返回值(属性值包装在 variant 中)
if (dbus_message_iter_init(reply, &iter)) {
dbus_message_iter_recurse(&iter, &variant_iter);
dbus_message_iter_get_basic(&variant_iter, &value);
printf("Property value: %d\n", value);
}
dbus_message_unref(reply);
dbus_connection_unref(connection);
return 0;
}
示例 5:属性访问(Set)
c
#include <dbus/dbus.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
DBusConnection *connection;
DBusError error;
DBusMessage *message;
DBusMessage *reply;
DBusPendingCall *pending;
DBusMessageIter iter, variant_iter;
int new_value = 42;
dbus_error_init(&error);
connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error)) {
fprintf(stderr, "Connection error: %s\n", error.message);
dbus_error_free(&error);
return 1;
}
// 创建方法调用:设置属性
message = dbus_message_new_method_call(
"com.example.Service",
"/com/example/Object",
"org.freedesktop.DBus.Properties", // 标准属性接口
"Set" // Set 方法
);
// 添加参数:接口名、属性名和属性值(包装在 variant 中)
const char *interface = "com.example.Interface";
const char *property = "PropertyName";
dbus_message_iter_init_append(message, &iter);
dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &interface);
dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property);
// 创建 variant 包装属性值
dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
DBUS_TYPE_INT32_AS_STRING, &variant_iter);
dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_INT32, &new_value);
dbus_message_iter_close_container(&iter, &variant_iter);
// 发送并等待回复
if (!dbus_connection_send_with_reply(connection, message, &pending, -1)) {
fprintf(stderr, "Failed to send message\n");
dbus_message_unref(message);
dbus_connection_unref(connection);
return 1;
}
dbus_connection_flush(connection);
dbus_message_unref(message);
dbus_pending_call_block(pending);
reply = dbus_pending_call_steal_reply(pending);
dbus_pending_call_unref(pending);
if (reply == NULL) {
fprintf(stderr, "No reply received\n");
dbus_connection_unref(connection);
return 1;
}
// 检查是否有错误
if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
dbus_message_get_args(reply, &error, DBUS_TYPE_STRING, &error.message, DBUS_TYPE_INVALID);
fprintf(stderr, "Error: %s\n", error.message);
dbus_error_free(&error);
} else {
printf("Property set successfully\n");
}
dbus_message_unref(reply);
dbus_connection_unref(connection);
return 0;
}
示例 6:提供服务(服务端)
这是一个完整的服务端示例,展示了如何注册服务名、创建对象、处理方法调用并返回结果。
c
#include <dbus/dbus.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 对象路径
#define OBJECT_PATH "/com/example/Object"
#define INTERFACE_NAME "com.example.Interface"
#define SERVICE_NAME "com.example.Service"
#define PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"
#define INTROSPECTABLE_INTERFACE "org.freedesktop.DBus.Introspectable"
#define OBJECT_MANAGER_INTERFACE "org.freedesktop.DBus.ObjectManager"
// 属性存储结构
typedef struct {
char *data; // 字符串属性 "Data"
} ServiceProperties;
// 全局属性存储
static ServiceProperties properties = { .data = NULL };
// 处理方法调用
DBusHandlerResult handle_method_call(DBusConnection *connection,
DBusMessage *message,
void *user_data) {
DBusMessage *reply = NULL;
const char *method_name;
const char *interface_name;
const char *path;
// 检查消息类型是否为方法调用
if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
// 检查对象路径
path = dbus_message_get_path(message);
if (path == NULL) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
// 检查接口名
interface_name = dbus_message_get_interface(message);
if (interface_name == NULL) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
// 获取方法名
method_name = dbus_message_get_member(message);
if (method_name == NULL) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
// ObjectManager 接口通常在根路径上实现,但也允许在对象路径上
if (strcmp(interface_name, OBJECT_MANAGER_INTERFACE) == 0) {
if (strcmp(path, "/") == 0 || strcmp(path, OBJECT_PATH) == 0) {
// 允许在根路径或对象路径上调用 ObjectManager
} else {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
} else if (strcmp(path, OBJECT_PATH) != 0) {
// 其他接口必须在对象路径上
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
// 处理 Properties 接口
if (strcmp(interface_name, PROPERTIES_INTERFACE) == 0) {
const char *target_interface;
const char *property_name;
DBusMessageIter iter, variant_iter;
if (strcmp(method_name, "Get") == 0) {
// Get 方法:获取属性值
if (!dbus_message_get_args(message, NULL,
DBUS_TYPE_STRING, &target_interface,
DBUS_TYPE_STRING, &property_name,
DBUS_TYPE_INVALID)) {
reply = dbus_message_new_error(message,
DBUS_ERROR_INVALID_ARGS,
"Invalid arguments");
} else if (strcmp(target_interface, INTERFACE_NAME) != 0) {
reply = dbus_message_new_error(message,
"org.freedesktop.DBus.Error.UnknownInterface",
"Unknown interface");
} else if (strcmp(property_name, "Data") == 0) {
// 返回 Data 属性(字符串类型)
reply = dbus_message_new_method_return(message);
dbus_message_iter_init_append(reply, &iter);
// 创建 variant 包装字符串值
const char *value = properties.data ? properties.data : "";
dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
DBUS_TYPE_STRING_AS_STRING, &variant_iter);
dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &value);
dbus_message_iter_close_container(&iter, &variant_iter);
} else {
reply = dbus_message_new_error(message,
"org.freedesktop.DBus.Error.UnknownProperty",
"Unknown property");
}
}
else if (strcmp(method_name, "Set") == 0) {
// Set 方法:设置属性值
const char *value_str;
DBusMessageIter iter, variant_iter;
// 使用迭代器读取参数(因为 variant 不能直接用 get_args)
if (!dbus_message_iter_init(message, &iter)) {
reply = dbus_message_new_error(message,
DBUS_ERROR_INVALID_ARGS,
"Invalid arguments");
} else {
// 读取第一个参数:interface_name
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
reply = dbus_message_new_error(message,
DBUS_ERROR_INVALID_ARGS,
"Invalid interface argument");
} else {
dbus_message_iter_get_basic(&iter, &target_interface);
dbus_message_iter_next(&iter);
// 读取第二个参数:property_name
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
reply = dbus_message_new_error(message,
DBUS_ERROR_INVALID_ARGS,
"Invalid property argument");
} else {
dbus_message_iter_get_basic(&iter, &property_name);
dbus_message_iter_next(&iter);
// 检查接口名
if (strcmp(target_interface, INTERFACE_NAME) != 0) {
reply = dbus_message_new_error(message,
"org.freedesktop.DBus.Error.UnknownInterface",
"Unknown interface");
} else if (strcmp(property_name, "Data") == 0) {
// 读取第三个参数:value (variant)
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
reply = dbus_message_new_error(message,
DBUS_ERROR_INVALID_ARGS,
"Invalid variant argument");
} else {
dbus_message_iter_recurse(&iter, &variant_iter);
if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_STRING) {
reply = dbus_message_new_error(message,
DBUS_ERROR_INVALID_ARGS,
"Property type mismatch");
} else {
dbus_message_iter_get_basic(&variant_iter, &value_str);
// 更新属性值
if (properties.data) {
free(properties.data);
}
properties.data = strdup(value_str);
// 创建成功回复
reply = dbus_message_new_method_return(message);
}
}
} else {
reply = dbus_message_new_error(message,
"org.freedesktop.DBus.Error.UnknownProperty",
"Unknown property");
}
}
}
}
}
else if (strcmp(method_name, "GetAll") == 0) {
// GetAll 方法:获取所有属性
if (!dbus_message_get_args(message, NULL,
DBUS_TYPE_STRING, &target_interface,
DBUS_TYPE_INVALID)) {
reply = dbus_message_new_error(message,
DBUS_ERROR_INVALID_ARGS,
"Invalid arguments");
} else if (strcmp(target_interface, INTERFACE_NAME) != 0) {
reply = dbus_message_new_error(message,
"org.freedesktop.DBus.Error.UnknownInterface",
"Unknown interface");
} else {
// 返回所有属性的字典 a{sv}
reply = dbus_message_new_method_return(message);
dbus_message_iter_init_append(reply, &iter);
// 创建字典容器
DBusMessageIter dict_iter, entry_iter, variant_iter;
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
DBUS_TYPE_STRING_AS_STRING
DBUS_TYPE_VARIANT_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
&dict_iter);
// 添加 Data 属性
dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY,
NULL, &entry_iter);
const char *key = "Data";
const char *value = properties.data ? properties.data : "";
dbus_message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &key);
// 创建 variant 包装值
dbus_message_iter_open_container(&entry_iter, DBUS_TYPE_VARIANT,
DBUS_TYPE_STRING_AS_STRING, &variant_iter);
dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &value);
dbus_message_iter_close_container(&entry_iter, &variant_iter);
dbus_message_iter_close_container(&dict_iter, &entry_iter);
dbus_message_iter_close_container(&iter, &dict_iter);
}
}
else {
reply = dbus_message_new_error(message,
DBUS_ERROR_UNKNOWN_METHOD,
"Unknown method");
}
// 发送回复
if (reply != NULL) {
dbus_connection_send(connection, reply, NULL);
dbus_connection_flush(connection);
dbus_message_unref(reply);
}
return DBUS_HANDLER_RESULT_HANDLED;
}
// 处理 Introspectable 接口
if (strcmp(interface_name, INTROSPECTABLE_INTERFACE) == 0) {
if (strcmp(method_name, "Introspect") == 0) {
// Introspect 方法:返回对象的 XML 描述
const char *xml =
"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
"<node>\n"
" <interface name=\"" INTERFACE_NAME "\">\n"
" <method name=\"Echo\">\n"
" <arg name=\"input\" type=\"s\" direction=\"in\"/>\n"
" <arg name=\"output\" type=\"s\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"GetData\">\n"
" <arg name=\"result\" type=\"s\" direction=\"out\"/>\n"
" </method>\n"
" <property name=\"Data\" type=\"s\" access=\"readwrite\"/>\n"
" </interface>\n"
" <interface name=\"" PROPERTIES_INTERFACE "\">\n"
" <method name=\"Get\">\n"
" <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
" <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
" <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
" </method>\n"
" <method name=\"Set\">\n"
" <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
" <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
" <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
" </method>\n"
" <method name=\"GetAll\">\n"
" <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
" <arg name=\"properties\" type=\"a{sv}\" direction=\"out\"/>\n"
" </method>\n"
" </interface>\n"
" <interface name=\"" INTROSPECTABLE_INTERFACE "\">\n"
" <method name=\"Introspect\">\n"
" <arg name=\"xml_data\" type=\"s\" direction=\"out\"/>\n"
" </method>\n"
" </interface>\n"
" <interface name=\"" OBJECT_MANAGER_INTERFACE "\">\n"
" <method name=\"GetManagedObjects\">\n"
" <arg name=\"objects\" type=\"a{oa{sa{sv}}}\" direction=\"out\"/>\n"
" </method>\n"
" </interface>\n"
"</node>\n";
reply = dbus_message_new_method_return(message);
dbus_message_append_args(reply,
DBUS_TYPE_STRING, &xml,
DBUS_TYPE_INVALID);
// 发送回复
if (reply != NULL) {
dbus_connection_send(connection, reply, NULL);
dbus_connection_flush(connection);
dbus_message_unref(reply);
}
return DBUS_HANDLER_RESULT_HANDLED;
} else {
reply = dbus_message_new_error(message,
DBUS_ERROR_UNKNOWN_METHOD,
"Unknown method");
if (reply != NULL) {
dbus_connection_send(connection, reply, NULL);
dbus_connection_flush(connection);
dbus_message_unref(reply);
}
return DBUS_HANDLER_RESULT_HANDLED;
}
}
// 处理 ObjectManager 接口
if (strcmp(interface_name, OBJECT_MANAGER_INTERFACE) == 0) {
if (strcmp(method_name, "GetManagedObjects") == 0) {
// GetManagedObjects 方法:返回所有管理的对象及其接口和属性
reply = dbus_message_new_method_return(message);
DBusMessageIter iter, objects_dict_iter, object_entry_iter, interfaces_dict_iter;
DBusMessageIter interface_entry_iter, properties_dict_iter, property_entry_iter;
DBusMessageIter variant_iter;
dbus_message_iter_init_append(reply, &iter);
// 创建外层字典容器 a{oa{sa{sv}}}
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
DBUS_TYPE_OBJECT_PATH_AS_STRING
DBUS_TYPE_ARRAY_AS_STRING
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
DBUS_TYPE_STRING_AS_STRING
DBUS_TYPE_ARRAY_AS_STRING
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
DBUS_TYPE_STRING_AS_STRING
DBUS_TYPE_VARIANT_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
&objects_dict_iter);
// 添加对象路径条目
dbus_message_iter_open_container(&objects_dict_iter, DBUS_TYPE_DICT_ENTRY,
NULL, &object_entry_iter);
// 添加对象路径(键)
const char *object_path_str = OBJECT_PATH;
dbus_message_iter_append_basic(&object_entry_iter, DBUS_TYPE_OBJECT_PATH, &object_path_str);
// 创建接口字典 a{sa{sv}}
dbus_message_iter_open_container(&object_entry_iter, DBUS_TYPE_ARRAY,
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
DBUS_TYPE_STRING_AS_STRING
DBUS_TYPE_ARRAY_AS_STRING
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
DBUS_TYPE_STRING_AS_STRING
DBUS_TYPE_VARIANT_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
&interfaces_dict_iter);
// 添加 com.example.Interface 接口
dbus_message_iter_open_container(&interfaces_dict_iter, DBUS_TYPE_DICT_ENTRY,
NULL, &interface_entry_iter);
const char *interface_key = INTERFACE_NAME;
dbus_message_iter_append_basic(&interface_entry_iter, DBUS_TYPE_STRING, &interface_key);
// 创建属性字典 a{sv}
dbus_message_iter_open_container(&interface_entry_iter, DBUS_TYPE_ARRAY,
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
DBUS_TYPE_STRING_AS_STRING
DBUS_TYPE_VARIANT_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
&properties_dict_iter);
// 添加 Data 属性
dbus_message_iter_open_container(&properties_dict_iter, DBUS_TYPE_DICT_ENTRY,
NULL, &property_entry_iter);
const char *property_key = "Data";
const char *property_value = properties.data ? properties.data : "";
dbus_message_iter_append_basic(&property_entry_iter, DBUS_TYPE_STRING, &property_key);
// 创建 variant 包装属性值
dbus_message_iter_open_container(&property_entry_iter, DBUS_TYPE_VARIANT,
DBUS_TYPE_STRING_AS_STRING, &variant_iter);
dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &property_value);
dbus_message_iter_close_container(&property_entry_iter, &variant_iter);
dbus_message_iter_close_container(&properties_dict_iter, &property_entry_iter);
// 关闭属性字典
dbus_message_iter_close_container(&interface_entry_iter, &properties_dict_iter);
dbus_message_iter_close_container(&interfaces_dict_iter, &interface_entry_iter);
// 关闭接口字典
dbus_message_iter_close_container(&object_entry_iter, &interfaces_dict_iter);
// 关闭对象条目
dbus_message_iter_close_container(&objects_dict_iter, &object_entry_iter);
// 关闭外层字典
dbus_message_iter_close_container(&iter, &objects_dict_iter);
// 发送回复
if (reply != NULL) {
dbus_connection_send(connection, reply, NULL);
dbus_connection_flush(connection);
dbus_message_unref(reply);
}
return DBUS_HANDLER_RESULT_HANDLED;
} else {
reply = dbus_message_new_error(message,
DBUS_ERROR_UNKNOWN_METHOD,
"Unknown method");
if (reply != NULL) {
dbus_connection_send(connection, reply, NULL);
dbus_connection_flush(connection);
dbus_message_unref(reply);
}
return DBUS_HANDLER_RESULT_HANDLED;
}
}
// 检查是否是我们的接口
if (strcmp(interface_name, INTERFACE_NAME) != 0) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
// 处理 Echo 方法
if (strcmp(method_name, "Echo") == 0) {
char *input;
char *output;
// 读取参数
if (!dbus_message_get_args(message, NULL,
DBUS_TYPE_STRING, &input,
DBUS_TYPE_INVALID)) {
// 创建错误回复
reply = dbus_message_new_error(message,
DBUS_ERROR_INVALID_ARGS,
"Invalid arguments");
} else {
// 处理:将输入转换为大写
output = malloc(strlen(input) + 1);
strcpy(output, input);
for (int i = 0; output[i]; i++) {
if (output[i] >= 'a' && output[i] <= 'z') {
output[i] = output[i] - 'a' + 'A';
}
}
// 创建回复消息
reply = dbus_message_new_method_return(message);
// 添加返回值
dbus_message_append_args(reply,
DBUS_TYPE_STRING, &output,
DBUS_TYPE_INVALID);
printf("Echo called with: %s, returning: %s\n", input, output);
free(output);
}
}
// 处理 GetData 方法
else if (strcmp(method_name, "GetData") == 0) {
const char *data = "Hello from service!";
reply = dbus_message_new_method_return(message);
dbus_message_append_args(reply,
DBUS_TYPE_STRING, &data,
DBUS_TYPE_INVALID);
printf("GetData called\n");
}
// 未知方法
else {
reply = dbus_message_new_error(message,
DBUS_ERROR_UNKNOWN_METHOD,
"Unknown method");
}
// 发送回复
if (reply != NULL) {
dbus_connection_send(connection, reply, NULL);
dbus_connection_flush(connection);
dbus_message_unref(reply);
}
return DBUS_HANDLER_RESULT_HANDLED;
}
// 对象路径注册表
static const DBusObjectPathVTable vtable = {
.message_function = handle_method_call,
.unregister_function = NULL
};
int main(void) {
DBusConnection *connection;
DBusError error;
DBusMessage *message;
DBusMessage *reply;
DBusPendingCall *pending;
dbus_uint32_t result;
dbus_error_init(&error);
// 连接到会话总线
connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error)) {
fprintf(stderr, "Connection error: %s\n", error.message);
dbus_error_free(&error);
return 1;
}
// 请求服务名
message = dbus_message_new_method_call(
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"RequestName"
);
const char *service_name = SERVICE_NAME;
dbus_uint32_t flags = DBUS_NAME_FLAG_REPLACE_EXISTING |
DBUS_NAME_FLAG_DO_NOT_QUEUE;
dbus_message_append_args(message,
DBUS_TYPE_STRING, &service_name,
DBUS_TYPE_UINT32, &flags,
DBUS_TYPE_INVALID);
// 发送请求并等待回复
if (!dbus_connection_send_with_reply(connection, message, &pending, -1)) {
fprintf(stderr, "Failed to send RequestName\n");
dbus_message_unref(message);
dbus_connection_unref(connection);
return 1;
}
dbus_connection_flush(connection);
dbus_message_unref(message);
// 等待回复
dbus_pending_call_block(pending);
reply = dbus_pending_call_steal_reply(pending);
dbus_pending_call_unref(pending);
if (reply == NULL) {
fprintf(stderr, "No reply to RequestName\n");
dbus_connection_unref(connection);
return 1;
}
// 检查请求结果
if (dbus_message_get_args(reply, &error,
DBUS_TYPE_UINT32, &result,
DBUS_TYPE_INVALID)) {
if (result == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
printf("Service name '%s' registered successfully\n", SERVICE_NAME);
} else {
fprintf(stderr, "Failed to register service name: %u\n", result);
dbus_message_unref(reply);
dbus_connection_unref(connection);
return 1;
}
} else {
fprintf(stderr, "Failed to get RequestName reply\n");
dbus_message_unref(reply);
dbus_connection_unref(connection);
return 1;
}
dbus_message_unref(reply);
// 注册根路径(用于 ObjectManager)
if (!dbus_connection_register_object_path(connection,
"/",
&vtable,
NULL)) {
fprintf(stderr, "Failed to register root object path\n");
dbus_connection_unref(connection);
return 1;
}
// 注册对象路径
if (!dbus_connection_register_object_path(connection,
OBJECT_PATH,
&vtable,
NULL)) {
fprintf(stderr, "Failed to register object path\n");
dbus_connection_unref(connection);
return 1;
}
printf("Root path '/' registered (for ObjectManager)\n");
printf("Object path '%s' registered\n", OBJECT_PATH);
printf("Service is running. Press Ctrl+C to exit.\n");
printf("Test methods:\n");
printf(" dbus-send --session --dest=%s --print-reply --type=method_call \\\n", SERVICE_NAME);
printf(" %s %s.Echo string:'hello'\n", OBJECT_PATH, INTERFACE_NAME);
printf(" dbus-send --session --dest=%s --print-reply --type=method_call \\\n", SERVICE_NAME);
printf(" %s %s.Get string:'%s' string:'Data'\n",
OBJECT_PATH, PROPERTIES_INTERFACE, INTERFACE_NAME);
printf(" dbus-send --session --dest=%s --print-reply --type=method_call \\\n", SERVICE_NAME);
printf(" %s %s.Introspect\n", OBJECT_PATH, INTROSPECTABLE_INTERFACE);
printf(" dbus-send --session --dest=%s --print-reply --type=method_call \\\n", SERVICE_NAME);
printf(" / %s.GetManagedObjects\n", OBJECT_MANAGER_INTERFACE);
// 初始化属性
properties.data = strdup("default value");
// 进入事件循环
while (dbus_connection_read_write_dispatch(connection, -1)) {
// 处理消息
}
// 清理资源
if (properties.data) {
free(properties.data);
}
dbus_connection_unref(connection);
return 0;
}
编译和运行:
bash
# 编译
gcc -o dbus-service dbus-service.c `pkg-config --cflags --libs dbus-1`
# 运行服务
./dbus-service
# 在另一个终端测试
# 测试 Echo 方法
dbus-send --session \
--dest=com.example.Service \
--print-reply \
--type=method_call \
/com/example/Object \
com.example.Interface.Echo \
string:'hello'
# 返回: method return sender=:1.xxx -> dest=:1.yyy reply_serial=2 string "HELLO"
# 测试 Properties.Get
dbus-send --session \
--dest=com.example.Service \
--print-reply \
--type=method_call \
/com/example/Object \
org.freedesktop.DBus.Properties.Get \
string:'com.example.Interface' string:'Data'
# 测试 Introspectable.Introspect
dbus-send --session \
--dest=com.example.Service \
--print-reply \
--type=method_call \
/com/example/Object \
org.freedesktop.DBus.Introspectable.Introspect
# 测试 ObjectManager.GetManagedObjects
dbus-send --session \
--dest=com.example.Service \
--print-reply \
--type=method_call \
/ \
org.freedesktop.DBus.ObjectManager.GetManagedObjects
# 测试 Peer.Ping(自动提供,无需实现)
dbus-send --session \
--dest=com.example.Service \
--print-reply \
--type=method_call \
/com/example/Object \
org.freedesktop.DBus.Peer.Ping
说明:
- 此示例实现了
org.freedesktop.DBus.Properties、org.freedesktop.DBus.Introspectable和org.freedesktop.DBus.ObjectManager接口 org.freedesktop.DBus.Peer接口由 D-Bus 库自动提供,无需实现即可使用
示例 7:提供服务并发送信号
这个示例展示了如何在服务中发送信号。
c
#include <dbus/dbus.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define OBJECT_PATH "/com/example/Object"
#define INTERFACE_NAME "com.example.Interface"
#define SERVICE_NAME "com.example.Service"
// 发送信号
void send_signal(DBusConnection *connection, const char *data) {
DBusMessage *signal;
signal = dbus_message_new_signal(OBJECT_PATH,
INTERFACE_NAME,
"DataChanged");
if (signal == NULL) {
fprintf(stderr, "Failed to create signal\n");
return;
}
dbus_message_append_args(signal,
DBUS_TYPE_STRING, &data,
DBUS_TYPE_INVALID);
if (!dbus_connection_send(connection, signal, NULL)) {
fprintf(stderr, "Failed to send signal\n");
} else {
dbus_connection_flush(connection);
printf("Signal sent: %s\n", data);
}
dbus_message_unref(signal);
}
// 处理方法调用
DBusHandlerResult handle_method_call(DBusConnection *connection,
DBusMessage *message,
void *user_data) {
DBusMessage *reply = NULL;
const char *method_name;
const char *interface_name;
const char *path;
// 检查消息类型是否为方法调用
if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
// 检查对象路径
path = dbus_message_get_path(message);
if (path == NULL || strcmp(path, OBJECT_PATH) != 0) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
// 检查接口名
interface_name = dbus_message_get_interface(message);
if (interface_name == NULL || strcmp(interface_name, INTERFACE_NAME) != 0) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
// 获取方法名
method_name = dbus_message_get_member(message);
if (method_name == NULL) {
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
// 处理 SetData 方法:设置数据并发送信号
if (strcmp(method_name, "SetData") == 0) {
char *data;
if (!dbus_message_get_args(message, NULL,
DBUS_TYPE_STRING, &data,
DBUS_TYPE_INVALID)) {
reply = dbus_message_new_error(message,
DBUS_ERROR_INVALID_ARGS,
"Invalid arguments");
} else {
// 处理数据(这里只是示例,实际应用中会保存数据)
printf("SetData called with: %s\n", data);
// 发送信号通知数据变化
send_signal(connection, data);
// 创建成功回复
reply = dbus_message_new_method_return(message);
}
}
else {
reply = dbus_message_new_error(message,
DBUS_ERROR_UNKNOWN_METHOD,
"Unknown method");
}
if (reply != NULL) {
dbus_connection_send(connection, reply, NULL);
dbus_connection_flush(connection);
dbus_message_unref(reply);
}
return DBUS_HANDLER_RESULT_HANDLED;
}
static const DBusObjectPathVTable vtable = {
.message_function = handle_method_call,
.unregister_function = NULL
};
int main(void) {
DBusConnection *connection;
DBusError error;
DBusMessage *message;
DBusMessage *reply;
DBusPendingCall *pending;
dbus_uint32_t result;
dbus_error_init(&error);
connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error)) {
fprintf(stderr, "Connection error: %s\n", error.message);
dbus_error_free(&error);
return 1;
}
// 请求服务名
message = dbus_message_new_method_call(
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"RequestName"
);
const char *service_name = SERVICE_NAME;
dbus_uint32_t flags = DBUS_NAME_FLAG_REPLACE_EXISTING |
DBUS_NAME_FLAG_DO_NOT_QUEUE;
dbus_message_append_args(message,
DBUS_TYPE_STRING, &service_name,
DBUS_TYPE_UINT32, &flags,
DBUS_TYPE_INVALID);
if (!dbus_connection_send_with_reply(connection, message, &pending, -1)) {
fprintf(stderr, "Failed to send RequestName\n");
dbus_message_unref(message);
dbus_connection_unref(connection);
return 1;
}
dbus_connection_flush(connection);
dbus_message_unref(message);
dbus_pending_call_block(pending);
reply = dbus_pending_call_steal_reply(pending);
dbus_pending_call_unref(pending);
if (reply == NULL ||
!dbus_message_get_args(reply, &error,
DBUS_TYPE_UINT32, &result,
DBUS_TYPE_INVALID) ||
result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
fprintf(stderr, "Failed to register service name\n");
if (reply) dbus_message_unref(reply);
dbus_connection_unref(connection);
return 1;
}
dbus_message_unref(reply);
// 注册对象路径
if (!dbus_connection_register_object_path(connection,
OBJECT_PATH,
&vtable,
NULL)) {
fprintf(stderr, "Failed to register object path\n");
dbus_connection_unref(connection);
return 1;
}
printf("Service is running. Press Ctrl+C to exit.\n");
printf("Test with: dbus-send --session --dest=%s \\\n", SERVICE_NAME);
printf(" --print-reply --type=method_call %s %s.SetData string:'test data'\n",
OBJECT_PATH, INTERFACE_NAME);
printf("Listen with: dbus-monitor --session \\\n");
printf(" \"type='signal',interface='%s'\"\n", INTERFACE_NAME);
// 事件循环
while (dbus_connection_read_write_dispatch(connection, -1)) {
// 处理消息
}
dbus_connection_unref(connection);
return 0;
}
测试:
bash
# 终端1:运行服务
./dbus-service-signal
# 终端2:监听信号
dbus-monitor --session "type='signal',interface='com.example.Interface'"
# 终端3:调用方法(会触发信号)
dbus-send --session \
--dest=com.example.Service \
--print-reply \
--type=method_call \
/com/example/Object \
com.example.Interface.SetData \
string:'new data'
性能评价
优点
- 功能强大:支持复杂的对象模型和方法调用
- 服务发现:自动服务注册和发现机制
- 类型安全:强类型系统,减少错误
- 跨语言:支持多种编程语言
- 标准化:广泛使用的标准,Linux桌面环境标准
缺点
- 性能开销:消息序列化/反序列化开销较大
- 延迟较高:不适合对延迟敏感的应用
- 复杂性:API相对复杂,学习曲线陡峭
- 依赖服务:需要D-Bus守护进程运行
- 不适合大数据量:主要用于小数据量的控制消息
性能特点
- 延迟:高(通常ms级别,受消息序列化影响)
- 吞吐量:低(不适合大数据量传输)
- CPU占用:中等(消息处理开销)
- 内存占用:中等(消息缓冲)
适用场景
- ✅ Linux桌面环境应用通信
- ✅ 系统服务之间的通信
- ✅ 需要服务发现的应用
- ✅ 需要复杂对象模型的场景
- ✅ 跨语言通信
- ❌ 对性能要求极高的场景
- ❌ 大数据量传输
- ❌ 实时性要求高的场景
常见用途
-
系统服务:
- NetworkManager:网络管理
- systemd:系统服务管理
- UPower:电源管理
-
桌面应用:
- 应用程序间通信
- 桌面环境集成
- 通知系统
-
设备管理:
- 硬件设备访问
- 媒体播放器控制
- 打印机管理
注意事项
- 权限控制:注意D-Bus的权限策略,某些操作需要权限
- 服务可用性:检查服务是否可用再调用
- 异步处理:注意异步调用的错误处理
- 消息大小:避免发送过大的消息
- 连接管理:正确管理D-Bus连接,避免泄漏
- 线程安全:注意多线程环境下的使用
- 版本兼容:注意不同版本的API差异