D-Bus : 是一个为应用程序间通信的消息总线系统, 用于进程之间的通信。
相较于传统的 管道(PIPE)
、 Socket
等原生基于字节流的IPC方式, D-Bus
提供了基于独立 Message
的传输方式,应用程序使用起来更加简单。
想一下传统进程间通信方式及用法?
D-Bus
的常用架构与传统的 Socket
一对一通信模式不同,它基于中间消息路由转发的模式来实现, 如下图:
D-Bus
默认提供两种BUS:系统BUS(system)
和 会话BUS(session)
。
系统BUS在每台机器上是惟一的,用于后台服务及操作系统之间的通信。
会话BUS用于每个登录用户会话的应用程序之间的通信。每个BUS实例由一个 bus-daemon
进程来管理,由其负责消息路由转发。应用程序需要收发消息,需要连接到BUS实例上。BUS实例使用基于XML的配置文件来控制安全策略,如用户能否注册服务,能给哪些服务接口发送消息等等。
下面我们用一个基于 libdbus
库实现的示例来说明简单的方法调用。
libdbus 官方API 参考链接 : https://dbus.freedesktop.org/doc/api/html/group__DBusBus.html
/* dbus_open : 建立一个dbus连接之后,为这个dbus连接起名 svrname : 客户端请求注册的名称 */ int dbus_open(const char * svrname) { DBusConnection* conn; DBusError err; int ret; /* DBUS_EXPORT void dbus_connection_set_change_sigpipe(dbus_bool_t will_modify_sigpipe) 此函数用于设置dbus_connection_new()是否将sigpippe behavior设置为SIG_IGN的全局标志。 TRUE以允许将sigpipe设置为SIG FALSE以禁止将sigpipe设置为SIG */ dbus_connection_set_change_sigpipe(FALSE); /*DBUS_EXPORT void dbus_error_init(DBusError * error) 初始化 DBusError结构体,不分配任何内存;仅当错误在某个点设置时才需要释放它。 */ dbus_error_init(&err); //conn = dbus_bus_get(DBUS_BUS_SESSION, &err); /* DBUS_EXPORT DBusConnection * dbus_bus_get(DBusBusType type,DBusError * error) 连接到总线守护程序,并向其注册客户端。 Parameters type bus 类型,这边是DBUS_BUS_SYSTEM :系统BUS error 返回错误的地址. Returns a DBusConnection with new ref or NULL on error */ conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); if (dbus_error_is_set(&err))//检查是否发生错误(已设置错误)。 { fprintf (stderr, "%s: Connection Error (%s)\n", __FUNCTION__, err.message); dbus_error_free(&err); conn = NULL; } if(conn == NULL) { return (int)NULL; } if(svrname != NULL) { if(svrname[0] != '\0') { /*DBUS_EXPORT int dbus_bus_request_name (DBusConnection * connection, const char * name, unsigned int flags, DBusError * error ) 请求总线通过调用总线上的 request_name 方法将给定名称分配给此连接。 */ ret = dbus_bus_request_name(conn, svrname, DBUS_NAME_FLAG_REPLACE_EXISTING , &err); if (dbus_error_is_set(&err)) { fprintf (stderr, "%s: Request Name(%s) Error (%s)\n", __FUNCTION__, svrname, err.message); dbus_error_free(&err); return (int)NULL; } /*返回 DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 时 , 表示 该名称未被注册可以使用*/ if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) { fprintf (stderr, "%s: The primary owner is not me\n", __FUNCTION__); return (int)NULL; } } } return (int)conn; }
/* 第一步 :建立一个dbus连接之后,为这个dbus连接起名, 第二步 :为我们将要进行的消息循环添加匹配条件 (就是通过信号名和信号接口名来进行匹配控制的) -- dbus_bus_add_match()。 我们进入等待循环后,只需要对信号名,信号接口名进行判断就可以分别处理各种信号了。在各个处理分支上。我们可以分离出消息中的参数。对参数类型进行判断和其他的处理。 */ int dbus_setup_interface(int did, const char * path, const char * inf) { DBusConnection* conn; DBusError err; char rule[DBUS_MAXIMUM_NAME_LENGTH + DBUS_MAXIMUM_NAME_LENGTH + 64]; int inf_len = 0; int path_len = 0; conn = (DBusConnection*)did; if(conn == NULL || (inf == NULL && path == NULL)) { return -1; } if(inf != NULL) { inf_len = strlen(inf); } if(path != NULL) { path_len = strlen(path); } if((inf_len == 0 && path_len == 0) || inf_len > DBUS_MAXIMUM_NAME_LENGTH || path_len > DBUS_MAXIMUM_NAME_LENGTH) { return -1; } else if(inf_len != 0 && path_len == 0) { sprintf(rule, "interface='%s'", inf); } else if(inf_len == 0 && path_len != 0) { sprintf(rule, "path='%s'", path); } else { sprintf(rule, "interface='%s',path='%s'", inf, path); } dbus_error_init(&err); /*DBUS_EXPORT void dbus_bus_add_match(DBusConnection * connection, const char * rule, DBusError* error) :为我们将要进行的消息循环添加匹配条件 Parameters connection connection to the message bus rule textual form of match rule error location to store any errors */ dbus_bus_add_match(conn, rule, &err); /*如果执行dbus_connection_flush()函数,那么进程将被阻塞,直到发送队列中的消息被通过SOCKET完全传送出去;如果不执行该函数,则会在下次主循环执行dbus_watch_handle的时候被通过SOCKET传送出去。*/ dbus_connection_flush(conn); if (dbus_error_is_set(&err)) { fprintf (stderr, "%s: Match Error (%s)\n", __FUNCTION__, err.message); return -1; } return 0; }