[IPC] 初识GDBUS

DBUS

什么是DBUS

DBUS是一种进程间通信(IPC)机制,支持进程间一对一、多对多通信。系统启动时,会同时启动一个后台服务进程(dbus-daemon),当两个进程间进行消息传递时,进程A会先将消息传递给dbus-daemon,然后dbus-daemon在将消息转发给目标进程。

DBUS的种类

最低级的dbus库,非libdbus莫属了。用起来相对来说会比较麻烦。同时,还有一些基于特定框架(Qt, Glib、Python、C# 、Ruby、Java等等)的dbus编程框架的实现,它们有的依赖于libdbus,有的是基于Dbus Spec进行重新实现。

DBUS的应用

在dbus的官网上,可以找到使用dbus的项目。像Android中使用的Wi-Fi管理配置工具wpa_supplicant、Linux中的蓝牙协议栈BlueZ均使用到了dbus通信框架。

几个DBUS中的基本概念

以下内容摘自《D-Bus Tutorial》,对于初识dbus的同学,建议首先阅读此文章。

Object和ObjectPath

熟悉OO的同学可能更了解Object的概念。在Java、CPP中,Object是作为类的一个实例出现的。简单讲,一条狗,一个人都可以称之为一个对象。对象有自己的状态和行为。例如:

Object
状态 Name:二狗
Sex: 男
High: 一米二
行为 吃饭、睡觉、打豆豆

这些对象和行为放在编程语言内称之为Method和Property。

在libdbus中,有个概念NativeObject,但是并不理会这些NativeObject,而是使用了一个ObjectPath的概念。高层应用编程中创建对象进行通信时候,都会指定一个ObjectPath,用于dbus低层进行消息的路由。

Object Path的命名

/org/bluez/dev_00_11_22_33_44_55
/com/company/department/kafka

这个和Java中的包(Package)的概念很相似。

Method和Signal

方法就是一个函数,具有输入输出参数,信号是用来广播的。如果一个对象需要获取这个信号,它需要先对这个信号进行监听(watch)。信号中也可以包含有效信息。

在 D-BUS 中有四种类型的消息:

​ 方法调用(method calls)

​ 方法返回(method returns)

​ 信号(signals)

​ 错误(errors)。

要执行 D-BUS 对象的方法,您需要向对象发送一个方法调用消息。它将完成一些处理(就是执行了对象中的Method,Method是可以带有输入参数的。)并返回,返回消息或者错误消息。信号的不同之处在于它们不返回任何内容:既没有“信号返回”消息,也没有任何类型的错误消息。

Interface

接口是一类方法和信号的组合。每个对象可以包含一个和多个接口。接口的命名方法:com.module.bluetooth.scanorg.freedesktop.Introspectable

Proxy

在调用远程对象的方法时,可以在本地创建一个代理对象,这样就可以向调用本地对象一样,调用远程对象了。

BusName

当一个应用程序连接到总线上,总线会为他分配一个唯一的总线名称(unique connect name),这个名称以开头。

同时,应用程序也会给他指定一个名字(well-known name)。例如"com.alkaid.TextEditor"

总线名称还会有另外一个用途,当一个应用的生命周期结束,或者崩溃。Kernel会发送notification给其他的应用,通知某个应用生命周期已经结束。

Address

地址就是server用于监听,client用于连接的地方,对于bus-daemon来说,它就是server,应用就是client。

对于libdbus,通过检查环境变量获取session dbus-daemon的地址;通过检查一个特定的unix socket domain,获得system dbus-daemon的地址。

如果使用dbus,但是不使用dbus-daemon,那么你就要自己定义server和client,而且需要定义一套识别server和client的方法。这个并不常用。

通过上面的描述,我们可以获得下面的视图: dbus结构

  • 当Client连接到bus-daemon上时,他也就有了一个bus-name,应用为它指定一个well-known名称,bus-daemon为它分配一个unique名称。
  • 消息传递的时候,会根据ObjectPath,送至相应的对象实例。

Method Call的使用场景

一个Method消息从进程A发送到进程B,B进程将会返回一个Method消息或者Error消息,A进程的消息中包含一个唯一的序列号,同样B进程返回的消息中也包含一个同样的序列号。

如果使用proxy的方式,通过调用本地proxy对象的方法,proxy将构建一个对象发送到远端,触发远端对象的方法。

Signal的应用场景

Signal是个广播消息,是不需要响应的。

bus-daemon只会把signal转发给需要的对象。所以接收方需要在bus-daemon注册match-rules,包含发送方名称和signal名称,然后就会接收到相应的信号了。

signal的处理流程如下:

  • 一个signal发送到bus-daemon,signal包含bus-name、signal name、intf name、以及payload。
  • 接收方需要注册match-rules,match-rules用来告诉bus-daemon那些是我感兴趣的signal,然后bus-daemon进行转发。
  • 对于接收方,如果使用libdbus,他会检查bus-name和signal,然后决定如何处理;如果使用binding,binding将会在proxy object上发出一个native signal。

Introspection

dbus提供了一个方法org.freedesktop.DBus.Introspectable.Introspect,调用这个方法将会返回一个XML的字符串,可以用来查询特定对象支持的方法名。但是,查询出来的方法只是对象理论上支持的方法,实际并未实现。

GDbus编程示例

dbus的框架有很多,最近在学习蓝牙协议栈blueZ,使用到了GDbus,这个示例也是用此框架,初次使用,需要安装相应的库,可以参考此篇文章

如下Case,调用了org.freedesktop.DBus.ListActivatableNames方法,返回dbus-daemon上所有可以启动的服务名称。

#include   
#include   
#include   
#include   
#include   
#include   
#include 


#define LOG(fmt, args...) printf(fmt, ##args);
int main(void) {

    DBusError err;
    DBusConnection *dbus_conn;
    DBusMessage *message;
    int ret = 0;
    int i = 0;
    DBusMessageIter Iter, subIter;
    char *arr;

    DBusPendingCall *pending;


    dbus_error_init(&err);
    dbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);

    if(dbus_error_is_set(&err)) {
        LOG(err.message);
        return -1;
    }

    if(NULL == dbus_conn) {
        LOG("-2");
        return -2;
    }
/*
    //you'd create .conf in /etc/dbus-1/system.d/*.conf
    ret = dbus_bus_request_name(dbus_conn, "test.dbus.methodcall", DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
    if(dbus_error_is_set(&err)) {
        LOG("%s\n", err.message);
        return -3;
    }
    if(DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) {
        LOG("-4\n");
        return -4;
    }
*/
    //此方法没有输入参数,所以我们无需添加输入参数
    message = dbus_message_new_method_call("org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListActivatableNames");

    dbus_connection_send_with_reply(dbus_conn, message, &pending, -1);

    dbus_connection_flush(dbus_conn);

    dbus_message_unref(message);
    //此处阻塞,知道调用完成,如果上面设定了timeout,超时也会返回
    dbus_pending_call_block(pending);
    //get reply message
    message = dbus_pending_call_steal_reply(pending);

    if(!dbus_message_iter_init(message, &Iter)) {
        LOG("-6\n");
    }
    LOG("type '%c'\n", dbus_message_iter_get_arg_type(&Iter));
    dbus_message_iter_recurse(&Iter, &subIter);    //因为方法返回的是array String,这里需要吧Iter中的数据迭代到subIter中,然后再get_basic
    LOG("type '%c'\n", dbus_message_iter_get_arg_type(&subIter));

    for(i = 0; i < 49; i++) {
        dbus_message_iter_get_basic(&subIter , &arr);
        dbus_message_iter_next(&subIter);          // 指向下一个
        LOG("%s\n", arr);
    }

    printf("hello World\n");
}

.

你可能感兴趣的:(linux,ipc,dbus)