vesperW · 1 天前

嵌入式开发之 D-Bus 通信机制

实验今天给大家分享一种在嵌入式开发过程中用到的 DBus 通信。

一、何为 DBus?

在 Linux 系统中,DBus 是进程间的隐形快递网络。它就像在系统里搭建了一套高速公路网:

• 守护进程(dbus-daemon)负责交通调度
• 进程通过虚拟地址(总线地址)相互寻址
• 数据包裹(message)精准投递,支持快递单号回执(同步返回)

DBus 工作流程示意图

二、四大核心要素解析

DBus 结构由四个要素组成,下面小哥类比了一下:

image.png

这四要素就能确保你的消息能准确送达目标对象的操作接口。

三、主辅进程的设计

1、业务场景

• 主进程(Control Center)

发送控制指令,如:"CHECK_STATUS"、"REBOOT"
等待辅助进程回复,同时获取辅助队列发过来的数据实时显示温度数据

• 辅助进程(Sensor Daemon)

接收主进程发送过来的指令并处理完后返回执行结果
每秒推送温度数据给主进程

2、通信蓝图

主辅进程通信流程图

采用双向通信机制:

🔹 同步方法调用:主进程下达指令后等待确认
🔹 异步信号发射:辅助进程主动推送数据流

四、一个 C 语言使用 Dbus 的代码案例

1、辅助进程实现(服务端)

// sensor_daemon.c
#include <dbus/dbus.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

DBusHandlerResult handle_control(DBusConnection* conn, DBusMessage* msg) {
    //指令处理逻辑
    if (dbus_message_is_method_call(msg, "com.example.Control", "Execute")) {
        constchar* cmd;
        dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &cmd, NULL);

        // 处理不同指令
        printf("[DAEMON] 收到指令: %s\n", cmd);
        constchar* res = "DEFAULT_OK";
        if (strcmp(cmd, "CHECK_STATUS") == 0) {
            res = "STATUS_OK";
        } elseif (strcmp(cmd, "REBOOT") == 0) {
            res = "REBOOTING";
        }

        // 返回响应
        DBusMessage* reply = dbus_message_new_method_return(msg);
        dbus_message_append_args(reply, DBUS_TYPE_STRING, &res, NULL);
        dbus_connection_send(conn, reply, NULL);
        dbus_message_unref(reply);
        
        return DBUS_HANDLER_RESULT_HANDLED;
    }
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

void send_temperature(DBusConnection* conn) {
    //温度数据模拟
    staticint temp = 25;
    temp += rand()%3 - 1; // 波动模拟
    
    DBusMessage* signal = dbus_message_new_signal(
        "/com/example/Sensor",
        "com.example.Sensor",
        "TemperatureUpdate"
    );
    dbus_message_append_args(signal, DBUS_TYPE_INT32, &temp, NULL);
    dbus_connection_send(conn, signal, NULL);
    dbus_message_unref(signal);
}

int main() {
    //连接初始化
    DBusConnection* conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
    dbus_bus_request_name(conn, "com.example.Sensor", 0, NULL);

    //注册处理方法
    dbus_connection_add_filter(conn, handle_control, NULL, NULL);
    dbus_bus_add_match(conn, "type='method_call',interface='com.example.Control'", NULL);

    //主循环
    while(1) {
        dbus_connection_read_write_dispatch(conn, 500); // 非阻塞轮询
        send_temperature();    // 定时发送温度
        sleep(1);
    }
    return0;
}

2、主进程实现(客户端)

// control_center.c
#include <dbus/dbus.h>
#include <stdio.h>
#include <string.h>

void monitor_temperature(DBusConnection* conn) {
    //温度监控线程
    dbus_bus_add_match(conn,
        "type='signal',"
        "interface='com.example.Sensor',"
        "path='/com/example/Sensor'",
        NULL
    );

    while(1) {
        dbus_connection_read_write(conn, 0);
        DBusMessage* msg = dbus_connection_pop_message(conn);
        if(msg && dbus_message_is_signal(msg, "com.example.Sensor", "TemperatureUpdate")) {
            int temp;
            dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &temp, NULL);
            printf("[监控] 当前温度: %d℃\n", temp);
        }
        if(msg) dbus_message_unref(msg);
    }
}

void send_command(DBusConnection* conn, const char* cmd) {
    //发送指令
    DBusMessage* msg = dbus_message_new_method_call(
        "com.example.Sensor",
        "/com/example/Sensor",
        "com.example.Control",
        "Execute"
    );
    dbus_message_append_args(msg, DBUS_TYPE_STRING, &cmd, NULL);

    //等待回复
    DBusError err;
    dbus_error_init(&err);
    DBusMessage* reply = dbus_connection_send_with_reply_and_block(conn, msg, 1000, &err);
    
    if(dbus_error_is_set(&err)) {
        printf("[错误] 指令发送失败: %s\n", err.message);
    } else {
        constchar* result;
        dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, &result, NULL);
        printf("[响应] %s\n", result);
    }
    
    dbus_message_unref(msg);
    dbus_message_unref(reply);
}

int main() {
    //初始化连接
    DBusConnection* conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
    
    //启动监控线程
    pthread_t tid;
    pthread_create(&tid, NULL, (void*)monitor_temperature, conn);

    //控制指令交互
    char cmd[64];
    while(1) {
        printf("输入指令 (或exit退出): ");
        fgets(cmd, sizeof(cmd), stdin);
        cmd[strcspn(cmd, "\n")] = 0;
        
        if(strcmp(cmd, "exit") == 0) break;
        send_command(conn, cmd);
    }
    
    return0;
}

3、实验演示

1️⃣ 编译运行

# 安装开发库
sudo apt install libdbus-1-dev -y

# 编译双端
gcc sensor_daemon.c -o sensor_daemon -ldbus-1
gcc control_center.c -o control_center -ldbus-1 -lpthread

# 终端1启动传感器守护进程
./sensor_daemon

# 终端2启动控制中心
./control_center

2️⃣ 操作示例

输入指令 (或exit退出): CHECK_STATUS
[响应] STATUS_OK
[监控] 当前温度: 25℃
输入指令 (或exit退出): REBOOT
[响应] REBOOTING
[监控] 当前温度: 24℃

而且 Dbus 调试功能也非常的完善,基本上把上面几个常用的 Dbus 接口函数搞清楚了,玩起来不难。

当然如果你调试 dbus 的时候比较麻烦,Linux 也提供了工具:

网络监控器数据:

dbus-monitor"interface=com.example.Sensor"

用于指令发送器

   # 发送重启指令
   dbus-send --session --dest=com.example.Sensor \
     /com/example/Sensor \
     com.example.Control.Execute \
     string:"REBOOT"

END

来源:嵌入式专栏

推荐阅读

欢迎大家点赞留言,更多 Arm 技术文章动态请关注极术社区嵌入式客栈专栏欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。

推荐阅读
关注数
2922
内容数
363
分享一些在嵌入式应用开发方面的浅见,广交朋友
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息