dbus是一个轻量级的IPC,用于进程间通信或进程与内核间的通信,dbus通信结构下图:
Bus daemon:总线守护进程,dbus是点对点进行通信的,所以基本上dbus进程连接的都是bus daemon,bus daemon负责将收到的dbus消息进行路由、转发。dbus进程将消息发送给bus daemon,bus daemon会结合消息的目标地址、对象路径、消息的类型、以及dbus进程希望收到的消息进行综合,然后转发。
下面是一个dbus进程连接的结构:
System bus:一个持久的系统总线,在引导时就会启动。由操作系统和后台使用,安全性好。普通进程也可以连接到这个总线上接收消息,但是发送消息却是受限的。
Session bus:用户登录后启动,属于那个用户的私有总线。是用户的应用程序间用来通信的一个总线。
Dbus 的连接:一个dbus进程向后台dbus进行连接,会得要一个连接名(bus name),bus name有两种情况:公共名、唯一名。每个dbus连接一定会有一个唯一名,且唯一名在整个后台dbus的生命周期内唯一。如下图,“:1.0”是唯一名,“org.xfce.xfdesktop”是公共名,如下图:
Object path:dbus连接下面可以有多个object path,如bluez连接下面有bluez、bluez/hciconfig等,个人认为之所以有object path,是为了区分不同的服务,如bluez可以理解为蓝牙的协议层,而hciconfig是控制层;
Interface:object path 下面也有很多接口,以bluez为例,下面存在Adapter1、AgentManager1等;
Method:每个interface下也有很多接口,以Adapter1为例,下面有startDiscovery、stopDiscovery等方法;而Method又分为四类:
(1). Method call消息:进程发送消息(目标连接名、对象路径、接口、方法、方法参数)到bus daemon上,bus daemon收到消息后,根据消息的目标名、对象路径,发送到目标进程上,目标进程收到后,使用发送过来的参数,执行这个方法;
(2). Method return 消息:在上述结果执行完成后,一些需要返回结果的接口,会通过这个接口将结果返回回去。
(3). signal消息:希望收到此信号的dbus进程需要在后台dbus上注册,表示自己希望收到这个信号消息,当bus daemon收到此消息时,会将消息转发给注册此信号的进程。
(4). Error消息:
通信示意图:
下面代码是一个Method call消息的内容:
int a = 2, b = 3;
msg = dbus_message_new_method_call("test.wei.dest", "/test/method/Object", "test.method.type", "Method");
DBusMessageIter iter;
dbus_message_iter_init_append(msg, &iter);
dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &a);
dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &b);
上面代码中消息的内容包括了目标连接、对象路径、接口、方法、参数。这样bus daemon就可以根据消息的内容,进行转发到test.wei.dest的连接上,并在目标连接的进程内使用参数a、b执行Method这个方法。相当于是test.wei.dest对外提供了一个Method的方法,供外部使用。
下面是一个自发自收的demo:
#include<stdio.h>
#include<stdlib.h>
#include<dbus/dbus.h>
//消息循环处理,对外提供了两个方法,Add、Quit;
DBusHandlerResult dbus_message_handler(DBusConnection* conn, DBusMessage* msg, void* user_data)
{
if (dbus_message_is_method_call(msg, "com.example.Calculator", "Add"))
{
// 处理 Add 方法
DBusMessageIter iter;
int a, b, sum;
dbus_message_iter_init(msg, &iter);
dbus_message_iter_get_basic(&iter, &a);
dbus_message_iter_next(&iter);
dbus_message_iter_get_basic(&iter, &b);
sum = a + b;
DBusMessage* reply = dbus_message_new_method_return(msg);
DBusMessageIter reply_iter;
dbus_message_iter_init_append(reply, &reply_iter);
dbus_message_iter_append_basic(&reply_iter, DBUS_TYPE_INT32, &sum);
dbus_connection_send(conn, reply,NULL);
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
else if (dbus_message_is_method_call(msg, "com.example.Calculator", "Quit"))
{
// 处理 Quit 方法
dbus_connection_close(conn);
return DBUS_HANDLER_RESULT_HANDLED;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
//收到的应答消息处理
void dbus_reply_handler(DBusPendingCall* pending, void* user_data)
{
int* sum = (int*)user_data;
DBusMessage* reply =dbus_pending_call_steal_reply(pending);
DBusMessageIter iter;
if (reply)
{
if (dbus_message_iter_init(reply, &iter))
{
dbus_message_iter_get_basic(&iter, sum);
printf("The sum is: %d\n", *sum);
}
dbus_message_unref(reply);
}
dbus_pending_call_unref(pending);
}
int main()
{
DBusError error;
DBusConnection* conn;
DBusMessage* msg;
DBusPendingCall* pending;
int sum = 0;
DBusObjectPathVTable dbus_interface = { .message_function = &dbus_message_handler };
dbus_error_init(&error);
// 建立到会话总线的连接
conn = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (dbus_error_is_set(&error))
{
fprintf(stderr, "Error connecting to system bus: %s\n", error.message);
dbus_error_free(&error);
return EXIT_FAILURE;
}
// 注册对象路径和消息处理函数
dbus_bus_request_name(conn, "com.example.Calculator", 0, &error); //向dbus申请公共名
if (dbus_error_is_set(&error))
{
fprintf(stderr, "Error requesting name: %s\n", error.message);
dbus_error_free(&error);
return EXIT_FAILURE;
}
dbus_connection_register_object(conn, "/com/example/Calculator", &dbus_interface, NULL);
// 创建DBus消息,并将参数添加到消息中
int a = 2, b = 3;
msg = dbus_message_new_method_call("com.example.Calculator", "/com/example/Calculator", "com.example.Calculator", "Add");
DBusMessageIter iter;
dbus_message_iter_init_append(msg, &iter);
dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &a);
dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &b);
// 发送DBus消息并等待应答
dbus_connection_send_with_reply(conn, msg, &pending, -1);
dbus_message_unref(msg);
dbus_pending_call_set_notify(pending, dbus_reply_handler, &sum, NULL); //将应答消息放到dbus_reply_handler去处理
while (dbus_connection_read_write_dispatch(conn, -1)) {
}
while(1);
dbus_connection_unref(conn); //销毁连接
return EXIT_SUCCESS;
}
参考:https://blog.csdn.net/yishuige/article/details/52852531