轉載請註明出處 做者: 唐風 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)
參考資料: