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消息:通知,可以看作为事件消息。
libdbus 服务端demo#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< 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内容定义如下:
dbus-send 用法
Usage: dbus-send [--help] [--system | --session | --bus=ADDRESS | --peer=ADDRESS] [--dest=NAME] [--type=TYPE] [--print-reply[=literal]] [--reply-timeout=MSEC][contents ...]
–system root用户进程dbus
–session 用户进程dbus
–print-reply 打印输出
–type=method_call / signal
–dest 目的服务名 方法路径 接口+method 参数类型:参数值
方法测试 信号测试


