Linux进程间通信之D-Bus

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的特点:

  1. 消息总线架构:提供系统总线和会话总线两种总线

    • 系统总线:系统级服务,所有用户共享
    • 会话总线:用户会话级服务,每个用户会话独立
  2. 对象模型:基于对象、接口、方法的面向对象模型

  3. 异步通信:支持异步方法调用和信号通知

  4. 类型系统:强类型系统,支持多种数据类型

  5. 服务发现:支持服务注册和发现机制

实现机制

  1. 总线守护进程

    • D-Bus守护进程(dbus-daemon)管理总线
    • 负责消息路由、服务注册、权限控制
  2. 消息类型

    • 方法调用(Method Call):RPC调用,需要回复
    • 方法返回(Method Return):方法调用的返回值
    • 信号(Signal):事件通知,不需要回复
    • 错误(Error):错误响应
  3. 通信流程

    复制代码
    应用程序A  ──>  [D-Bus守护进程]  ──>  应用程序B
                     (消息路由)
  4. 对象路径 :使用类似文件系统的路径标识对象(如 /org/freedesktop/NetworkManager

  5. 接口名称 :使用反向域名格式(如 org.freedesktop.DBus.Introspectable

标准接口:org.freedesktop.DBus.Properties

org.freedesktop.DBus.Properties 是 D-Bus 规范定义的标准接口,用于统一访问对象的属性。该接口提供了标准化的属性访问机制,使得客户端可以通过统一的方式访问不同对象的属性。

接口说明
  • 接口名org.freedesktop.DBus.Properties
  • 对象路径:任何实现了属性的对象都可以提供此接口
  • 目的:提供标准化的属性访问方法
主要方法
  1. Get:获取属性值

    • 参数
      • interface_name (string):属性所属的接口名
      • property_name (string):属性名
    • 返回值variant 类型,包含属性值
    • 说明:返回指定接口的指定属性值,属性值包装在 variant 中
  2. Set:设置属性值

    • 参数
      • interface_name (string):属性所属的接口名
      • property_name (string):属性名
      • value (variant):新的属性值
    • 返回值:无(成功)或错误
    • 说明:设置指定接口的指定属性值,新值需要包装在 variant 中
  3. 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 方法动态获取对象的所有属性
实现要求

实现属性的对象应该:

  1. 在对象上提供 org.freedesktop.DBus.Properties 接口
  2. 实现 GetSetGetAll 方法
  3. 在属性变化时发送 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():连接到系统总线或会话总线

    c 复制代码
    DBusConnection *dbus_bus_get(DBusBusType type, DBusError *error);
    • typeDBUS_BUS_SESSION(会话总线)或 DBUS_BUS_SYSTEM(系统总线)
    • 返回连接对象,失败返回 NULL
  • dbus_connection_unref():释放连接

    c 复制代码
    void dbus_connection_unref(DBusConnection *connection);
服务端API
  • dbus_connection_register_object_path():注册对象路径(服务端)

    c 复制代码
    dbus_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():请求服务名(服务端)

    c 复制代码
    dbus_uint32_t dbus_bus_request_name(
        DBusConnection *connection,
        const char *name,              // 服务名
        dbus_uint32_t flags,           // 标志位
        DBusError *error
    );
    • 用于服务端请求在总线上的唯一服务名
    • flagsDBUS_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():初始化错误对象

    c 复制代码
    void dbus_error_init(DBusError *error);
  • dbus_error_is_set():检查是否有错误

    c 复制代码
    dbus_bool_t dbus_error_is_set(DBusError *error);
  • dbus_error_free():释放错误对象

    c 复制代码
    void dbus_error_free(DBusError *error);
方法调用
  • dbus_message_new_method_call():创建方法调用消息

    c 复制代码
    DBusMessage *dbus_message_new_method_call(
        const char *bus_name,      // 服务名
        const char *object_path,   // 对象路径
        const char *interface,     // 接口名
        const char *method         // 方法名
    );
  • dbus_message_append_args():添加方法参数

    c 复制代码
    dbus_bool_t dbus_message_append_args(
        DBusMessage *message,
        int first_arg_type,  // 第一个参数类型
        ...                   // 参数值和类型对
    );
  • dbus_connection_send_with_reply():发送方法调用并等待回复

    c 复制代码
    dbus_bool_t dbus_connection_send_with_reply(
        DBusConnection *connection,
        DBusMessage *message,
        DBusPendingCall **pending_return,
        int timeout_milliseconds
    );
  • dbus_pending_call_block():阻塞等待回复

    c 复制代码
    void dbus_pending_call_block(DBusPendingCall *pending);
  • dbus_pending_call_steal_reply():获取回复消息

    c 复制代码
    DBusMessage *dbus_pending_call_steal_reply(DBusPendingCall *pending);
  • dbus_message_get_args():从回复消息中读取返回值

    c 复制代码
    dbus_bool_t dbus_message_get_args(
        DBusMessage *message,
        DBusError *error,
        int first_arg_type,  // 第一个返回值类型
        ...                   // 返回值指针和类型对
    );
信号处理
  • dbus_message_new_signal():创建信号消息

    c 复制代码
    DBusMessage *dbus_message_new_signal(
        const char *path,      // 对象路径
        const char *interface, // 接口名
        const char *name       // 信号名
    );
  • dbus_connection_send():发送信号(不需要回复)

    c 复制代码
    dbus_bool_t dbus_connection_send(
        DBusConnection *connection,
        DBusMessage *message,
        dbus_uint32_t *serial
    );
  • dbus_bus_add_match():添加信号匹配规则

    c 复制代码
    dbus_bool_t dbus_bus_add_match(
        DBusConnection *connection,
        const char *rule,  // 匹配规则字符串
        DBusError *error
    );
  • dbus_connection_add_filter():注册信号处理函数

    c 复制代码
    dbus_bool_t dbus_connection_add_filter(
        DBusConnection *connection,
        DBusHandleMessageFunction function,
        void *user_data,
        DBusFreeFunction free_data_function
    );
  • dbus_connection_read_write_dispatch():处理消息(事件循环)

    c 复制代码
    dbus_bool_t dbus_connection_read_write_dispatch(
        DBusConnection *connection,
        int timeout_milliseconds
    );
消息管理
  • dbus_message_unref():释放消息对象

    c 复制代码
    void dbus_message_unref(DBusMessage *message);
  • dbus_connection_flush():刷新连接,确保消息发送

    c 复制代码
    void 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.Propertiesorg.freedesktop.DBus.Introspectableorg.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'

性能评价

优点

  1. 功能强大:支持复杂的对象模型和方法调用
  2. 服务发现:自动服务注册和发现机制
  3. 类型安全:强类型系统,减少错误
  4. 跨语言:支持多种编程语言
  5. 标准化:广泛使用的标准,Linux桌面环境标准

缺点

  1. 性能开销:消息序列化/反序列化开销较大
  2. 延迟较高:不适合对延迟敏感的应用
  3. 复杂性:API相对复杂,学习曲线陡峭
  4. 依赖服务:需要D-Bus守护进程运行
  5. 不适合大数据量:主要用于小数据量的控制消息

性能特点

  • 延迟:高(通常ms级别,受消息序列化影响)
  • 吞吐量:低(不适合大数据量传输)
  • CPU占用:中等(消息处理开销)
  • 内存占用:中等(消息缓冲)

适用场景

  • ✅ Linux桌面环境应用通信
  • ✅ 系统服务之间的通信
  • ✅ 需要服务发现的应用
  • ✅ 需要复杂对象模型的场景
  • ✅ 跨语言通信
  • ❌ 对性能要求极高的场景
  • ❌ 大数据量传输
  • ❌ 实时性要求高的场景

常见用途

  1. 系统服务

    • NetworkManager:网络管理
    • systemd:系统服务管理
    • UPower:电源管理
  2. 桌面应用

    • 应用程序间通信
    • 桌面环境集成
    • 通知系统
  3. 设备管理

    • 硬件设备访问
    • 媒体播放器控制
    • 打印机管理

注意事项

  1. 权限控制:注意D-Bus的权限策略,某些操作需要权限
  2. 服务可用性:检查服务是否可用再调用
  3. 异步处理:注意异步调用的错误处理
  4. 消息大小:避免发送过大的消息
  5. 连接管理:正确管理D-Bus连接,避免泄漏
  6. 线程安全:注意多线程环境下的使用
  7. 版本兼容:注意不同版本的API差异

扩展阅读

相关推荐
fufu03112 小时前
Linux环境下的C语言编程(四十九)
linux·c语言·算法
YGGP2 小时前
【Golang】LeetCode198. 打家劫舍
算法·leetcode
啊阿狸不会拉杆2 小时前
《数字图像处理》实验6-图像分割方法
图像处理·人工智能·算法·计算机视觉·数字图像处理
YGGP2 小时前
【Golang】LeetCode 152. 乘积最大子数组
算法·leetcode
爱学大树锯2 小时前
171 · 乱序字符串
算法
小李小李快乐不已2 小时前
栈和堆理论基础
c++·算法·leetcode
最爱吃咸鸭蛋2 小时前
LeetCode 97
算法·leetcode·职场和发展
淮北也生橘122 小时前
Linux驱动开发:移植一个MIPI摄像头驱动并将其点亮(基于Sstar 2355平台)
linux·运维·驱动开发·嵌入式linux
遇见火星2 小时前
Linux运维:RPM包配置管理指南
linux·运维·服务器·rpm