其实dbus_bus_get所做的事情远比我们以为的复杂的多,首先做的第一件事情是,初始化bus_connection_addresses这个指针数组,指针指向的是总线地址,主要是通过环境变量来获取三种总线连接的地址,不同系统会有不一样的配置。根据用户指定需要连接的总线类型指定分别建立一张Hash表来管理DBusConnection。第二步:调用dbus_bus_register接口主要做的事情是调用一个系统标准的method叫"Hello"来获取一个unique name。
//建立连接 dbus_error_init(&err); conn = dbus_bus_get(DBUS_BUS_SESSION, &err); if(!conn && dbus_error_is_set(&err)) { printf("Error: %s\n", err.message); return (-1); } //设置为当收到disconnect信号的时候不退出应用程序(_exit()) dbus_connection_set_exit_on_disconnect(conn, FALSE);
dbus_bus_request_name里面也用到了标准method:"RequestName"来申请名称。
//请求获取公共名 nRet = dbus_bus_request_name(conn, CLIENT_WELL_NAME, DBUS_NAME_FLAG_REPLACE_EXISTING, &err); if(DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != nRet) { printf("request name failed \n"); return (-1); }
static void emit_signal(DBusConnection* conn) { DBusMessage *message = NULL; dbus_bool_t nRet = FALSE; const char *v_STRING = "emit_signal"; dbus_uint32_t serial = 0; message = dbus_message_new_signal(CLIENT_OBJ_PATH, CLIENT_INTERFACE, CLIENT_SIGNAL); if(message == NULL) { fprintf(stderr, "Message NULL\n"); return; } nRet = dbus_message_append_args(message, DBUS_TYPE_STRING, &v_STRING, DBUS_TYPE_INVALID); dbus_connection_send(conn, message, &serial); if(FALSE == nRet) { fprintf(stderr, "(send)Out of Memeory\n"); return; } dbus_connection_flush(conn); dbus_message_unref(message); message = NULL; printf("\nemit signal\n"); }
static void method_call(DBusConnection* conn) { DBusMessage *message = NULL; dbus_bool_t nRet = FALSE; const char *v_STRING = "method_call"; DBusPendingCall* Pending; DBusMessageIter args; message = dbus_message_new_method_call(SERVER_WELL_NAME, SERVER_OBJ_PATH, SERVER_INTERFACE, SERVER_METHOD); if(message == NULL) { fprintf(stderr, "Message NULL\n"); return; } nRet = dbus_message_append_args (message, DBUS_TYPE_STRING, &v_STRING, DBUS_TYPE_INVALID); dbus_connection_send_with_reply(conn, message, &Pending, -1); if(FALSE == nRet) { fprintf(stderr, "(send)Out of Memeory\n"); return; } /* if(FALSE == dbus_pending_call_set_notify(Pending, method_call_notify, NULL, NULL)) { printf("OMM\n"); } dbus_connection_flush(conn); dbus_message_unref(message); message = NULL; if(TRUE == dbus_pending_call_get_completed(Pending)) { printf("complete\n"); } dbus_pending_call_unref(Pending); */ dbus_connection_flush(conn); dbus_message_unref(message); message = NULL; dbus_pending_call_block(Pending); message = dbus_pending_call_steal_reply(Pending); if (NULL == message) { fprintf(stderr, "Reply Null\n"); exit(1); } // free the pending message handle dbus_pending_call_unref(Pending); char* level = NULL; // read the parameters if (!dbus_message_iter_init(message, &args)) fprintf(stderr, "Message has no arguments!\n"); else if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type(&args)) fprintf(stderr, "Argument is not int!\n"); else dbus_message_iter_get_basic(&args, &level); printf("Got Reply: %s\n", level); dbus_message_unref(message); }
有些时候我们有这的要求例如:我们发送消息不希望一直阻塞在发送接口而当有应答消息的时候希望系统给我们提示。DBUS也有类似的机制我们在调用dbus_connection_send_with_reply接口发送消息之后可以调用dbus_pending_call_set_notify接口为Pedning消息设定一个notify函数,当有消息应答的时候我们自己设定的函数被触发这样就达到了异步通信的目的。在多线程里面我们常常会有这样的需求,但是DBus对多线程支持的并不好。换句话说,dbus_pending_call_set_notify是非线程安全的,假如我们有一个线程调用了dbus_pending_call_set_notify接口等待应答触发,另外一个线程调用了dispatcher 分发函数把接收到的消息处理,那么第一个线程的notify永远不会被触发。在网络上查找了相关资料之后发现,dbus_pending_call_set_notify和多线程只能二者选择一个。如果要使用多线程消息发送的时候最好调用send_with_reply_and_block()发送消息,然后再设置notify函数,最后用dbus_pending_call_steal_reply()提取出消息。
1. dbus_connection_read_write(conn, 0); 2. msg = dbus_connection_pop_message(conn)
while(1) { // sleep(1); while(dbus_connection_read_write_dispatch(conn, -1)); }小结:
利用DBus作为进程间通信方式,我们经常会使用Glib或者python的binding版本,而不是直接使用DBus原始API,但是了解底层实现还是很有必要的,否则一种只见树木不见森林的感觉会一直萦绕。上面我们介绍了没有main loop的情况下一些通信方法。后面会把事件循环结合起来研究一下上层binding的一些实现。
参考文献:http://en.it-usenet.org/thread/12034/6281/