linux系统进程间通过dbus通信,D-BUS由总线构成,总线分为两种,系统总线(system-bus)和会话总线(session-bus)。系统总线在引导时就会启动,这个总线由操作系统和后台进程使用,安全性非常好,以使得任意的应用程序不能欺骗系统事件.会话总线在用户登陆后启动,属于用户私有,是用户进程用来通信的一个会话总线。如果一个应用程序需要接收来自系统总线的消息,可以直接连接到系统总线 —— 不过,它可以发送的消息将是受限的。只有Linux内核、Linux桌面环境和权限较高的程序才能向系统总线写入消息,以此保障系统安全性,防止有恶意进程假冒Linux发送消息。
每个应用程序连接到总线后,指定一个服务名,bus name;
消息发送到指定的对象,对象格式类似路径名称:/com/jimi/dbus
一组功能的集合名字,com.jimi.test,接口下实现很多方法或者信号
在 D-BUS 中有四种类型的消息:方法调用(method calls)、方法返回(method returns)、信号(signals)和错误(errors)。要执行 D-BUS 对象的方法,您需要向对象发送一个方法调用消息。它将完成一些处理(就是执行了对象中的Method,Method是可以带有输入参数的。)并返回,返回消息或者错误消息。信号的不同之处在于它们不返回任何内容:既没有“信号返回”消息,也没有任何类型的错误消息
signal
发送信号,接收者获取信号,并响应信号的事件,信号不返回数据。
method
客户端调用服务端的方法,有输入,有输出。
当一个应用连接到bus daemon,daemon立即会分配一个名字给这个连接,称为unique connection name ,这个唯一标识的名字以冒号:开头,例如:34-907,这个名字在daemon的整个生命周期是唯一的。但是这种名字总是临时分配,无法确定的,也难以记忆,因此应用可以要求有另外一个名字well-known name 来对应这个唯一标识,就像我们使用域名来对应IP地址一样。例如可以使用com.mycompany来映射:34-907。
Address –> [BusName] –> Path –> Interface –> Method
bus name不是必要的,它只在daemon的情况下用于路由,点对点的直接连接是不需要的。
简单地说:Address是D-Bus中server用来监听client的地址,当一个client连接上D-Bus,通常是Daemo的方式,这个client就有了一个Bus Name。其他应用可以根据消息中所带的Bus Name,来判断和哪个应用相关。消息在总线中传递的时候,传递到应用中,再根据objectpath,送至应用中具体的对象实例中,也就是是应用中根据Interface创建的对象。这些Interface有method和singal两种,用来发送、接收、响应消息。
消息通过D-Bus在进程间传递。有四类消息:
一、Method call消息:将触发对象的一个method
二、Method return消息:触发的方法返回的结果
三、Error消息:触发的方法返回一个异常
四、Signal消息:通知,可以看作为事件消息。
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using json = nlohmann::json;
static DBusConnection *connect;
const char * object_path = "/com/jimi/dbus";
const char * signal_object_path = "/signal/jimi/dbus";
const char * method_interface = "ipc.method";
const char * signal_interface = "ipc.signal";
void handle_unregister(DBusConnection * connection, void * user_data);
DBusHandlerResult message_handler(DBusConnection * connection,DBusMessage * msg,void * user_data);
int dbus_send_signal(DBusConnection * connection,const char *signalName, char *value);
static DBusObjectPathVTable server_path_vt = {
.unregister_function = handle_unregister,
.message_function = message_handler
};
typedef struct {
dbus_uint32_t id;
}ConnectData;
ConnectData server_data = {0};
void reply_to_method_call(DBusMessage * msg, DBusConnection * conn){
DBusMessage * reply;
DBusMessageIter arg;
char * param = NULL;
dbus_bool_t stat = TRUE;
dbus_uint32_t level = 2010;
dbus_uint32_t serial = 0;
//从msg中读取参数,这个在上一次学习中学过
if(!dbus_message_iter_init(msg,&arg))
printf("Message has no args/n");
else if(dbus_message_iter_get_arg_type(&arg) != DBUS_TYPE_STRING)
printf("Arg is not string!/n");
else
dbus_message_iter_get_basic(&arg,¶m);
printf("%s",param);
if(param == NULL) return;
string str_reply = param;
//创建返回消息reply
reply = dbus_message_new_method_return(msg);
//在返回消息中填入两个参数,和信号加入参数的方式是一样的。这次我们将加入两个参数。
dbus_message_iter_init_append(reply,&arg);
if(!dbus_message_iter_append_basic (&arg,DBUS_TYPE_BOOLEAN,&stat)){
printf("Out of Memory!/n");
exit(1);
}
if(!dbus_message_iter_append_basic (&arg,DBUS_TYPE_UINT32,&level)){
printf("Out of Memory!/n");
exit(1);
}
if(!dbus_message_iter_append_basic (&arg,DBUS_TYPE_STRING,&str_reply)){
printf("Out of Memory!/n");
exit(1);
}
//发送返回消息
if( !dbus_connection_send (conn, reply, &serial)){
printf("Out of Memory/n");
exit(1);
}
dbus_connection_flush (conn);
dbus_message_unref (reply);
}
void reply_to_introspec(DBusMessage * msg, DBusConnection * conn){
DBusMessage * reply;
DBusMessageIter arg;
dbus_uint32_t serial = 0;
string str_reply;
//读取xml,xml写入了path下的所有接口及方法,d-feet可以查询
ifstream in("/home/uos/dbus_test.xml");
stringstream buffer;
buffer<<in.rdbuf();
str_reply = buffer.str();
printf("%s",str_reply.c_str());
//创建返回消息reply
reply = dbus_message_new_method_return(msg);
dbus_message_iter_init_append(reply,&arg);
if(!dbus_message_iter_append_basic (&arg,DBUS_TYPE_STRING,&str_reply)){
printf("Out of Memory!/n");
exit(1);
}
//发送返回消息
if( !dbus_connection_send (conn, reply, &serial)){
printf("Out of Memory/n");
exit(1);
}
dbus_connection_flush (conn);
dbus_message_unref (reply);
}
DBusHandlerResult message_handler(DBusConnection * connection,DBusMessage * msg,void * user_data)
{
DBusMessageIter arg;
char * sigvalue;
if(!msg){
return DBUS_HANDLER_RESULT_HANDLED;
}
switch(dbus_message_get_type(msg)){
case DBUS_MESSAGE_TYPE_METHOD_CALL:
{
if(dbus_message_is_method_call(msg,method_interface,"test_method")){
auto path = dbus_message_get_path (msg);
if(strcmp(path,object_path) == 0)
{
reply_to_method_call(msg, connection);
}
}
else if(dbus_message_is_method_call(msg, DBUS_INTERFACE_INTROSPECTABLE,"Introspect")){ //"org.freedesktop.DBus.Introspectable"
auto path = dbus_message_get_path (msg);
if(strcmp(path,object_path) == 0)
{
reply_to_introspec(msg, connection);
}
}
break;
}
case DBUS_MESSAGE_TYPE_SIGNAL:
{
if(dbus_message_is_signal(msg, signal_interface, "test_signal")){
if(!dbus_message_iter_init(msg,&arg))
fprintf(stderr,"Message Has no Param");
else if(dbus_message_iter_get_arg_type(&arg) != DBUS_TYPE_STRING)
fprintf(stderr,"Param is not string");
else
{
dbus_message_iter_get_basic(&arg,&sigvalue);
//信号内容
printf("get signal content is : %s",sigvalue);
fflush(stdout);
//dbus_send_signal(connection, "test_signal", sigvalue);
reply_to_method_call(msg, connection);
}
}
break;
}
case DBUS_MESSAGE_TYPE_METHOD_RETURN:
{
return DBUS_HANDLER_RESULT_HANDLED;
}
case DBUS_MESSAGE_TYPE_INVALID:
{
return DBUS_HANDLER_RESULT_HANDLED;
}
default:
return DBUS_HANDLER_RESULT_HANDLED;
}
}
void handle_unregister(DBusConnection * connection, void * user_data){
dbus_free(user_data);
}
void dbus_server()
{
DBusMessage * msg;
DBusMessageIter arg;
DBusConnection * connection;
DBusError err;
int ret;
char * sigvalue;
dbus_error_init(&err);
//创建于session D-Bus的连接
connection = dbus_bus_get(DBUS_BUS_SESSION, &err);
if(dbus_error_is_set(&err)){
fprintf(stderr,"Connection Error %s/n",err.message);
dbus_error_free(&err);
}
if(connection == NULL)
return;
//设置一个BUS name:test.wei.dest
ret = dbus_bus_request_name(connection,"com.jimi.dbus",DBUS_NAME_FLAG_REPLACE_EXISTING,&err);
if(dbus_error_is_set(&err)){
fprintf(stderr,"Name Error %s/n",err.message);
dbus_error_free(&err);
}
if(ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
return;
ret = dbus_connection_register_object_path(connection,object_path,&server_path_vt,&server_data);
if(dbus_error_is_set(&err)){
fprintf(stderr,"Name Error %s/n",err.message);
dbus_error_free(&err);
}
//消息循环
while(dbus_connection_read_write_dispatch(connection,-1)){
}
}
int main(){
dbus_server();
return 0;
}
以上是一个完整的dbus服务器示例,可以通过d-feet查看接口方法,如果你写的dbus程序在d-feet下看不到对象路径,功能方法调用正常,则是因为没有实现总线的标准接口 org.freedesktop.DBus.Introspectable 的方法Introspect,具体实现如上述代码,introspect方法返回的是xml数据,xml数据定义了接口-方法-参数-类型-入\出方向,d-feet能够显示dbus-server的接口方法,是因为调用了标准接口的Instrospect回显了所有的方法和信号。
dbus_test.xml内容定义如下:
DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="ipc.signal">
<signal name="test_signal">
<arg name="info" type="s" direction="out"/>
signal>
interface>
<interface name="ipc.method">
<method name="test_method">
<arg name="input" type="s" direction="in"/>
method>
interface>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="xml_data" type="s" direction="out"/>
method>
interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg name="interface" type="s" direction="in"/>
<arg name="propname" type="s" direction="in"/>
<arg name="value" type="s" direction="out"/>
method>
<method name="Set">
<arg name="interface" type="s" direction="in"/>
<arg name="propname" type="s" direction="in"/>
<arg name="value" type="s" direction="in"/>
method>
<method name="GetAll">
<arg name="interface" type="s" direction="in"/>
<arg name="props" type="a{sv}" direction="out"/>
method>
interface>
node>
Usage: dbus-send [--help] [--system | --session | --bus=ADDRESS | --peer=ADDRESS] [--dest=NAME] [--type=TYPE] [--print-reply[=literal]] [--reply-timeout=MSEC] <destination object path> <message name> [contents ...]
–system root用户进程dbus
–session 用户进程dbus
–print-reply 打印输出
–type=method_call / signal
–dest 目的服务名 方法路径 接口+method 参数类型:参数值