转载请注明出处 做者: 唐风 html
DBus是用来进行进程间通讯的。下面这张图展现了一些DBus的大部分东西,可是它太复杂了: 编程
DBus 自己是构建在 Socket 机制之上。真正的通讯仍是由 Socket 来完成的。DBus 则是在这之上,制定了一些通讯的协议,并提供了更高一层的接口,以更方便应用程序之间进行数据的交互。 windows
在DBus的体系中,有一个常驻的进程 Daemon,全部进程间的交互都经过它来进行分发和管理。全部但愿使用 DBus 进行通讯的进程,都必须事先连上 Daemon,并将本身的名字注册到 Daemon 上,以后,Daemon会根据须要把消息以及数据发到相应的进程中。 api
首先使用 数据结构
1 conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
让应用程序和 DBus 之间取得链接。以后,使用函数 app
1 ret = dbus_bus_request_name(conn, "test.method.server", 2 DBUS_NAME_FLAG_REPLACE_EXISTING 3 , &err);
将本身的进程名字注册到 Daemon 上。(参考前篇的[共通用代码])。这样通讯就有了基础了。 框架
DBus 提供的最简单的一种通讯方式是信号(Signal),应用程序能够发送一个信号到 Daemon 上,以后,Daemon 会根据信号的种类和谁但愿获得信号等信息,把相应的数据发给每一个但愿获得信号的进程。也就是 Signal 具备广播的功能。信号具备两个基本的属性,一个是名称,用来标识各个不一样的信号,一个是数据,信号是能够带必定的数据的。Signal 的通讯过程能够用下面的图大概展现出来: 异步
若是一个进程(好比 B )想接收接口名为 test.signal.Type 的信号,那么可使用下面的函数向 Daemon 添加匹配信号,让 Daemon 知道本身对这种信号感兴趣。 函数
1 // add a rule for which messages we want to see 2 dbus_bus_add_match(conn, 3 "type='signal',interface='test.signal.Type'", 4 &err); // see signals from the given interface
而后, 进程 B 可使用下面的函数来进行等待: ui
1 dbus_connection_read_write(conn, 0); 2 msg = dbus_connection_pop_message(conn);
一旦有消息发送过来,进程 B 就能够经过 msg 取到相应的数据了。(参考前篇代码段[接收消息一、2] )
如今有一个进程 A,
1 dbus_uint32_t serial = 0; // unique number to associate replies with requests 2 DBusMessage* msg; 3 4 // create a signal and check for errors 5 msg = dbus_message_new_signal("/test/signal/Object", // object name of the signal 6 "test.signal.Type", // interface name of the signal 7 "Test"); // name of the signal 8 9 // append arguments onto signal 10 dbus_message_iter_init_append(msg, &args); 11 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &sigvalue)) { 12 fprintf(stderr, "Out Of Memory!\n"); 13 exit(1); 14 } 15 16 // send the message and flush the connection 17 if (!dbus_connection_send(conn, msg, &serial)) { 18 fprintf(stderr, "Out Of Memory!\n"); 19 exit(1); 20 }
到这里为止,已经涉及到了几个基本而又核心的概念,搞清楚它们,几乎就大概知道了 DBus 的使用方法了。
DBusMessage 是 DBus 中的核心数据结构。能够这样理解:DBus中传递消息数据的时候,就是经过它来传递的。对于使用者来讲,DBusMessage 中存储了两种重要的信息,一种是为通讯机制服务的各类 Name,一种是通讯的数据自己。
各类名字(Name)
在前面用到的不少接口中都还有名称/路径字符串做为参数。
DBus Name: 在 DBus 中最为重要的名字是“Bus Name”,Bus Name 是一个每一个应用程序(或是通讯对象)用来标识本身用的。几乎能够当成是“IP”地址来理解。Bus Name有两种,一种是“Unique Connection Name”,是以冒号开头的,是全局惟一但“人类不友好”的命名,一种是“Well-know Name”,人类友好的。Bus Name 的命名规则是:
1 Bus name 就像网址同样,由“.”号分割的多个子字符串组成,每一个子字符串都必须至少有一个以上的字符。 2 每一个子字符串都只能由“[A-Z][a-z][0-9]_-”这些 ASCII 字符组成,只有 Unique Name 的子串能够以数字开头。 3 每一个 Bus name 至少要有一个“.”,和两个子字符串,不能以“.”开头 4 Bus name 不能超过 255 个字符
几个例子是:Unique Name :392-2.33 org.freedesktop.DBus 等等
DBus Name 是用来给应用程序进行标识本身的,因此每当程序连上 DBus Daemon 后,就会分配到一个 Unique Name,同时应用程序还能够要求本身分配另外一个 Well-know name (经过 dbus_bus_request_name 函数)。
Interface name: DBus 也有 interface 这个概念,主要是用来为更高一层的框架使用方面而设定的。在 C API 这一层,你几乎能够无视这个概念,只须要知道这个一个“字符串”,并在消息匹配是被 DBus 使用到,会随着消息在不一样的进程以前传递,从进程 A 发送一个消息或是数据到进程 B 时,其中一定会带有一个部分就是这个字符串,至于 B 进程怎么用(或是无视它)均可以。它的命名规则与 DBus Name 几乎是同样的,只有一点要注意,interface name 中不能带有“-”字符。
Object path:DBus 中的 object path,与 interface 同样,也只是个概念在更高一层的框架(QT Dbus)中才比较有用,在 C API 这一层,几乎能够无视这个概念,把它当成一个普通的字符串,根据通讯的须要,用来作一种标识和区分。Object path 的命名规则是:/com/example/MusicPlayer1
1 object path 能够是任意长度的 2 以'/'开头,并以以'/'分隔的若干子字符串组成 3 每一个子串必须由“[A-Z][a-z][0-9]_”中的字符组成 4 不能有空子串(也就是不能连续两个'/'符) 5 除了“root path”('/')以外,不能再有 object path 是以 '/' 结尾的了。
一个 object path 的例子:/com/example/MusicPlayer1
Member name:Member 包含两种类型,一种是 Signal,一种是 Method。在大多数方面,他们几乎是同样的,除了两点:1. Signal是在总线中进行广播的,而Method是指定发给某个进程的。2. Signal 不会有返回,而 Method 必定会有返回(同步的或是异步的)。Member name的命名规则是这样的:
1 只能包含"[A-Z][a-z][0-9]_"这些字符,且不能以数字开头。不能包含“.”。 2 不能超过255个字符
以 C API 的层面来看,Member name最大的做用就是在两个进程间共享“发出的消息的类型信息”。DBus 只能以 Signal / Method 来进行消息通讯,这两种方式都容许在消息发出以前,在消息中 append 各类类型的数据,当通讯的对方收到消息后,它就能够经过 Signal / Method 的名称知道如何把各类数据再解析出来。
到如今为止,已经介绍了三种最为重要的 Name,若是与的熟悉 windows 消息机制对比的话,我大概以为,DBus Name 就是进程的 ID,有了这个你才能把消息发给指定的进程,object path ,interface等概念在“(QT等)高层框架中才有意义”,C API 级别的使用的话,能够无视它的概念,当成消息甄别用的信息就行了。 Member name 至关于 Message type,有了它你才知道怎么去解析发过来的数据。
下篇将会记录 DBusMessage 另外一个主要的组成部分:通讯数据。
1 DBusMessage* msg; 2 DBusMessageIter args; 3 4 // msg... 5 6 dbus_message_iter_init_append(msg, &args); 7 dbus_uint32_t my_data = 10; 8 if(!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &my_data)) { 9 printf("Out of memory\n"); 10 return RES_FAILED; 11 } 12 13 dbus_connection_flush(conn); 14 dbus_message_unref(msg);
1 DBusMessage* msg; 2 DBusMessageIter args; 3 4 // get a DBusMessage from process A 5 6 if(!dbus_message_iter_init(msg, &args)) { 7 printf("dbus_message_iter_init error, msg has no arguments!\n"); 8 } 9 else if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&args)){ 10 printf("not a uint 32 type !\n"); 11 } 12 else { 13 dbus_uint32_t my_age = 0; 14 dbus_message_iter_get_basic(&args, &my_age); 15 printf("Got signal with value %d\n", my_age); 16 }
很简单,同样的先使用 dbus_messge_iter_init 先把 DBusMessage 对象和从 DBus 总线中取到的 msg 关联起来。
这样,使用第 9 行的函数先取得第一个通讯数据中第一个参数的类型,若是类型无误的话能够进而使用第 14 行的函数取得参数值自己。
这样,一个简单的数据如何入到 DBusMessage 中,又如何从 DBusMessage 中取出来就明白了。那么如何将 DBusMessage 在进程之间传递呢?
1 dbus_connection_read_write(conn, 0); 2 msg = dbus_connection_pop_message(conn); 3 4 if(NULL == msg) { 5 sleep(1); 6 continue; 7 }
使用 1 和 2 行的代码就能够取出发送到本进程的消息,以后就可使用 msg (若是 msg 不是 NULL 的话)来获取通讯数据了。
到这里,基本概念就有了。后面,应该对 DBus 的细节再深刻的探索。
Sample 代码:
发送方进程(my_client):
接收方进程(my_server.cpp)
参考资料: