DBus學習筆記

摘要:DBus做爲一個輕量級的IPC被愈來愈多的平臺接受,在MeeGo中DBus也是主要的進程間通訊方式,這個筆記將從基本概念開始記錄筆者學習DBus的過程

[1] DBus學習筆記一:DBus學習的一些參考資料
[2] DBus學習筆記二:什麼是DBus? 
[3] DBus學習筆記三:DBus的一些基本概念 html

 

一些基本概念的解釋和翻譯:
http://blog.mcuol.com/User/AT91RM9200/Article/12816_1.htm
http://www.cnblogs.com/wzh206/archive/2010/05/13/1734901.html
一個完整的DBus學習教程(強烈推薦,寫得至關的全):
http://blog.csdn.net/fmddlmyy/archive/2008/12/23/3585730.aspx
兩個DBus的完整示例,至關有參考價值
http://blog.chinaunix.net/u1/58649/showart_462468.html
http://hi.baidu.com/zengzhaonong/blog/item/670b98d6e63ae42c07088bae.html
DBus官方網站,最原滋原味的DBus學習內容
http://www.freedesktop.org/wiki/Software/dbus
http://dbus.freedesktop.org/doc/dbus-tutorial.html
http://dbus.freedesktop.org/doc/dbus-specification.html
歡迎你們提供更多的DBus學習資料,一塊兒研究java

 

 

DBus是一種IPC機制,由freedesktop.org項目提供,使用GPL許可證發行,用於進程間通訊或進程與內核的通訊。
:Linux中的IPC通訊機制還包括,管道(fifo),共享內存,信號量,消息隊列,Socket等。
DBus進程間通訊主要有三層架構
1.底層接口層:主要是經過libdbus這個函數庫,給予系統使用DBus的能力。 
2.總線層:主 要Message bus daemon這個總線守護進程提供的,在Linux系統啓動時運行,負責進程間的消息路由和傳遞,其中包括Linux內核和Linux桌面環境的消息傳 遞。總線守護進程可同時與多個應用程序相連,並能把來自一個應用程序的消息路由到0或者多個其餘程序。 
3.應用封裝層:經過一系列基於特定應用程序框架將DBus的底層接口封裝成友好的Wrapper庫,供不一樣開發人員使用(DBus官方主頁http://www.freedesktop.org/wiki/Software/dbus,提供了大部分編程語言的DBus庫版本)。好比libdbus-glib, libdbus-python.node

 

如上圖所示,Bus Daemon Process是運行在linux系統中的一個後臺守護進程,dbus-daemon運行時會調用libdus的庫。Application Process1表明的就是應用程序進程,經過調用特定的應用程序框架的Wrapper庫與dbus-daemon進行通訊。
從上圖也能夠看出來Application和Daemon中其實仍是經過socket進行通行。python

DBus的三大優勢:低延遲,低開銷,高可用性。linux

*低延遲:DBus一開始就是用來設計成避免來回傳遞和容許異步操做的。所以雖然在Application和Daemon之間是經過socket實現的,可是又去掉了socket的循環等待,保證了操做的實時高效。ios

*低開銷:DBus使用一個二進制的協議,不須要轉化成像XML這樣的文本格式。由於DBus是主要用來機器內部的IPC,而不是爲了網絡上的IPC機制而準備的.因此它纔可以在本機內部達到最優效果。程序員

*高可用性:DBus是基於消息機制而不是字節流機制。它能自動管理一大堆困難的IPC問題。一樣的,DBus庫被設計來讓程序員可以使用他們已經寫好的代碼。而不會讓他們放棄已經寫好的代碼,被迫經過學習新的IPC機制來根據新的IPC特性重寫這些代碼。shell

 

 

在介紹基本概念以前,先介紹一個學習DBus的好工具d-feet,這個工具主要是用來查看DBus的System Bus和Session Bus中的各個消息鏈接的。固然,你也能夠在這裏面添加本身建立的消息總線,以便於觀察。apache

 

下面根據上圖介紹一下DBus中的一些基本概念。編程

會話總線(Session Buses普通進程建立,可同時存在多條。會話總線屬於某個進程私有,它用於進程間傳遞消息。

系統總線(System Bus在引導時就會啓動,它由操做系統和後臺進程使用,安全性很是好,以使得任意的應用程序不能欺騙系統事件。固然,若是一個應用程序須要接受來自系統總線的消息,他也能夠直接鏈接到系統總線中,可是他能發送的消息是受限的。

Bus Name按字面理解爲總線名稱貌似不是很貼切,應該是一個鏈接名稱,主要是用來標識一個應用和消息總線的鏈接。從上圖能夠看出來,總線名稱主要分爲兩類

"org.kde.StatusNotifierWatcher"這種形式的稱爲公共名(well-knownname)

":1.3"這種形式的稱爲惟一名(Unique Name)

公共名提供衆所周知的服務。其餘應用經過這個名稱來使用名稱對應的服務。可能有多個鏈接要求提供同個公共名的服 務,即多個應用鏈接到消息總線,要求提供同個公共名的服務。消息總線會把這些鏈接排在鏈表中,並選擇一個鏈接提供公共名錶明的服務。能夠說這個提供服務的 鏈接擁有了這個公共名。若是這個鏈接退出了,消息總線會從鏈表中選擇下一個鏈接提供服務。

惟一名以「:」開頭,「:」後面一般是圓點分隔的兩個數字,例如「:1.0」。每一個鏈接都有一個惟一名。在一個 消息總線的生命期內,不會有兩個鏈接有相同的惟一名。擁有公衆名的鏈接一樣有惟一名,例如在前面的圖 中,「org.kde.StatusNotifierWatcher」的惟一名是「:1.51」。

每一個鏈接都有一個惟一名,但不必定有公共名。

只有惟一名而沒有公共名叫作私有鏈接,由於它們沒有提供能夠經過公共名訪問的服務。

Object Paths

「org.kde.StatusNotifierWatcher」這個鏈接中有三個Object Paths,標識這這個鏈接中提供了三個不一樣的服務,每一個Object Paths表示一個服務。這個路徑在鏈接中是惟一的。

Interfaces

在每一個Object Paths下都包含有多個接口(Interfaces),舉例以下接口:

org.freedesktop.DBus.Introspectable

org.freedesktop.DBus.Properties

org.kde.StatusNotifierWatcher

紅色的兩個是消息總線提供的標準接口,而剩下的一個是須要具體的應用去實現的。

MethodsSignals

Methods表示能夠被具體調用的方法

Signals則表示的是信號,此信號能夠被廣播,而鏈接了這個信號的對象在接收到信號時就能夠進行相應的處理。和Qt中的信號應該是一個意思。

 

 

D-BUS 基礎

http://blog.mcuol.com/User/AT91RM9200/Article/12816_1.htm

 

 

D-Bus Tutorial進行了一些翻譯加上本身的一些理解。

有不少種IPC或者網絡通訊系統,如:CORBA, DCE, DCOM, DCOP, XML-RPC, SOAP, MBUS, Internet Communications Engine (ICE)等等,可能會有數百種,dbus的目的主要是下面兩點:
1.在同一個桌面會話中,進行桌面應用程序之間的通信
2.桌面程序與內核或者守護進程的通訊。

Dbus是一套進程通訊體系,它有如下幾層:
1.libdbus庫,提供給各個應用程序調用,使應用程序具備通訊和數據交換的能力,兩個應用程序能夠直接進行通訊,就像是一條socket通道,兩個程序之間創建通道以後,就能夠通信了。
2.消息守護進程,在libdbus的基礎上建立,能夠管理多個應用程序之間的通訊。每一個應用程序都和消息守護進程創建dbus的連接,而後由消息守護進程進行消息的分派。
3.各類包裝庫,有libdbus-glib,libdbus-qt等等,目的是將dbus的底層api進行一下封裝。

下面有一張圖能夠很方便說明dbus的體系結構。
dbus

dbus中的消息由一個消息頭(標識是哪種消息)和消息數據組成,比socket的流式數據更方便一些。bus daemon 就像是一個路由器,與各個應用程序進行鏈接,分派這些消息。bus daemon 在一臺機器上有多個實例,第一個實例是全局的實例,相似於sendmail和或者apache,這個實例有很嚴格的安全限制,只接受一些特定的系統消息, 用於系統通訊。其餘bus daemon是一些會話,用於用戶登陸以後,在當前會話(session)中進行的通信。系統的bus daemon 和會話的bus daemon 是分開的,彼此不會互相影響,會話bus daemon 不會去調用系統的bus daemon 。

Native Objects and Object Paths
在不一樣的編程語言中,都定義了一些「對象」,如java中的 java.lang.Object,GLIB中的GObject,QT中的QObject等 等。D-BUS的底層接口,和libdbus API相關,是沒有這些對象的概念的,它提供的是一種叫對象路徑(object path),用於讓高層接口綁定到各個對象中去,容許遠端應用程序指向它們。object path就像是一個文件路徑,能夠叫作/org/kde/kspread/sheets/3/cells/4/5等。

Methods and Signals
每一個對象都有一些成員,兩種成員:方法(methods)和信號(signals),在對象中,方 法能夠被調用。信號會被廣播,感興趣的對象能夠處理這個 信號,同時信號中也能夠帶有相關的數據。每個方法或者信號均可以用一個名字來命名,如」Frobate」 或者 「OnClicked」。

Interfaces
每一個對象都有一個或者多個接口,一個接口就是多個方法和信號的集合。dbus使用簡單的命名空間字符串來表示接口,如org.freedesktop.Introspectable。能夠說dbus接口至關於C++中的純虛類。

Proxies
代理對象用於模擬在另外的進程中的遠端對象,代理對象像是一個正常的普通對象。d-bus的底層接口必須手動建立方法調用的 消息,而後發送,同時必須手動 接受和處理返回的消息。高層接口可使用代理來替換這些,當調用代理對象的方法時,代理內部會轉換成dbus的方法調用,等待消息返回,對返回結果解包, 返回給相應的方法。能夠看看下面的例子,使用dbus底層接口編寫的代碼:
Message message = new Message("/remote/object/path", "MethodName", arg1, arg2);
Connection connection = getBusConnection();
connection.send(message);
Message reply = connection.waitForReply(message);
if (reply.isError()) {

} else {
Object returnValue = reply.getReturnValue();
}
使用代理對象編寫的代碼:
Proxy proxy = new Proxy(getBusConnection(), "/remote/object/path");
Object returnValue = proxy.MethodName(arg1, arg2);

客戶端代碼減小不少。

 

 

Bus Names
當一個應用程序鏈接上bus daemon時,daemon會分配一個惟一的名字給它。以冒號(:)開始,這些名字在daemon的生命週期中是不會改變的,能夠認爲這些名字就是一個 IP地址。當這個名字映射到應用程序的鏈接上時,應用程序能夠說擁有這個名字。同時應用能夠聲明額外的容易理解的名字,好比能夠取一個名字 com.mycompany.TextEditor,能夠認爲這些名字就是一個域名。其餘應用程序能夠往這個名字發送消息,執行各類方法。

名字還有第二個重要的用途,能夠用於跟蹤應用程序的生命週期。當應用退出(或者崩潰)時,與bus的鏈接將被OS內核關掉,bus將會發送通知,告訴剩餘的應用程序,該程序已經丟失了它的名字。名字還能夠檢測應用是否已經啓動,這每每用於只能啓動一個實例的應用。

Addresses
使用d-bus的應用程序既能夠是server也能夠是client,server監聽到來的鏈接,client鏈接到 server,一旦鏈接創建,消息 就能夠流轉。若是使用dbus daemon,全部的應用程序都是client,daemon監聽全部的鏈接,應用程序初始化鏈接到daemon。

dbus地址指明server將要監聽的地方,client將要鏈接的地方,例如,地址:unix:path=/tmp/abcdef代表 server將在/tmp/abcdef路徑下監聽unix域的socket,client也將鏈接到這個socket。一個地址也能夠指明是 TCP/IP的socket,或者是其餘的。

當使用bus daemon時,libdbus會從環境變量中(DBUS_SESSION_BUS_ADDRESS)自動認識「會話daemon」的地址。若是是系統 daemon,它會檢查指定的socket路徑得到地址,也可使用環境變量(DBUS_SESSION_BUS_ADDRESS)進行設定。

當dbus中不使用daemon時,須要定義哪個應用是server,哪個應用是client,同時要指明server的地址,這不是很一般的作法。

Big Conceptual Picture
要在指定的對象中調用指定的方法,須要知道的參數以下:
Address -> [Bus Name] -> Path -> Interface -> Method
bus name是可選的,除非是但願把消息送到特定的應用中才須要。interface也是可選的,有一些歷史緣由,DCOP不須要指定接口,由於DCOP在同一個對象中禁止同名的方法。

Messages - Behind the Scenes
若是使用dbus的高層接口,就能夠不用直接操做這些消息。DBUS有四種類型的消息:
1.方法調用(method call) 在對象上執行一個方法
2.方法返回(method return)返回方法執行的結果
3.錯誤(error)調用方法產生的異常
4.信號(signal)通知指定的信號發生了,能夠想象成「事件」。

要執行 D-BUS 對象的方法,須要向對象發送一個方法調用消息。它將完成一些處理並返回一個方法返回消息或者錯誤消息。信號的不一樣之處在於它們不返回任何內容:既沒有「信號返回」消息,也沒有任何類型的錯誤消息。

每一個消息都有一個消息頭,包含多個字段,有一個消息體,包含多個參數。能夠認爲消息頭是消息的路由信息,消息體做爲一個載體。消息頭裏面的字段包含 發送的bus name,目標bus name,方法或者信號名字等,同時消息頭裏面定義的字段類型規定了消息體裏面的數據格式。例如:字符「i」表明了」32-bit integer」,「ii」就表明了消息體裏面有兩個」32-bit integer」。

Calling a Method - Behind the Scenes
在dbus中調用一個方法包含了兩條消息,進程A向進程B發送 方法調用消息,進程B向進程A發送應答消息。全部的消息都由daemon進行分派,每一個調用 的消息都有一個不一樣的序列號,返回消息包含這個序列號,以方便調用者匹配調用消息與應答消息。調用消息包含一些參數,應答消息可能包含錯誤標識,或者包含 方法的返回數據。

方法調用的通常流程:
1.使用不一樣語言綁定的dbus高層接口,都提供了一些代理對象,調用其餘進程裏面的遠端對象就像是在本地進程中的調用同樣。應用調用代理上的方法,代理將構造一個方法調用消息給遠端的進程。
2.在DBUS的底層接口中,應用須要本身構造方法調用消息(method call message),而不能使用代理。
3.方法調用消息裏面的內容有:目的進程的bus name,方法的名字,方法的參數,目的進程的對象路徑,以及可選的接口名稱。
4.方法調用消息是發送到bus daemon中的。
5.bus daemon查找目標的bus name,若是找到,就把這個方法發送到該進程中,不然,daemon會產生錯誤消息,做爲應答消息給發送進程。
6. 目標進程解開消息,在dbus底層接口中,會當即調用方法,而後發送方法的應答消息給daemon。在dbus高層接口中,會先檢測對象路徑,接口, 方法名稱,而後把它轉換成對應的對象(如GObject,QT中的QObject等)的方法,而後再將應答結果轉換成應答消息發給daemon。
7.bus daemon接受到應答消息,將把應答消息直接發給發出調用消息的進程。
8.應答消息中能夠包容不少返回值,也能夠標識一個錯誤發生,當使用綁定時,應答消息將轉換爲代理對象的返回值,或者進入異常。

bus daemon不對消息從新排序,若是發送了兩條消息到同一個進程,他們將按照發送順序接受到。接受進程並須要按照順序發出應答消息,例如在多線程中處理這些消息,應答消息的發出是沒有順序的。消息都有一個序列號能夠與應答消息進行配對。

Emitting a Signal - Behind the Scenes
在dbus中一個信號包含一條信號消息,一個進程發給多個進程。也就是說,信號是單向的廣播。信號能夠包含一些參數,可是做爲廣播,它是沒有返回值的。

信號觸發者是不瞭解信號接受者的,接受者向daemon註冊感興趣的信號,註冊規則是」match rules」,記錄觸發者名字和信號名字。daemon只向註冊了這個信號的進程發送信號。

信號的通常流程以下:
1.當使用dbus底層接口時,信號須要應用本身建立和發送到daemon,使用dbus高層接口時,可使用相關對象進行發送,如Glib裏面提供的信號觸發機制。
2.信號包含的內容有:信號的接口名稱,信號名稱,發送進程的bus name,以及其餘參數。
3.任何進程均可以依據」match rules」註冊相關的信號,daemon有一張註冊的列表。
4.daemon檢測信號,決定哪些進程對這個信號感興趣,而後把信號發送給這些進程。
5.每一個進程收到信號後,若是是使用了dbus高層接口,能夠選擇觸發代理對象上的信號。若是是dbus底層接口,須要檢查發送者名稱和信號名稱,而後決定怎麼作。

 

 

DBUS基礎知識

http://www.cnblogs.com/wzh206/archive/2010/05/13/1734901.html

 

 

1.  進程間使用D-Bus通訊

D-Bus是一種高級的進程間通訊機制,它由freedesktop.org項目提供,使用GPL許可證發行。D-Bus最主要的用途是在 Linux桌面環境爲進程提供通訊,同時能將Linux桌面環境和Linux內核事件做爲消息傳遞到進程。D-Bus的主要機率爲總線,註冊後的進程可通 過總線接收或傳遞消息,進程也可註冊後等待內核事件響應,例如等待網絡狀態的轉變或者計算機發出關機指令。目前,D-Bus已被大多數Linux發行版所 採用,開發者可以使用D-Bus實現各類複雜的進程間通訊任務。

2.  D-Bus的基本概念

D-Bus是一個消息總線系統,其功能已涵蓋進程間通訊的全部需求,並具有一些特殊的用途。D-Bus是三層架構的進程間通訊系統,其中包括:

接口層:接口層由函數庫libdbus提供,進程可經過該庫使用D-Bus的能力。

總線層:總線層其實是由D-Bus總線守護進程提供的。它在Linux系統啓動時運行,負責進程間的消息路由和傳遞,其中包括Linux內核和Linux桌面環境的消息傳遞。

包裝層:包裝層一系列基於特定應用程序框架的Wrapper庫。

D-Bus具有自身的協議,協議基於二進制數據設計,與數據結構和編碼方式無關。該協議無需對數據進行序列化,保證了信息傳遞的高效性。不管是libdbus,仍是D-Bus總線守護進程,均不須要太大的系統開銷。

總線是D-Bus的進程間通訊機制,一個系統中一般存在多條總線,這些總線由D-Bus總線守護進程管理。最重要的總線爲系統總線(System Bus),Linux內核引導時,該總線就已被裝入內存。只有Linux內核、Linux桌面環境和權限較高的程序才能向該總線寫入消息,以此保障系統安 全性,防止有惡意進程假冒Linux發送消息。

會話總線(Session Buses)由普通進程建立,可同時存在多條。會話總線屬於某個進程私有,它用於進程間傳遞消息。

進程必須註冊後才能收到總線中的消息,而且可同時鏈接到多條總線中。D-Bus提供了匹配器(Matchers)使進程能夠有選擇性的接收消息,另 外運行進程註冊回調函數,在收到指定消息時進行處理。匹配器的功能等同與路由,用於避免處理無關消息形成進程的性能降低。除此之外,D-Bus機制的重要 概念有如下幾個。

對象:對象是封裝後的匹配器與回調函數,它以對等(peer-to-peer)協議使每一個消息都有一個源地址和一個目的地址。這些地址又稱爲對象路 徑,或者稱之爲總線名稱。對象的接口是回調函數,它以相似C++的虛擬函數實現。當一個進程註冊到某個總線時,都要建立相應的消息對象。

消息:D-Bus的消息分爲信號(signals)、方法調用(method calls)、方法返回(method returns)和錯誤(errors)。信號是最基本的消息,註冊的進程可簡單地發送信號到總線上,其餘進程經過總線讀取消息。方法調用是經過總線傳遞 參數,執行另外一個進程接口函數的機制,用於某個進程控制另外一個進程。方法返回是註冊的進程在收到相關信息後,自動作出反應的機制,由回調函數實現。錯誤是 信號的一種,是註冊進程錯誤處理機制之一。

服務:服務(Services)是進程註冊的抽象。進程註冊某個地址後,便可得到對應總線的服務。D-Bus提供了服務查詢接口,進程可經過該接口查詢某個服務是否存在。或者在服務結束時自動收到來自系統的消息。

創建服務的流程:
----------------------------------
    創建一個dbus鏈接以後 -- dbus_bus_get(),爲這個dbus鏈接(DbusConnection)起名 -- dbus_bus_request_name(),這個名字將會成爲咱們在後續進行遠程調用的時候的服務名,而後咱們進入監聽循環 -- dbus_connection_read_write()。在循環中,咱們從總線上取出消息 -- dbus_connection_pop_message(),並經過比對消息中的方法接口名和方法名 -- dbus_message_is_method_call(),若是一致,那麼咱們跳轉到相應的處理中去。在相應的處理中,咱們會從消息中取出遠程調用的 參數。而且創建起回傳結果的通路 -- reply_to_method_call()。回傳動做自己等同於一次不須要等待結果的遠程調用。
發送信號的流程:
----------------------------------
    創建一個dbus鏈接以後,爲這個dbus鏈接起名,創建一個發送信號的通道,注意,在創建通道的函數中,須要咱們填寫該信號的接口名和信號名 -- dbus_message_new_signal()。而後咱們把信號對應的相關參數壓進去 -- dbus_message_iter_init_append(); dbus_message_iter_append_basic()。而後就能夠啓動發送了 -- dbus_connection_send(); dbus_connection_flush。
進行一次遠程調用的流程:
----------------------------------
    創建好dbus鏈接以後,爲這dbus鏈接命名,申請一個遠程調用通道 -- dbus_message_new_method_call(),注意,在申請遠程調用通道的時候,須要填寫服務器名,本次調用的接口名,和本次調用名 (方法名)。壓入本次調用的參數 -- dbus_message_iter_init_append(); dbus_message_iter_append_basic(),其實是申請了一個首地址,咱們就是把咱們真正要傳的參數,往這個首地址裏面送(送 完以後通常都會判斷是否內存越界了)。而後就是啓動發送調用並釋放發送相關的消息結構 -- dbus_connection_send_with_reply()。這個啓動函數中帶有一個句柄。咱們立刻會阻塞等待這個句柄給咱們帶回總線上回傳的 消息。當這個句柄回傳消息以後,咱們從消息結構中分離出參數。用dbus提供的函數提取參數的類型和參數 -- dbus_message_iter_init(); dbus_message_iter_next(); dbus_message_iter_get_arg_type(); dbus_message_iter_get_basic()。也就達成了咱們進行本次遠程調用的目的了。
信號接收流程:
----------------------------------
    創建一個dbus鏈接以後,爲這個dbus鏈接起名,爲咱們將要進行的消息循環添加匹配條件(就是經過信號名和信號接口名來進行匹配控制的) -- dbus_bus_add_match()。咱們進入等待循環後,只須要對信號名,信號接口名進行判斷就能夠分別處理各類信號了。在各個處理分支上。咱們 能夠分離出消息中的參數。對參數類型進行判斷和其餘的處理。
dbus_connection_read_write()
--------------------------------------
    As long as the connection is open, this function will block until it can read or write, then read or write, then return #TRUE.
    If the connection is closed, the function returns #FALSE.
dbus_connection_pop_message()
--------------------------------------
    Returns the first-received message from the incoming message queue, removing it from the queue. The caller owns a reference to the returned message. If the queue is empty, returns #NULL.
dbus_connection_send()
--------------------------------------
    Adds a message to the outgoing message queue. Does not block to write the message to the network; that happens asynchronously. To force the message to be written, call dbus_connection_flush(). Because this only queues the message, the only reason it can
fail is lack of memory. Even if the connection is disconnected, no error will be returned.
    @param connection the connection.
    @param message the message to write. 
    @param serial return location for message serial, or #NULL if you don't care
    @returns #TRUE on success.
dbus_connection_send_with_reply()
--------------------------------------
    Queues a message to send, as with dbus_connection_send(), but also returns a #DBusPendingCall used to receive a reply to the message. If no reply is received in the given timeout_milliseconds, this function expires the pending reply and generates a synthetic error reply (generated in-process, not by the remote application) indicating that a timeout occurred.
    A #DBusPendingCall will see a reply message before any filters or registered object path handlers. See dbus_connection_dispatch() for details on when handlers are run.
    A #DBusPendingCall will always see exactly one reply message, unless it's cancelled with dbus_pending_call_cancel().
    If #NULL is passed for the pending_return, the #DBusPendingCall will still be generated internally, and used to track the message reply timeout. This means a timeout error will occur if no reply arrives, unlike with dbus_connection_send().
    If -1 is passed for the timeout, a sane default timeout is used. -1 is typically the best value for the timeout for this reason, unless you want a very short or very long timeout. There is no way to avoid a timeout entirely, other than passing INT_MAX for the
timeout to mean "very long timeout." libdbus clamps an INT_MAX timeout down to a few hours timeout though.
    @warning if the connection is disconnected, the #DBusPendingCall will be set to #NULL, so be careful with this.
    @param connection the connection
    @param message the message to send
    @param pending_return return location for a #DBusPendingCall object, or #NULL if connection is disconnected
    @param timeout_milliseconds timeout in milliseconds or -1 for default
    @returns #FALSE if no memory, #TRUE otherwise.
dbus_message_is_signal()
--------------------------------------
    Checks whether the message is a signal with the given interface and member fields. If the message is not #DBUS_MESSAGE_TYPE_SIGNAL, or has a different interface or member field, returns #FALSE.
dbus_message_iter_init()
--------------------------------------
    Initializes a #DBusMessageIter for reading the arguments of the message passed in.
dbus_message_iter_next()
--------------------------------------
    Moves the iterator to the next field, if any. If there's no next field, returns #FALSE. If the iterator moves forward, returns #TRUE.
dbus_message_iter_get_arg_type()
--------------------------------------
    Returns the argument type of the argument that the message iterator points to. If the iterator is at the end of the message, returns #DBUS_TYPE_INVALID. 
dbus_message_iter_get_basic()
--------------------------------------
    Reads a basic-typed value from the message iterator. Basic types are the non-containers such as integer and string.
dbus_message_new_signal()
--------------------------------------
    Constructs a new message representing a signal emission. Returns #NULL if memory can't be allocated for the message. A signal is identified by its originating object path, interface, and the name of the signal.
    Path, interface, and signal name must all be valid (the D-Bus specification defines the syntax of these fields).
    @param path the path to the object emitting the signal
    @param interface the interface the signal is emitted from
    @param name name of the signal
    @returns a new DBusMessage, free with dbus_message_unref()
dbus_message_iter_init_append()
--------------------------------------
    Initializes a #DBusMessageIter for appending arguments to the end of a message.
    @param message the message
    @param iter pointer to an iterator to initialize
dbus_message_iter_append_basic()
--------------------------------------
    Appends a basic-typed value to the message. The basic types are the non-container types such as integer and string.
    @param iter the append iterator
    @param type the type of the value
    @param value the address of the value
    @returns #FALSE if not enough memory
dbus_message_new_method_call()
--------------------------------------
    Constructs a new message to invoke a method on a remote object. Returns #NULL if memory can't be allocated for the message. The destination may be #NULL in which case no destination is set; this is appropriate when using D-Bus in a peer-to-peer context (no message bus). The interface may be #NULL, which means that if multiple methods with the given name exist it is undefined which one will be invoked.
    The path and method names may not be #NULL.
    Destination, path, interface, and method name can't contain any invalid characters (see the D-Bus specification).
    @param destination name that the message should be sent to or #NULL
    @param path object path the message should be sent to
    @param interface interface to invoke method on, or #NULL
    @param method method to invoke
    @returns a new DBusMessage, free with dbus_message_unref()
dbus_bus_get()
--------------------------------------
    Connects to a bus daemon and registers the client with it. If a connection to the bus already exists, then that connection is returned. The caller of this function owns a reference to the bus.
    @param type bus type
    @param error address where an error can be returned.
    @returns a #DBusConnection with new ref
dbus_bus_request_name()
--------------------------------------
    Asks the bus to assign the given name to this connection by invoking the RequestName method on the bus.
    First you should know that for each bus name, the bus stores a queue of connections that would like to own it. Only one owns it at a time - called the primary owner. If the primary owner releases the name or disconnects, then the next owner in the queue atomically takes over.
    So for example if you have an application org.freedesktop.TextEditor and multiple instances of it can be run, you can have all of them sitting in the queue. The first one to start up will receive messages sent to org.freedesktop.TextEditor, but if that one exits another will become the primary owner and receive messages.
    The queue means you don't need to manually watch for the current owner to disappear and then request the name again.
    @param connection the connection
    @param name the name to request
    @param flags flags
    @param error location to store the error
    @returns a result code, -1 if error is set
    給DBusConnection起名字(命名) -- 兩個相互通訊的鏈接(connection)不能同名
    命名規則: xxx.xxx (zeng.xiaolong)
dbus_bus_add_match()
--------------------------------------
    Adds a match rule to match messages going through the message bus. The "rule" argument is the string form of a match rule.
    @param connection connection to the message bus
    @param rule textual form of match rule
    @param error location to store any errors
dbus_pending_call_block()
--------------------------------------
    Block until the pending call is completed. The blocking is as with dbus_connection_send_with_reply_and_block(); it does not enter the main loop or process other messages, it simply waits for the reply in question.
    If the pending call is already completed, this function returns immediately.
    @todo when you start blocking, the timeout is reset, but it should really only use time remaining since the pending call was created. This requires storing timestamps instead of intervals in the timeout
    @param pending the pending call
dbus_pending_call_steal_reply()
--------------------------------------
    Gets the reply, or returns #NULL if none has been received yet. Ownership of the reply message passes to the caller. This function can only be called once per pending call, since the reply message is tranferred to the caller.
    @param pending the pending call
    @returns the reply message or #NULL.

安裝D-Bus可在其官方網站下載源碼編譯,地址爲http://dbus.freedesktop.org。或者在終端上輸入下列指令:

  1.  
    1. yum install dbus dbus-devel dbus-doc

安裝後,頭文件位於"/usr/include/dbus-<版本號>/dbus"目錄中,編譯使用D-Bus的程序時需加入編譯指令"`pkg-config --cflags --libs dbus-1`"。

3.  D-Bus的用例

在使用GNOME桌面環境的Linux系統中,一般用GLib庫提供的函數來管理總線。在測試下列用例前,首先須要安裝GTK+開發包(見22.3節)並配置編譯環境。該用例一共包含兩個程序文件,每一個程序文件需單獨編譯成爲可執行文件。

1.消息發送程序

"dbus-ding-send.c"程序每秒經過會話總線發送一個參數爲字符串Ding!的信號。該程序的源代碼以下:

  1. #include <glib.h>                               // 包含glib庫  
  2. #include <dbus/dbus-glib.h>                     // 包含
    glib庫中D-Bus管理庫  
  3. #include <stdio.h> 
  4. static gboolean send_ding(DBusConnection *bus);// 定義發送消息函數的原型  
  5. int main ()  
  6. {  
  7.    GMainLoop *loop;                             // 定義一個事件循環對象的指針  
  8.    DBusConnection *bus;                         // 定義總線鏈接對象的指針  
  9.    DBusError error;                             // 定義D-Bus錯誤消息對象  
  10.    loop = g_main_loop_new(NULL, FALSE);         // 建立新事件循環對象  
  11.    dbus_error_init (&error);                    // 將錯誤消息對象鏈接到D-Bus  
  12.                                                 // 錯誤消息對象  
  13.    bus = dbus_bus_get(DBUS_BUS_SESSION, &error);// 鏈接到總線  
  14.    if (!bus) {                              // 判斷是否鏈接錯誤  
  15. g_warning("鏈接到D-Bus失敗: %s", error.message);  
  16.                                         // 使用GLib輸出錯誤警告信息  
  17.       dbus_error_free(&error);              // 清除錯誤消息  
  18.       return 1;  
  19.    }  
  20.    dbus_connection_setup_with_g_main(bus, NULL);  
  21.                                             // 將總線設爲接收GLib事件循環  
  22.    g_timeout_add(1000, (GSourceFunc)send_ding, bus);  
  23.                                     // 每隔1000ms調用一次send_ding()函數  
  24.                                             // 將總線指針做爲參數  
  25.    g_main_loop_run(loop);                   // 啓動事件循環  
  26.    return 0;  
  27. }  
  28. static gboolean send_ding(DBusConnection *bus)  // 定義發
    送消息函數的細節  
  29. {  
  30.    DBusMessage *message;                        // 建立消息對象指針  
  31.    message = dbus_message_new_signal("/com/burtonini/dbus/ding",   
  32.                                        "com.burtonini.dbus.Signal",  
  33.                                        "ding");     // 建立消息對象並標識路徑  
  34.    dbus_message_append_args(message,  
  35.                             DBUS_TYPE_STRING, "ding!",  
  36.                             DBUS_TYPE_INVALID);     //將字符串Ding!定義爲消息  
  37.    dbus_connection_send(bus, message, NULL);    // 發送該消息  
  38.    dbus_message_unref(message);                 // 釋放消息對象  
  39.    g_print("ding!/n");                          // 該函數等同與標準輸入輸出                                     
  40.    return TRUE;  
  41. }

main()函數建立一個GLib事件循環,得到會話總線的一個鏈接,並將D-Bus事件處理集成到GLib事件循環之中。而後它建立了一個名爲 send_ding()函數做爲間隔爲一秒的計時器,並啓動事件循環。send_ding()函數構造一個來自於對象路徑"/com/burtonini /dbus/ding"和接口"com.burtonini.dbus.Signal"的新的Ding信號。而後,字符串Ding!做爲參數添加到信號中 並經過總線發送。在標準輸出中會打印一條消息以讓用戶知道發送了一個信號。

2.消息接收程序

dbus-ding-listen.c程序經過會話總線接收dbus-ding-send.c程序發送到消息。該程序的源代碼以下:

  1. #include <glib.h>                               // 包含glib庫  
  2. #include <dbus/dbus-glib.h>                     // 包含glib庫中D-Bus管理庫  
  3. static DBusHandlerResult signal_filter      // 定義接收消息函數的原型  
  4.       (DBusConnection *connection, DBusMessage *message, void *user_data);  
  5. int main()  
  6. {  
  7.    GMainLoop *loop;                             // 定義一個事件循環對象的指針  
  8.    DBusConnection *bus;                         // 定義總線鏈接對象的指針  
  9.    DBusError error;                             // 定義D-Bus錯誤消息對象  
  10.    loop = g_main_loop_new(NULL, FALSE);         // 建立新事件循環對象  
  11.    dbus_error_init(&error);                     // 將錯誤消息對象鏈接到D-Bus  
  12.                                                 // 錯誤消息對象  
  13.    bus = dbus_bus_get(DBUS_BUS_SESSION, &error);    // 鏈接到總線  
  14.    if (!bus) {                              // 判斷是否鏈接錯誤  
  15. g_warning("鏈接到D-Bus失敗: %s", error.message);  
  16.                                         // 使用GLib輸出錯誤警告信息  
  17.       dbus_error_free(&error);              // 清除錯誤消息  
  18.       return 1;  
  19.   }  
  20.    dbus_connection_setup_with_g_main(bus, NULL);      
  21.                                             // 將總線設爲接收GLib事件循環  
  22.    dbus_bus_add_match(bus, "type='signal',interface
    ='com.burtonini.dbus.Signal'");  // 定義匹配器  
  23.    dbus_connection_add_filter(bus, signal_filter, loop, NULL);  
  24.                                             // 調用函數接收消息  
  25.    g_main_loop_run(loop);                   // 啓動事件循環  
  26.    return 0;  
  27. }  
  28. static DBusHandlerResult                    // 定義接收消息函數的細節  
  29. signal_filter (DBusConnection *connection, 
    DBusMessage *message, void *user_data)  
  30. {  
  31.    GMainLoop *loop = user_data;             // 定義事件循環對象的指針,並與主函數中的同步  
  32.    if (dbus_message_is_signal               // 接收鏈接成功消息,判斷是否鏈接失敗  
  33.         (message, DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL,
    "Disconnected")) {  
  34.       g_main_loop_quit (loop);              // 退出主循環  
  35.       return DBUS_HANDLER_RESULT_HANDLED;  
  36.    }  
  37.    if (dbus_message_is_signal(message, "com.burtonini.dbus.Signal",   
  38.    "Ping")) {  
  39.                                             // 指定消息對象路徑,判斷是否成功  
  40.       DBusError error;                      // 定義錯誤對象  
  41.       char *s;  
  42. dbus_error_init(&error);                // 將錯誤消息對象鏈接到D-Bus錯誤  
  43.                                         // 消息對象  
  44.       if (dbus_message_get_args                 // 接收消息,並判斷是否有錯誤  
  45.          (message, &error, DBUS_TYPE_STRING, &s, 
    DBUS_TYPE_INVALID)) {  
  46.          g_print("接收到的消息是: %s/n", s);   // 輸出接收到的消息  
  47.          dbus_free (s);                     // 清除該消息  
  48.       }   
  49.       else {                                    // 有錯誤時執行下列語句  
  50.          g_print("消息已收到,但有錯誤提示: %s/n", error.message);  
  51.          dbus_error_free (&error);  
  52.       }  
  53.       return DBUS_HANDLER_RESULT_HANDLED;  
  54.    }  
  55.    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;  
  56. }

該程序偵聽dbus-ping-send.c程序正在發出的信號。main()函數和前面同樣啓動,建立一個到總線的鏈接。而後它聲明願意在使用 com.burtonini.dbus.Signal接口的信號被髮送時獲得通知,將signal_filter()函數設置爲通知函數,而後進入事件循 環。當知足匹配的消息被髮送時,signal_func()函數會被調用。

若是須要肯定在接收消息時如何處理,可經過檢測消息頭實現。若收到的消息爲總線斷開信號,則主事件循環將被終止,由於監聽的總線已經不存在了。若收 到其餘的消息,首先將收到的消息與期待的消息進行比較,二者相同則輸出其中參數,並退出程序。二者不相同則告知總線並無處理該消息,這樣消息會繼續保留 在總線中供別的程序處理。

 

dbus實例講解

http://blog.csdn.net/fmddlmyy/archive/2008/12/23/3585730.aspx

 

 

網上有很多介紹dbus的文章。本文的目標是補充一些簡單的例子。

一、dbus是什麼東西?

網上有一篇叫「D-Bus Tutorial」的文章,流傳較廣。很多介紹dbus的資料,都引用了其中的段落。其實相對於這篇文章,我建議你們直接讀「D-Bus Specification」,篇幅不算長,文字也不算枯燥。

D-Bus是針對桌面環境優化的IPC(interprocess communication )機制,用於進程間的通訊或進程與內核的通訊。最基本的D-Bus協議是一對一的通訊協議。但在不少狀況下,通訊的一方是消息總線。消息總線是一個特殊的 應用,它同時與多個應用通訊,並在應用之間傳遞消息。下面咱們會在實例中觀察消息總線的做用。消息總線的角色有點相似與X系統中的窗口管理器,窗口管理器 既是X客戶,又負責管理窗口。

支持dbus的系統都有兩個標準的消息總線:系統總線和會話總線。系統總線用於系統與應用的通訊。會話總線用於應用之間的通訊。網上有一個叫d-feet的python程序,咱們能夠用它來觀察系統中的dbus世界。

圖一、由d-feet觀察到的D-Bus世界

D-Bus是一個程序。它提供了API。但咱們通常不會直接使用dbus的接口。dbus-glib是GTK版本的dbus接口封裝。本文假設讀者 安裝了dbus-glib,我安裝的是dbus-glib-0.76。後面還會看到,經過python操縱dbus是多麼簡單。

二、D-Bus的基本概念

2.一、從例子開始

我寫了一個最簡單的dbus服務器,它經過dbus提供了一個加法的接口。你們能夠下載這個例子。這是一個autotool工程,你們解包後,執行:

./autogen.sh
./configure
make

而後在src目錄運行:

./example-service

這時再運行d-feet,鏈接session bus,在「Bus Name」窗口會看到一個叫「org.fmddlmyy.Test」鏈接名。

圖二、提供D-Bus服務的org.fmddlmyy.Test

選擇「org.fmddlmyy.Test」,在右側窗口點擊展開「Object Paths」->「/TestObj」->「Interfaces」->「org.fmddlmyy.Test.Basic」->「Methods」, 能夠看到一個Add方法。雙擊Add方法,彈出下面這個對話框:

圖三、經過D-Bus接口計算1+2=3

在Parameters窗口輸入「1,2」,點擊「Execute」按鈕,而後在「Output」窗口咱們看到了輸出結果。咱們剛剛建立了一個dbus服務並調用了它。

2.二、名詞

咱們來解釋一下d-feet中出現的名詞。

2.2.一、Bus Name

能夠把Bus Name理解爲鏈接的名稱,一個Bus Name老是表明一個應用和消息總線的鏈接。有兩種做用不一樣的Bus Name,一個叫公共名(well-known names),還有一個叫惟一名(Unique Connection Name)。

2.2.1.一、可能有多個備選鏈接的公共名

公共名提供衆所周知的服務。其餘應用經過這個名稱來使用名稱對應的服務。可能有多個鏈接要求提供同個公共名的服務,即多個應用鏈接到消息總線,要求 提供同個公共名的服務。消息總線會把這些鏈接排在鏈表中,並選擇一個鏈接提供公共名錶明的服務。能夠說這個提供服務的鏈接擁有了這個公共名。若是這個鏈接 退出了,消息總線會從鏈表中選擇下一個鏈接提供服務。公共名是由一些圓點分隔的多個小寫標誌符組成的,例如「org.fmddlmyy.Test」、 「org.bluez」。

2.2.1.二、每一個鏈接都有一個惟一名

當應用鏈接到消息總線時,消息總線會給每一個應用分配一個惟一名。惟一名以「:」開頭,「:」後面一般是圓點分隔的兩個數字,例如「:1.0」。每一個 鏈接都有一個惟一名。在一個消息總線的生命期內,不會有兩個鏈接有相同的惟一名。擁有公衆名的鏈接一樣有惟一名,例如在前面的圖 中,「org.fmddlmyy.Test」的惟一名是「:1.17」。

有的鏈接只有惟一名,沒有公衆名。能夠把這些名稱稱爲私有鏈接,由於它們沒有提供能夠經過公共名訪問的服務。 d-feet界面上有個「Hide Private」按鈕,能夠用來隱藏私有鏈接。

2.2.二、Object Paths

Bus Name肯定了一個應用到消息總線的鏈接。在一個應用中能夠有多個提供服務的對象。這些對象按照樹狀結構組織起來。每一個對象都有一個惟一的路徑(Object Paths)。或者說,在一個應用中,一個對象路徑標誌着一個惟一的對象。

「org.fmddlmyy.Test」只有一個叫做「/TestObj」的對象。圖1中的「org.bluez」有多個對象路徑。

2.2.三、Interfaces

經過對象路徑,咱們找到應用中的一個對象。每一個對象能夠實現多個接口。例如:「org.fmddlmyy.Test」的「/TestObj」實現瞭如下接口:

  • org.fmddlmyy.Test.Basic
  • org.freedesktop.DBus.Introspectable
  • org.freedesktop.DBus.Properties

後面講代碼時會看到,咱們在代碼中其實只實現了「org.fmddlmyy.Test.Basic」這個接口。接口 「org.freedesktop.DBus.Introspectable」和「org.freedesktop.DBus.Properties」是 消息總線提供的標準接口。

2.2.四、Methods和Signals

接口包括方法和信號。例如「org.fmddlmyy.Test」的「/TestObj」對象的「org.fmddlmyy.Test.Basic」接口有一個Add方法。後面的例子中咱們會介紹信號。

標準接口「org.freedesktop.DBus.Introspectable」的Introspect方法是個頗有用的方法。相似於 Java的反射接口,調用Introspect方法能夠返回接口的xml描述。咱們雙擊 「org.fmddlmyy.Test」->「/TestObj」->「org.fmddlmyy.Test.Basic」->「org.freedesktop.DBus.Introspectable」 的Introspect方法。這個方法沒有輸入參數,咱們直接點擊「Execute」按鈕,你在「Output」窗口看到了什麼?

圖四、調用Introspect方法

後面咱們會用另外一種方式調用Introspect方法。

2.3 小結

「org.fmddlmyy.Test」->「/TestObj」->「org.fmddlmyy.Test.Basic」->「org.freedesktop.DBus.Introspectable」 的Introspect方法,這個描述是否是很麻煩。其實前面還要加上「session bus」。

後面在看客戶端的C代碼時,咱們會看到一樣的過程:用dbus_g_bus_get獲得到session bus的鏈接。在這個鏈接上用dbus_g_proxy_new_for_name函數得到到擁有指定公共名的鏈接的指定對象的指定接口的代理。最後,用 dbus_g_proxy_call函數經過接口代理調用接口提供的方法。

3 下集預告

d-feet雖然很方便,但它使用了python的gtk模塊,在一些嵌入式環境可能使用不了。後面會看到,用一個叫dbus-send的命令行工 具,或者寫幾行python腳本均可以完成一樣的工做。咱們還會用一個叫dbus-monitor的命令行工具觀察dbus調用過程當中究竟發生了什麼?

 

 

應用程序A和消息總線鏈接,這個鏈接獲取了一個衆所周知的公共名(記做鏈接A)。應用程序A中有對象A1提供了接口I1,接口I1有方法M1。應用程序B和消息總線鏈接,要求調用鏈接A上對象A1的接口I1的方法M1。

在上一講的加法例子中,上面這段話能夠實例化爲:應用程序example-service和會話總線鏈接。這個鏈接獲取了一個衆所周知的公共名 「org.fmddlmyy.Test」。應用程序example-servic中有對象「/TestObj」提供了接口 「org.fmddlmyy.Test.Basic」,接口「org.fmddlmyy.Test.Basic」有方法「Add」。應用程序d-feet 和會話總線鏈接,要求調用鏈接「org.fmddlmyy.Test」上對象「/TestObj」的接口 「org.fmddlmyy.Test.Basic」的方法「Add」。

應用程序B調用應用程序A的方法,其實就是應用程序B嚮應用程序A發送了一個類型爲「method_call」的消息。應用程序A經過一個類型爲「method_retutn」的消息將返回值發給應用程序B。咱們簡單介紹一下D-Bus總線上的消息。

一、D-Bus的消息

上一講說過最基本的D-Bus協議是一對一的通訊協議。與直接使用socket不一樣,D-Bus是面向消息的協議。 D-Bus的全部功能都是經過在鏈接上流動的消息完成的。

1.一、消息類型

D-Bus有四種類型的消息:

  • method_call 方法調用
  • method_return 方法返回
  • error 錯誤
  • signal 信號

前面介紹的遠程方法調用就用到了method_call和method_return消息。顧名思義,在發生錯誤時會產生error消息。若是把method_call看做打電話,那麼signal消息就是來電了。後面還會詳細討論。

1.二、dbus-send和dbus-monitor

dbus提供了兩個小工具:dbus-send和dbus-monitor。咱們能夠用dbus-send發送消息。用dbus-monitor監 視總線上流動的消息。讓咱們經過dbus-send發送消息來調用前面的Add方法,這時dbus-send充當了應用程序B。用dbus- monitor觀察調用過程當中的消息。

啓動example-service:

$ ./example-service 

在另外一個控制檯啓動dbus-monitor:

$ dbus-monitor

dbus-monitor默認監視會話總線。執行:

$ dbus-send --session --type=method_call --print-reply --dest=org.fmddlmyy.Test /TestObj org.fmddlmyy.Test.Basic.Add int32:100 int32:999

輸出爲:

method return sender=:1.21 -> dest=:1.22 reply_serial=2
   int32 1099

dbus-monitor的相關輸出包括:

signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.22"
   string ""
   string ":1.22"
method call sender=:1.22 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=Hello
method call sender=:1.22 -> dest=org.fmddlmyy.Test path=/TestObj; interface=org.fmddlmyy.Test.Basic; member=Add
   int32 100
   int32 999
method return sender=:1.21 -> dest=:1.22 reply_serial=2
   int32 1099
signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.22"
   string ":1.22"
   string ""

:1.22就是dbus-send在本次調用中與會話總線所創建鏈接的惟一名。:1.21是鏈接「org.fmddlmyy.Test」的惟一名。 在以上輸出中咱們能夠看到:1.22向「org.fmddlmyy.Test」發送method_call消息,調用Add方法。 :1.21經過method_return消息將調用結果發回:1.22。其它輸出信息會在之後說明。

dbus-send的詳細用法能夠參閱手冊。調用遠程方法的通常形式是:

$ dbus-send [--system | --session] --type=method_call --print-reply --dest=鏈接名 對象路徑 接口名.方法名 參數類型:參數值 參數類型:參數值

dbus-send支持的參數類型包括:string, int32, uint32, double, byte, boolean。

二、消息總線的方法和信號

2.一、概述

消息總線是一個特殊的應用,它能夠在與它鏈接的應用之間傳遞消息。能夠把消息總線看做一臺路由器。正是經過消息總線,D-Bus纔在一對一的通訊協議基礎上實現了多對一和一對多的通訊。

消息總線雖然有特殊的轉發功能,但消息總線也仍是一個應用。其它應用與消息總線的通訊也是經過1.1節的基本消息類型完成的。做爲一個應用,消息總線也提供了本身的接口,包括方法和信號。

咱們能夠經過向鏈接「org.freedesktop.DBus 」上對象「/」發送消息來調用消息總線提供的方法。事實上,應用程序正是經過這些方法鏈接到消息總線上的其它應用,完成請求公共名等工做的。

2.二、清單

消息總線對象支持第一講中提到的標準接口"org.freedesktop.DBus.Introspectable",咱們能夠調用org.freedesktop.DBus.Introspectable.Introspect方法查看消息總線對象支持的接口。例如:

$ dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.Introspectable.Introspect

輸出爲:

method return sender=org.freedesktop.DBus -> dest=:1.20 reply_serial=2
   string "<!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="org.freedesktop.DBus.Introspectable">
    <method name="Introspect">
      <arg name="data" direction="out" type="s"/>
    </method>
  </interface>
  <interface name="org.freedesktop.DBus">
    <method name="Hello">
      <arg direction="out" type="s"/>
    </method>
    <method name="RequestName">
      <arg direction="in" type="s"/>
      <arg direction="in" type="u"/>
      <arg direction="out" type="u"/>
    </method>
    <method name="ReleaseName">
      <arg direction="in" type="s"/>
      <arg direction="out" type="u"/>
    </method>
    <method name="StartServiceByName">
      <arg direction="in" type="s"/>
      <arg direction="in" type="u"/>
      <arg direction="out" type="u"/>
    </method>
    <method name="NameHasOwner">
      <arg direction="in" type="s"/>
      <arg direction="out" type="b"/>
    </method>
    <method name="ListNames">
      <arg direction="out" type="as"/>
    </method>
    <method name="ListActivatableNames">
      <arg direction="out" type="as"/>
    </method>
    <method name="AddMatch">
      <arg direction="in" type="s"/>
    </method>
    <method name="RemoveMatch">
      <arg direction="in" type="s"/>
    </method>
    <method name="GetNameOwner">
      <arg direction="in" type="s"/>
      <arg direction="out" type="s"/>
    </method>
    <method name="ListQueuedOwners">
      <arg direction="in" type="s"/>
      <arg direction="out" type="as"/>
    </method>
    <method name="GetConnectionUnixUser">
      <arg direction="in" type="s"/>
      <arg direction="out" type="u"/>
    </method>
    <method name="GetConnectionUnixProcessID">
      <arg direction="in" type="s"/>
      <arg direction="out" type="u"/>
    </method>
    <method name="GetConnectionSELinuxSecurityContext">
      <arg direction="in" type="s"/>
      <arg direction="out" type="ay"/>
    </method>
    <method name="ReloadConfig">
    </method>
    <method name="GetId">
      <arg direction="out" type="s"/>
    </method>
    <signal name="NameOwnerChanged">
      <arg type="s"/>
      <arg type="s"/>
      <arg type="s"/>
    </signal>
    <signal name="NameLost">
      <arg type="s"/>
    </signal>
    <signal name="NameAcquired">
      <arg type="s"/>
    </signal>
  </interface>
</node>
"

從輸出能夠看到會話總線對象支持標準接口「org.freedesktop.DBus.Introspectable」和接口 「org.freedesktop.DBus」。接口「org.freedesktop.DBus」有16個方法和3個信號。下表列出了 「org.freedesktop.DBus」的12個方法的簡要說明:

 

org.freedesktop.DBus.RequestName (in STRING name, in UINT32 flags, out UINT32 reply) 請求公衆名。其中flag定義以下:
DBUS_NAME_FLAG_ALLOW_REPLACEMENT 1
DBUS_NAME_FLAG_REPLACE_EXISTING 2
DBUS_NAME_FLAG_DO_NOT_QUEUE 4

返回值reply定義以下:
DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1
DBUS_REQUEST_NAME_REPLY_IN_QUEUE 2
DBUS_REQUEST_NAME_REPLY_EXISTS 3
DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER 4
org.freedesktop.DBus.ReleaseName (in STRING name, out UINT32 reply) 釋放公衆名。返回值reply定義以下:
DBUS_RELEASE_NAME_REPLY_RELEASED 1
DBUS_RELEASE_NAME_REPLY_NON_EXISTENT 2
DBUS_RELEASE_NAME_REPLY_NOT_OWNER 3 
org.freedesktop.DBus.Hello (out STRING unique_name) 一個應用在經過消息總線向其它應用發消息前必須先調用Hello獲取本身這個鏈接的惟一名。返回值就是鏈接的惟一名。dbus沒有定義專門的切斷鏈接命令,關閉socket就是切斷鏈接。
在1.2節的dbus-monitor輸出中能夠看到dbus-send調用消息總線的Hello方法。
org.freedesktop.DBus.ListNames (out ARRAY of STRING bus_names) 返回消息總線上已鏈接的全部鏈接名,包括全部公共名和惟一名。例如鏈接「org.fmddlmyy.Test」同時有公共名「org.fmddlmyy.Test」和惟一名「:1.21」,這兩個名稱都會被返回。
org.freedesktop.DBus.ListActivatableNames (out ARRAY of STRING bus_names) 返回全部能夠啓動的服務名。dbus支持按需啓動服務,即根據應用程序的請求啓動服務。
org.freedesktop.DBus.NameHasOwner (in STRING name, out BOOLEAN has_owner) 檢查是否有鏈接擁有指定名稱。
org.freedesktop.DBus.StartServiceByName (in STRING name, in UINT32 flags, out UINT32 ret_val) 按名稱啓動服務。參數flags暫未使用。返回值ret_val定義以下:
1 服務被成功啓動
2 已經有鏈接擁有要啓動的服務名
org.freedesktop.DBus.GetNameOwner (in STRING name, out STRING unique_connection_name) 返回擁有指定公衆名的鏈接的惟一名。
org.freedesktop.DBus.GetConnectionUnixUser (in STRING connection_name, out UINT32 unix_user_id) 返回指定鏈接對應的服務器進程的Unix用戶id。
org.freedesktop.DBus.AddMatch (in STRING rule) 爲當前鏈接增長匹配規則。
org.freedesktop.DBus.RemoveMatch (in STRING rule) 爲當前鏈接去掉指定匹配規則。
org.freedesktop.DBus.GetId (out STRING id) 返回消息總線的ID。這個ID在消息總線的生命期內是惟一的。

 

接口「org.freedesktop.DBus」的3個信號是:

 

org.freedesktop.DBus.NameOwnerChanged (STRING name, STRING old_owner, STRING new_owner) 指定名稱的擁有者發生了變化。
org.freedesktop.DBus.NameLost (STRING name) 通知應用失去了指定名稱的擁有權。
org.freedesktop.DBus.NameAcquired (STRING name) 通知應用得到了指定名稱的擁有權。

 

2.三、練習

讓咱們來試試消息總線提供的方法。

2.3.一、從ListName到d-feet的基本邏輯

用dbus-send調用:

$ dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListNames

輸出爲:

method return sender=org.freedesktop.DBus -> dest=:1.23 reply_serial=2
   array [
      string "org.freedesktop.DBus"
      string "org.freedesktop.Notifications"
      string "org.freedesktop.Tracker"
      string "org.freedesktop.PowerManagement"
      string ":1.7"
      string ":1.8"
      string "org.gnome.ScreenSaver"
      string ":1.9"
      string ":1.10"
      string ":1.22"
      string ":1.11"
      string "org.gnome.GnomeVFS.Daemon"
      string ":1.23"
      string ":1.12"
      string ":1.13"
      string ":1.0"
      string ":1.14"
      string ":1.1"
      string ":1.15"
      string ":1.2"
      string ":1.16"
      string ":1.3"
      string "org.gnome.GkbdConfigRegistry"
      string ":1.4"
      string "org.fmddlmyy.Test"
      string ":1.5"
      string "org.gnome.SettingsDaemon"
      string ":1.6"
   ]

這是會話總線當前已鏈接的鏈接名。在d-feet窗口的左側窗口顯示的就是ListNames返回的鏈接名。聰明的讀者也許已經想到使用消息總線的 「org.freedesktop.DBus.ListNames」方法和各鏈接的 「org.freedesktop.DBus.Introspectable.Introspect」,咱們就能夠像d-feet同樣查看總線上全部鏈接 的全部對象的全部接口的全部方法和信號。

你的想法很好。但有一個問題,咱們必須對鏈接中的對象調用「org.freedesktop.DBus.Introspectable.Introspect」方法。 ListNames只列出了鏈接名,咱們怎麼獲取鏈接中的對象路徑呢?

答案很簡單,若是咱們不知道對象路徑就從根目錄開始吧。鏈接中的對象是按照樹型結構組織的。咱們遍歷鏈接的對象樹就能夠找到全部的對象。調用對象的 「org.freedesktop.DBus.Introspectable.Introspect」方法就能夠查看對象的全部接口的全部方法和信號。例 如:假設咱們不知道鏈接"org.fmddlmyy.Test"裏有什麼對象,咱們能夠對根對象"/"執行:

$ dbus-send --session --type=method_call --print-reply --dest=org.fmddlmyy.Test / org.freedesktop.DBus.Introspectable.Introspect

輸出爲:

method return sender=:1.22 -> dest=:1.25 reply_serial=2
   string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
  <node name="TestObj"/>
</node>
"

"org.fmddlmyy.Test"的對象樹的根節點只有一個子節點"TestObj",再查看"/TestObj":

$ dbus-send --session --type=method_call --print-reply --dest=org.fmddlmyy.Test /TestObj org.freedesktop.DBus.Introspectable.Introspect

輸出爲:

method return sender=:1.22 -> dest=:1.26 reply_serial=2
   string "<!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="org.freedesktop.DBus.Introspectable">
    <method name="Introspect">
      <arg name="data" direction="out" type="s"/>
    </method>
  </interface>
  <interface name="org.freedesktop.DBus.Properties">
    <method name="Get">
      <arg name="interface" direction="in" type="s"/>
      <arg name="propname" direction="in" type="s"/>
      <arg name="value" direction="out" type="v"/>
    </method>
    <method name="Set">
      <arg name="interface" direction="in" type="s"/>
      <arg name="propname" direction="in" type="s"/>
      <arg name="value" direction="in" type="v"/>
    </method>
    <method name="GetAll">
      <arg name="interface" direction="in" type="s"/>
      <arg name="props" direction="out" type="a{sv}"/>
    </method>
  </interface>
  <interface name="org.fmddlmyy.Test.Basic">
    <method name="Add">
      <arg name="arg0" type="i" direction="in"/>
      <arg name="arg1" type="i" direction="in"/>
      <arg name="ret" type="i" direction="out"/>
    </method>
  </interface>
</node>
"

做爲一個練習,讓咱們來查看系統總線的上的bluez接口。執行:

$ dbus-send --system --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListNames

輸出爲:

method return sender=org.freedesktop.DBus -> dest=:1.30 reply_serial=2
   array [
      string "org.freedesktop.DBus"
      string ":1.7"
      string ":1.8"
      string ":1.9"
      string "org.freedesktop.SystemToolsBackends"
      string ":1.30"
      string "org.freedesktop.NetworkManagerInfo"
      string ":1.20"
      string "org.freedesktop.Avahi"
      string ":1.21"
      string "org.bluez"
      string ":1.22"
      string "org.freedesktop.NetworkManager"
      string "org.freedesktop.ConsoleKit"
      string ":1.23"
      string "com.redhat.dhcp"
      string ":1.13"
      string ":1.0"
      string ":1.14"
      string ":1.1"
      string ":1.15"
      string ":1.2"
      string "org.freedesktop.Hal"
      string "com.redhat.NewPrinterNotification"
      string ":1.16"
      string ":1.3"
      string ":1.17"
      string ":1.4"
      string ":1.18"
      string ":1.5"
      string ":1.19"
      string ":1.6"
   ]

咱們看到鏈接"org.bluez"。查看它的根對象:

$ dbus-send --system --type=method_call --print-reply --dest=org.bluez / org.freedesktop.DBus.Introspectable.Introspect

輸出爲:

method return sender=:1.7 -> dest=:1.31 reply_serial=2
   string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
  <node name="org"/>
</node>
"

接着查對象"/org":

$ dbus-send --system --type=method_call --print-reply --dest=org.bluez /org org.freedesktop.DBus.Introspectable.Introspect

輸出爲:

method return sender=:1.7 -> dest=:1.32 reply_serial=2
   string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
  <node name="bluez"/>
</node>
"

接着查對象"/org/bluez":

$ dbus-send --system --type=method_call --print-reply --dest=org.bluez /org/bluez org.freedesktop.DBus.Introspectable.Introspect

輸出爲:

method return sender=:1.7 -> dest=:1.33 reply_serial=2
   string "<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="/org/bluez">
        <interface name="org.bluez.Manager">
                <method name="InterfaceVersion">
                        <arg type="u" direction="out"/>
                </method>
                <method name="DefaultAdapter">
                        <arg type="s" direction="out"/>
                </method>
                <method name="FindAdapter">
                        <arg type="s" direction="in"/>
                        <arg type="s" direction="out"/>
                </method>
                <method name="ListAdapters">
                        <arg type="as" direction="out"/>
                </method>
                <method name="FindService">
                        <arg type="s" direction="in"/>
                        <arg type="s" direction="out"/>
                </method>
                <method name="ListServices">
                        <arg type="as" direction="out"/>
                </method>
                <method name="ActivateService">
                        <arg type="s" direction="in"/>
                        <arg type="s" direction="out"/>
                </method>
                <signal name="AdapterAdded">
                        <arg type="s"/>
                </signal>
                <signal name="AdapterRemoved">
                        <arg type="s"/>
                </signal>
                <signal name="DefaultAdapterChanged">
                        <arg type="s"/>
                </signal>
                <signal name="ServiceAdded">
                        <arg type="s"/>
                </signal>
                <signal name="ServiceRemoved">
                        <arg type="s"/>
                </signal>
        </interface>
        <interface name="org.bluez.Database">
                <method name="AddServiceRecord">
                        <arg type="ay" direction="in"/>
                        <arg type="u" direction="out"/>
                </method>
                <method name="AddServiceRecordFromXML">
                        <arg type="s" direction="in"/>
                        <arg type="u" direction="out"/>
                </method>
                <method name="UpdateServiceRecord">
                        <arg type="u" direction="in"/>
                        <arg type="ay" direction="in"/>
                </method>
                <method name="UpdateServiceRecordFromXML">
                        <arg type="u" direction="in"/>
                        <arg type="s" direction="in"/>
                </method>
                <method name="RemoveServiceRecord">
                        <arg type="u" direction="in"/>
                </method>
                <method name="RegisterService">
                        <arg type="s" direction="in"/>
                        <arg type="s" direction="in"/>
                        <arg type="s" direction="in"/>
                </method>
                <method name="UnregisterService">
                        <arg type="s" direction="in"/>
                </method>
                <method name="RequestAuthorization">
                        <arg type="s" direction="in"/>
                        <arg type="s" direction="in"/>
                </method>
                <method name="CancelAuthorizationRequest">
                        <arg type="s" direction="in"/>
                        <arg type="s" direction="in"/>
                </method>
        </interface>
        <interface name="org.bluez.Security">
                <method name="RegisterDefaultPasskeyAgent">
                        <arg type="s" direction="in"/>
                </method>
                <method name="UnregisterDefaultPasskeyAgent">
                        <arg type="s" direction="in"/>
                </method>
                <method name="RegisterPasskeyAgent">
                        <arg type="s" direction="in"/>
                        <arg type="s" direction="in"/>
                </method>
                <method name="UnregisterPasskeyAgent">
                        <arg type="s" direction="in"/>
                        <arg type="s" direction="in"/>
                </method>
                <method name="RegisterDefaultAuthorizationAgent">
                        <arg type="s" direction="in"/>
                </method>
                <method name="UnregisterDefaultAuthorizationAgent">
                        <arg type="s" direction="in"/>
                </method>
        </interface>
        <node name="service_audio"/>
        <node name="service_input"/>
        <node name="service_network"/>
        <node name="service_serial"/>
</node>
"

咱們看到了對象"/org/bluez"的全部接口。對象"/org/bluez"還有子節 點"service_audio"、"service_input"、"service_network"和"service_serial"。必要時我 們能夠接着查下去。d-feet的基本邏輯就是這樣。後面咱們會本身實現一個dteeth。dteeth是命令行程序,能夠遍歷指定鏈接的對象樹,列出所 有對象的全部接口的方法和信號。

 

 

 

2.3.二、ListActivatableNames和服務器的自動啓動

運行:

$ dbus-send --system --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListActivatableNames

$ dbus-send --session --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.ListActivatableNames

返回的數據是同樣的。在個人電腦上返回的數據是:

   array [
      string "org.freedesktop.DBus"
      string "org.freedesktop.Notifications"
      string "net.ekiga.helper"
      string "org.freedesktop.PowerManagement"
      string "org.freedesktop.Tracker"
      string "org.freedesktop.SystemToolsBackends.GroupsConfig"
      string "org.freedesktop.SystemToolsBackends.NTPConfig"
      string "org.gnome.Tomboy"
      string "org.freedesktop.SystemToolsBackends.HostsConfig"
      string "org.freedesktop.SystemToolsBackends.NFSConfig"
      string "org.freedesktop.SystemToolsBackends"
      string "net.ekiga.instance"
      string "org.gnome.GnomeVFS.Daemon"
      string "com.redhat.dhcp"
      string "org.freedesktop.SystemToolsBackends.TimeConfig"
      string "org.freedesktop.SystemToolsBackends.IfacesConfig"
      string "org.freedesktop.SystemToolsBackends.ServicesConfig"
      string "org.gnome.Rhythmbox"
      string "org.freedesktop.SystemToolsBackends.Platform"
      string "org.freedesktop.SystemToolsBackends.UsersConfig"
      string "org.freedesktop.SystemToolsBackends.SMBConfig"
      string "org.gnome.SettingsDaemon"
   ]

咱們也能夠用python腳本調用ListActivatableNames。例如:寫一個叫dls.py的腳本:

$ cat dls.py
#!/usr/bin/env python
import dbus
bus=dbus.SystemBus()
bus_obj=bus.get_object('org.freedesktop.DBus', '/')
iface=dbus.Interface(bus_obj, 'org.freedesktop.DBus')
names=iface.ListActivatableNames()
for n in names:
    print n

運行:

$ ./dls.py |sort

輸出爲:

com.redhat.dhcp
net.ekiga.helper
net.ekiga.instance
org.freedesktop.DBus
org.freedesktop.Notifications
org.freedesktop.PowerManagement
org.freedesktop.SystemToolsBackends
org.freedesktop.SystemToolsBackends.GroupsConfig
org.freedesktop.SystemToolsBackends.HostsConfig
org.freedesktop.SystemToolsBackends.IfacesConfig
org.freedesktop.SystemToolsBackends.NFSConfig
org.freedesktop.SystemToolsBackends.NTPConfig
org.freedesktop.SystemToolsBackends.Platform
org.freedesktop.SystemToolsBackends.ServicesConfig
org.freedesktop.SystemToolsBackends.SMBConfig
org.freedesktop.SystemToolsBackends.TimeConfig
org.freedesktop.SystemToolsBackends.UsersConfig
org.freedesktop.Tracker
org.gnome.GnomeVFS.Daemon
org.gnome.Rhythmbox
org.gnome.SettingsDaemon
org.gnome.Tomboy

使用python腳本調用dbus接口是否是很簡單。若是你看過dbus-glib的代碼(後面會講解),你對python的簡潔會有更深入的感觸。若是你執行:

$ cat /usr/share/dbus-1/services/*|grep Name|awk -F= '{print $2}'|sort

你會獲得:

com.redhat.dhcp
net.ekiga.helper
net.ekiga.instance
org.freedesktop.Notifications
org.freedesktop.PowerManagement
org.freedesktop.SystemToolsBackends
org.freedesktop.SystemToolsBackends.GroupsConfig
org.freedesktop.SystemToolsBackends.HostsConfig
org.freedesktop.SystemToolsBackends.IfacesConfig
org.freedesktop.SystemToolsBackends.NFSConfig
org.freedesktop.SystemToolsBackends.NTPConfig
org.freedesktop.SystemToolsBackends.Platform
org.freedesktop.SystemToolsBackends.ServicesConfig
org.freedesktop.SystemToolsBackends.SMBConfig
org.freedesktop.SystemToolsBackends.TimeConfig
org.freedesktop.SystemToolsBackends.UsersConfig
org.freedesktop.Tracker
org.gnome.GnomeVFS.Daemon
org.gnome.Rhythmbox
org.gnome.SettingsDaemon
org.gnome.Tomboy

這條命令的輸出與ListActivatableNames的輸出是否是基本相同?你能看懂上面這條命令嗎?它將"/usr/share /dbus-1/services/"下全部文件交給grep篩選出包含「Name」的行。將包含「Name」的行交給awk處理,awk用"="做爲列 分隔符,取出第二列而後交給sort排序後輸出。 "/usr/share/dbus-1/services/"目錄就是dbus放service文件的地方。須要自動啓動的服務器會在這個目錄放一個 service文件,例如:

$ cat /usr/share/dbus-1/services/dhcdbd.service
[D-BUS Service]
Name=com.redhat.dhcp
Exec=/usr/sbin/dhcdbd

Name是服務器的公共名,Exec是服務器的執行路徑。在客戶請求一個服務,但該服務尚未啓動時。dbus會根據service文件自動啓動服務。咱們再寫一個調用「org.fmddlmyy.Test」的Add接口的python腳本:

$ cat add.py
#!/usr/bin/env python
import dbus
bus = dbus.SessionBus()
obj = bus.get_object( 'org.fmddlmyy.Test', '/TestObj' )
iface = dbus.Interface(obj, 'org.fmddlmyy.Test.Basic')
sum = iface.Add(100, 999)
print sum

在啓動「org.fmddlmyy.Test」服務器前調用這個腳本

$ ./add.py

會獲得錯誤輸出:

...
dbus.exceptions.DBusException: org.freedesktop.DBus.Error.ServiceUnknown: The name org.fmddlmyy.Test was not provided by any .service files

咱們編輯一個service文件:

$ cat org.fmddlmyy.Test.service
[D-BUS Service]
Name=org.fmddlmyy.Test
Exec=/home/lvjie/work/dbus/hello-dbus3-0.1/src/example-service

把這個文件放到"/usr/share/dbus-1/services/"目錄後,再執行add.py:

$ sudo cp org.fmddlmyy.Test.service /usr/share/dbus-1/services/
$ cd ../../py
$ ./add.py
1099

此次dbus自動啓動了服務器,咱們的客戶腳本獲得了正確的輸出,你有沒有感到dbus的神奇?dbus在自動啓動服務器後,不會自動關閉。若是沒人管它,這個服務器會一直開着。

2.3.三、其它方法

再演示幾個「org.freedesktop.DBus」接口的方法。NameHasOwner判斷有沒有鏈接擁有指定的公共名:

$ dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.NameHasOwner string:"org.fmddlmyy.Test"

輸出爲:

method return sender=org.freedesktop.DBus -> dest=:1.31 reply_serial=2
   boolean true

GetNameOwner返回公共名對應的惟一名:

$ dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.GetNameOwner string:"org.fmddlmyy.Test"

輸出爲:

method return sender=org.freedesktop.DBus -> dest=:1.32 reply_serial=2
   string ":1.30"

GetConnectionUnixUser返回指定鏈接對應的服務器進程的Unix用戶id:

$ dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.GetConnectionUnixUser string:":1.30"

輸出爲:

method return sender=org.freedesktop.DBus -> dest=:1.33 reply_serial=2
   uint32 1000

這就是個人用戶id:

$ id -u lvjie
1000

GetId返回消息總線的ID:

$ dbus-send --session --type=method_call --print-reply --dest=org.freedesktop.DBus / org.freedesktop.DBus.GetId

輸出爲:

method return sender=org.freedesktop.DBus -> dest=:1.34 reply_serial=2
   string "dc209fee5f8ce01b0c23da0049668f11"

3 結束語

這一集有一些python代碼。即便你沒有學過python,我也建議你看一看、試一試。其實我也沒有學過python。小時候,好像聽過什麼德國 人一邊看說明書一邊開飛機的笑話。這或許是吹牛,但對於程序員來講一邊查手冊一邊用新語言寫一些簡單程序應該不算困難。下一講咱們要寫一個略大點的 python程序。雖然我習慣於C/C++的事必躬親,但不能否認python確實是頗有魅力的語言,難怪在嵌入式環境也有那麼多人用python做原型 開發。

 

 

 

我想在freerunner(一個開源linux手機)上查看fso(openmoko的諸多軟件版本之一)的dbus信 息。但fso的python沒有gtk模塊,跑不了d-feet。在上一講我介紹了d-feet的基本思路:用 「org.freedesktop.DBus.ListNames」枚舉消息總線上的鏈接,用 「org.freedesktop.DBus.Introspectable.Introspect」 從"/"開始遍歷鏈接的對象樹。上一講咱們手工查看了兩個鏈接,那麼咱們能不能寫一個程序自動遍歷鏈接的對象樹,輸出指定鏈接的全部對象的全部接口的全部 方法和信號?

固然能夠,爲此我寫了一個叫dteeth的python腳本。不過在介紹這個腳本前,讓咱們先看看dbus的數據類型。

一、dbus的數據類型

dbus用xml描述接口,例如:

<?xml version="1.0" encoding="UTF-8" ?>

<node name="/org/freesmartphone/GSM/Device">
  <interface name="org.freesmartphone.GSM.SMS">
    <method name="SendMessage">
       <arg name="number" type="s"/>
       <arg name="contents" type="s"/>
       <arg name="featuremap" type="a{sv}"/>
       <arg type="i" direction="out"/>
    </method>
    <signal name="IncomingMessage">
       <arg name="address" type="s"/>
       <arg name="contents" type="s"/>
       <arg name="features" type="a{sv}"/>
    </signal>
  </interface>
</node>

其實前兩講已經看過不少例子了。node就是接口中的對象,node能夠包含node,構成對象樹。 dbus的接口描述文件統一採用utf-8編碼。我相信讀者很容易理解這個接口描述文件。我只想解釋一下描述參數數據類型的type域。 dbus的數據類型是由"s"或"a{sv}"這樣的類型簽名(Type Signatures)定義的。類型簽名中可使用如下標記:

a ARRAY 數組
b BOOLEAN 布爾值
d DOUBLE IEEE 754雙精度浮點數
g SIGNATURE 類型簽名
i INT32 32位有符號整數
n INT16 16位有符號整數
o OBJECT_PATH 對象路徑
q UINT16 16位無符號整數
s STRING 零結尾的UTF-8字符串
t UINT64 64位無符號整數
u UINT32 32位無符號整數
v VARIANT 能夠聽任意數據類型的容器,數據中包含類型信息。例如glib中的GValue。
x INT64 64位有符號整數
y BYTE 8位無符號整數
() 定義結構時使用。例如"(i(ii))"
{} 定義鍵-值對時使用。例如"a{us}"

a表示數組,數組元素的類型由a後面的標記決定。例如:

  • "as"是字符串數組。
  • 數組"a(i(ii))"的元素是一個結構。用括號將成員的類型括起來就表示結構了,結構能夠嵌套。
  • 數組"a{sv}"的元素是一個鍵-值對。"{sv}"表示鍵類型是字符串,值類型是VARIANT。

在之後的例子中,咱們會親手實現上面這個xml描述的接口,包括服務器和客戶程序。到時候,讀者會對dbus的數據類型有更直觀的認識。

二、dteeth

2.一、運行dteeth

能夠從這裏下載dteeth的源代碼。其中包含兩個python腳本:dteeth.py和_introspect_parser.py。 dteeth.py是我寫的。_introspect_parser.py是個開源模塊,能夠分析Introspect返回的xml數據。

dteeth用法以下:

$ ./dteeth.py -h
Usage: dteeth [--system] <name of a connection on the bus >

默認鏈接session總線,除非你加上--system。能夠一次指定同一消息總線的多個鏈接。先在PC上試一試:

$ ./dteeth.py org.fmddlmyy.Test
org.fmddlmyy.Test
    /TestObj
        org.fmddlmyy.Test.Basic
            methods
                Add( in i arg0 , in i arg1 , out i ret )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s data )
        org.freedesktop.DBus.Properties
            methods
                Set( in s interface , in s propname , in v value )
                GetAll( in s interface , out a{sv} props )
                Get( in s interface , in s propname , out v value )

我也在fso版本的freerunner手機上運行了一下,獲得了org.freesmartphone.ogsmd的全部對象的全部的接口的全部方法和信號:

org.freesmartphone.ogsmd
    /org/freedesktop/Gypsy
        org.freedesktop.Gypsy.Time
            signals
                TimeChanged( i time )
            methods
                GetTime( out i )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freedesktop.Gypsy.Device
            signals
                FixStatusChanged( i fixstatus )
                ConnectionStatusChanged( b constatus )
            methods
                GetConnectionStatus( out b )
                Stop( )
                Start( )
                GetFixStatus( out i )
        org.freedesktop.Gypsy.Course
            signals
                CourseChanged( i fields , i tstamp , d speed , d heading , d climb )
            methods
                GetCourse( out i , out i , out d , out d , out d )
        org.freedesktop.Gypsy.Position
            signals
                PositionChanged( i fields , i tstamp , d lat , d lon , d alt )
            methods
                GetPosition( out i , out i , out d , out d , out d )
        org.freedesktop.Gypsy.Accuracy
            signals
                AccuracyChanged( i fields , d pdop , d hdop , d vdop )
            methods
                GetAccuracy( out i , out d , out d , out d )
        org.freesmartphone.Resource
            methods
                Enable( )
                Disable( )
                Suspend( )
                Resume( )
        org.freedesktop.Gypsy.Satellite
            signals
                SatellitesChanged( a(ubuuu) satellites )
            methods
                GetSatellites( out a(ubuuu) )
        org.freesmartphone.GPS.UBX
            signals
                DebugPacket( s clid , i length , aa{sv} data )
            methods
                SendDebugPacket( in s clid , in i length , in aa{sv} data )
                GetDebugFilter( in s clid , out b )
                SetDebugFilter( in s clid , in b state )
        org.freedesktop.Gypsy.Server
            methods
                Create( in s device , out o )
                Shutdown( in o path )
    /org/freesmartphone/Device/Audio
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.Audio
            signals
                SoundStatus( s name , s status , a{sv} properties )
                Scenario( s scenario , s reason )
            methods
                SetScenario( in s name )
                GetInfo( out s )
                GetAvailableScenarios( out as )
                PushScenario( in s name )
                GetScenario( out s )
                PullScenario( out s )
                StopSound( in s name )
                StopAllSounds( )
                PlaySound( in s name )
                StoreScenario( in s name )
    /org/freesmartphone/Device/Display/pcf50633_bl
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.Display
            methods
                SetBrightness( in i brightness )
                GetName( out s )
                SetBacklightPower( in b power )
                GetBrightness( out i )
                GetBacklightPower( out b )
    /org/freesmartphone/Device/IdleNotifier/0
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.IdleNotifier
            signals
                State( s state )
            methods
                SetState( in s state )
                GetState( out s )
                SetTimeout( in s state , in i timeout )
                GetTimeouts( out a{si} )
    /org/freesmartphone/Device/Info
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.Info
            methods
                GetCpuInfo( out a{sv} )
    /org/freesmartphone/Device/Input
        org.freesmartphone.Device.Input
            signals
                Event( s name , s action , i seconds )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
    /org/freesmartphone/Device/LED/gta02_aux_red
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.LED
            methods
                SetBrightness( in i brightness )
                GetName( out s )
                SetBlinking( in i delay_on , in i delay_off )
    /org/freesmartphone/Device/LED/gta02_power_blue
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.LED
            methods
                SetBrightness( in i brightness )
                GetName( out s )
                SetBlinking( in i delay_on , in i delay_off )
    /org/freesmartphone/Device/LED/gta02_power_orange
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.LED
            methods
                SetBrightness( in i brightness )
                GetName( out s )
                SetBlinking( in i delay_on , in i delay_off )
    /org/freesmartphone/Device/LED/neo1973_vibrator
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.LED
            methods
                SetBrightness( in i brightness )
                GetName( out s )
                SetBlinking( in i delay_on , in i delay_off )
    /org/freesmartphone/Device/PowerControl/Bluetooth
        org.freesmartphone.Device.PowerControl
            signals
                Power( s device , b power )
            methods
                Reset( )
                GetName( out s )
                SetPower( in b power )
                GetPower( out b )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Resource
            methods
                Resume( )
                Enable( )
                Disable( )
                Suspend( )
    /org/freesmartphone/Device/PowerControl/UsbHost
        org.freesmartphone.Device.PowerControl
            signals
                Power( s device , b power )
            methods
                Reset( )
                GetName( out s )
                SetPower( in b power )
                GetPower( out b )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
    /org/freesmartphone/Device/PowerControl/WiFi
        org.freesmartphone.Device.PowerControl
            signals
                Power( s device , b power )
            methods
                Reset( )
                GetName( out s )
                SetPower( in b power )
                GetPower( out b )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Resource
            methods
                Resume( )
                Enable( )
                Disable( )
                Suspend( )
    /org/freesmartphone/Device/PowerSupply/apm
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.PowerSupply
            methods
                GetName( out s )
                GetEnergyPercentage( out i )
                GetOnBattery( out b )
                GetInfo( out a{sv} )
    /org/freesmartphone/Device/PowerSupply/bat
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.PowerSupply
            signals
                PowerStatus( s status )
                Capacity( i percent )
            methods
                GetEnergyPercentage( out i )
                GetInfo( out a{sv} )
                IsPresent( out b )
                GetName( out s )
                GetCapacity( out i )
                GetPowerStatus( out s )
    /org/freesmartphone/Device/RealTimeClock/rtc0
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Device.RealTimeClock
            methods
                GetWakeupReason( out s )
                SetCurrentTime( in s t )
                Suspend( )
                GetWakeupTime( out s )
                GetName( out s )
                GetCurrentTime( out s )
                SetWakeupTime( in s t )
    /org/freesmartphone/Events
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Events
            methods
                AddRule( in s rule_str )
                TriggerTest( in s name , in b value )
    /org/freesmartphone/Framework
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Framework
            methods
                GetDebugLevel( in s logger , out s )
                GetDebugDestination( out s , out s )
                ListDebugLoggers( out as )
                ListObjectsInSubsystem( in s subsystem , out as )
                SetDebugDestination( in s category , in s destination )
                SetDebugLevel( in s logger , in s levelname )
                ListObjectsByInterface( in s interface , out ao )
                ListSubsystems( out as )
    /org/freesmartphone/GSM/Device
        org.freesmartphone.GSM.Call
            signals
                CallStatus( i index , s status , a{sv} properties )
            methods
                Activate( in i index )
                Emergency( in s number )
                SendDtmf( in s tones )
                ReleaseHeld( )
                HoldActive( )
                ReleaseAll( )
                Initiate( in s number , in s type_ , out i )
                ListCalls( out a(isa{sv}) )
                Transfer( in s number )
                Release( in i index )
                ActivateConference( in i index )
        org.freesmartphone.GSM.Debug
            methods
                DebugInjectString( in s channel , in s string )
                DebugCommand( in s command , out as )
                DebugEcho( in s echo , out s )
                DebugListChannels( out as )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.GSM.Device
            methods
                CancelCommand( )
                GetInfo( out a{sv} )
                GetAntennaPower( out b )
                SetSimBuffersSms( in b sim_buffers_sms )
                GetFeatures( out a{sv} )
                SetAntennaPower( in b power )
                GetSimBuffersSms( out b )
        org.freesmartphone.GSM.SMS
            signals
                IncomingMessage( s address , s text , a{sv} features )
            methods
                SendMessage( in s number , in s contents , in a{sv} featuremap , out i )
        org.freesmartphone.GSM.SIM
            signals
                ReadyStatus( b status )
                MemoryFull( )
                AuthStatus( s status )
                IncomingStoredMessage( i index )
            methods
                RetrievePhonebook( in s category , out a(iss) )
                SendAuthCode( in s code )
                ChangeAuthCode( in s old_pin , in s new_pin )
                SendGenericSimCommand( in s command , out s )
                ListPhonebooks( out as )
                SetServiceCenterNumber( in s number )
                GetHomeZones( out a(siii) )
                RetrieveEntry( in s category , in i index , out s , out s )
                DeleteMessage( in i index )
                SendRestrictedSimCommand( in i command , in i fileid , in i p1 , in i p2 , in i p3 , in s data , out i , out i , out s )
                GetMessagebookInfo( out a{sv} )
                GetSimReady( out b )
                GetPhonebookInfo( in s category , out a{sv} )
                GetSimInfo( out a{sv} )
                SendStoredMessage( in i index , out i )
                SetAuthCodeRequired( in b required , in s pin )
                GetAuthStatus( out s )
                StoreMessage( in s number , in s contents , in a{sv} featuremap , out i )
                GetAuthCodeRequired( out b )
                RetrieveMessage( in i index , out s , out s , out s , out a{sv} )
                StoreEntry( in s category , in i index , in s name , in s number )
                Unlock( in s puk , in s new_pin )
                GetServiceCenterNumber( out s )
                RetrieveMessagebook( in s category , out a(isssa{sv}) )
                DeleteEntry( in s category , in i index )
        org.freesmartphone.GSM.Network
            signals
                Status( a{sv} status )
                SignalStrength( i strength )
                IncomingUssd( s mode , s message )
            methods
                EnableCallForwarding( in s reason , in s class_ , in s number , in i timeout )
                ListProviders( out a(isss) )
                GetCallForwarding( in s reason , out a{sv} )
                Unregister( )
                SetCallingIdentification( in s status )
                Register( )
                SendUssdRequest( in s request )
                DisableCallForwarding( in s reason , in s class_ )
                GetSignalStrength( out i )
                GetCallingIdentification( out s )
                RegisterWithProvider( in i operator_code )
                GetNetworkCountryCode( out s )
                GetStatus( out a{sv} )
        org.freesmartphone.Resource
            methods
                Enable( )
                Disable( )
                Suspend( )
                Resume( )
        org.freesmartphone.GSM.CB
            signals
                IncomingCellBroadcast( i channel , s data )
            methods
                GetCellBroadcastSubscriptions( out s )
                SetCellBroadcastSubscriptions( in s channels )
        org.freesmartphone.GSM.PDP
            signals
                ContextStatus( i index , s status , a{sv} properties )
            methods
                SetCurrentGprsClass( in s class_ )
                ActivateContext( in s apn , in s user , in s password )
                DeactivateContext( )
                ListAvailableGprsClasses( out as )
                GetContextStatus( out s )
                GetCurrentGprsClass( out s )
    /org/freesmartphone/GSM/Server
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.GSM.HZ
            signals
                HomeZoneStatus( s zone )
            methods
                GetHomeZoneStatus( out s )
                GetKnownHomeZones( out as )
    /org/freesmartphone/PIM/Contacts
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.PIM.Contacts
            methods
                Query( in a{sv} query , out s )
                Add( in a{sv} contact_data , out s )
                GetSingleContactSingleField( in a{sv} query , in s field_name , out s )
        org.freesmartphone.PIM.Contact
            methods
                GetContent( out a{sv} )
                GetMultipleFields( in s field_list , out a{sv} )
    /org/freesmartphone/PIM/Contacts/Queries
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.PIM.ContactQuery
            methods
                GetContactPath( out s )
                Skip( in i num_entries )
                Dispose( )
                GetResult( out a{sv} )
                GetResultCount( out i )
                Rewind( )
                GetMultipleResults( in i num_entries , out aa{sv} )
    /org/freesmartphone/PIM/Messages
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.PIM.Messages
            signals
                NewMessage( s message_URI )
            methods
                GetSingleMessageSingleField( in a{sv} query , in s field_name , out s )
                Query( in a{sv} query , out s )
                Add( in a{sv} message_data , out s )
                GetFolderURIFromName( in s folder_name , out s )
                GetFolderNames( out as )
        org.freesmartphone.PIM.Message
            methods
                GetContent( out a{sv} )
                MoveToFolder( in s new_folder_name )
                GetMultipleFields( in s field_list , out a{sv} )
    /org/freesmartphone/PIM/Messages/Folders
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.PIM.Messages
            signals
                NewMessage( s message_URI )
            methods
                GetSingleMessageSingleField( in a{sv} query , in s field_name , out s )
                Query( in a{sv} query , out s )
                Add( in a{sv} message_data , out s )
                GetFolderURIFromName( in s folder_name , out s )
                GetFolderNames( out as )
        org.freesmartphone.PIM.Message
            methods
                GetContent( out a{sv} )
                MoveToFolder( in s new_folder_name )
                GetMultipleFields( in s field_list , out a{sv} )
    /org/freesmartphone/PIM/Messages/Folders/0
        org.freesmartphone.PIM.MessageFolder
            signals
                MessageMoved( s message_uri , s new_folder_name )
            methods
                GetMessageCount( out i )
                GetMessageURIs( in i first_message_id , in i message_count , out as )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
    /org/freesmartphone/PIM/Messages/Folders/1
        org.freesmartphone.PIM.MessageFolder
            signals
                MessageMoved( s message_uri , s new_folder_name )
            methods
                GetMessageCount( out i )
                GetMessageURIs( in i first_message_id , in i message_count , out as )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
    /org/freesmartphone/PIM/Messages/Queries
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.PIM.MessageQuery
            methods
                Skip( in i num_entries )
                Dispose( )
                GetResult( out a{sv} )
                GetResultCount( out i )
                Rewind( )
                GetMultipleResults( in i num_entries , out a{ia{sv}} )
                GetMessageURI( out s )
    /org/freesmartphone/PIM/Sources
        org.freesmartphone.PIM.Sources
            methods
                GetEntryCount( out i )
                InitAllEntries( )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.PIM.Source
            methods
                GetSupportedPIMDomains( out as )
                GetName( out s )
                GetStatus( out s )
    /org/freesmartphone/Phone
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Phone
            signals
                Incoming( o call )
            methods
                InitProtocols( out as )
                CreateCall( in s number , in s protocol , in b force , out o )
    /org/freesmartphone/Preferences
        org.freesmartphone.Preferences
            methods
                GetProfiles( out as )
                GetService( in s name , out o )
                GetServices( out as )
                SetProfile( in s profile )
                GetProfile( out s )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
    /org/freesmartphone/Preferences/rules
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Preferences.Service
            signals
                Notify( s key , v value )
            methods
                GetType( in s key , out s )
                SetValue( in s key , in v value )
                GetKeys( out as )
                IsProfilable( in s key , out b )
                GetValue( in s key , out v )
    /org/freesmartphone/Time
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Time
            signals
                Minute( i year , i mon , i day , i hour , i min , i sec , i wday , i yday , i isdst )
            methods
                GetLocalTime( in i seconds , out i , out i , out i , out i , out i , out i , out i , out i , out i )
    /org/freesmartphone/Time/Alarm
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )
        org.freesmartphone.Time.Alarm
            methods
                ClearAlarm( in s busname )
                SetAlarm( in s busname , in i timestamp )
    /org/freesmartphone/Usage
        org.freesmartphone.Usage
            signals
                ResourceAvailable( s resourcename , b state )
                ResourceChanged( s resourcename , b state , a{sv} attributes )
            methods
                ReleaseResource( in s resourcename )
                Suspend( )
                GetResourceState( in s resourcename , out b )
                SetResourcePolicy( in s resourcename , in s policy )
                GetResourcePolicy( in s resourcename , out s )
                GetResourceUsers( in s resourcename , out as )
                ListResources( out as )
                RegisterResource( in s resourcename , in o path )
                RequestResource( in s resourcename )
        org.freedesktop.DBus.Introspectable
            methods
                Introspect( out s )

2.二、源代碼

下面是dteeth的源代碼:

$ cat -n dteeth.py
     1  #!/usr/bin/env python
     2  # -*- coding: utf-8 -*-
     3
     4  import dbus
     5  import _introspect_parser
     6  import getopt, sys
     7
     8  MARGIN_WIDTH = 4
     9  ONE_MARGIN = ' ' * MARGIN_WIDTH
    10
    11  # signal是個元組,它有一個元素,是一個列表。列表的元素是signal的參數
    12  # 列表的每一個元素都是字典。它有兩個元素,鍵值分別是'type'和'name'
    13  def show_signal(name, signal, margin):
    14      print margin+name+'(',
    15      args = signal[0]
    16      for i, arg in enumerate(args):
    17          if i > 0:
    18              print ',',
    19          if arg['name']:
    20              print '%s %s' % (arg['type'], arg['name']),
    21          else:
    22              print '%s' % arg['type'],
    23      print  ')'
    24
    25  # method是個元組,它有兩個元素,都是列表。前一個列表的元素是輸入參數,後一個列表的元素是輸出參數
    26  def show_method(name, method, margin):
    27      print margin+name+'(',
    28  # 輸入參數
    29      args = method[0]
    30      in_num = len(args)
    31      out_num = len(method[1])
    32      for i, arg in enumerate(args):
    33          if i > 0:
    34              print ',',
    35          if arg['name']:
    36              print 'in %s %s' % (arg['type'], arg['name']),
    37          else:
    38              print 'in %s' % arg['type'],
    39  # 輸出參數
    40      if (in_num > 0) and (out_num > 0) :
    41          print ',',
    42      args = method[1]
    43      for i, arg in enumerate(args):
    44          if i > 0:
    45              print ',',
    46          if arg['name']:
    47              print 'out %s %s' % (arg['type'], arg['name']),
    48          else:
    49              print 'out %s' % arg['type'],
    50      print  ')'
    51
    52  def show_property(name, property, margin):
    53      print margin+name
    54      print margin,
    55      print property
    56
    57  # interfaces是個字典,它有三個元素,鍵值分別是'signals'、'methods'和'properties'
    58  def show_iface(name, iface, margin):
    59      print margin + name
    60      margin += ONE_MARGIN
    61      signals=iface['signals']
    62      l = len(signals)
    63      if l > 0:
    64          print margin+'signals'
    65          for node in signals:
    66              show_signal(node, signals[node], margin+ONE_MARGIN)
    67
    68      methods=iface['methods']
    69      l = len(methods)
    70      if l > 0:
    71          print margin+'methods'
    72          for node in methods:
    73              show_method(node, methods[node], margin+ONE_MARGIN)
    74
    75      properties=iface['properties']
    76      l = len(properties)
    77      if l > 0:
    78          print margin+'properties'
    79          for node in properties:
    80              show_property(node, properties[node], margin+ONE_MARGIN)
    81
    82  def show_obj(bus, name, obj_name, margin):
    83      obj=bus.get_object(name, obj_name)
    84      iface=dbus.Interface(obj, 'org.freedesktop.DBus.Introspectable')
    85      xml=iface.Introspect();
    86      data = _introspect_parser.process_introspection_data(xml)
    87
    88      # data是個字典,它有兩個元素,鍵值分別是'child_nodes'和'interfaces'
    89      if len(data['interfaces']) > 0:
    90          print margin + obj_name
    91
    92      for node in data['interfaces']:
    93          iface=data['interfaces'][node]
    94          show_iface(node, iface, margin+ONE_MARGIN)
    95
    96      for node in data['child_nodes']:
    97          if obj_name == '/':
    98              show_obj(bus, name, '/' + node, margin)
    99          else:
   100              show_obj(bus, name, obj_name + '/' + node, margin)
   101
   102  def show_connection(bus, name, margin):
   103      print margin + name
   104      show_obj(bus, name, '/', margin+ONE_MARGIN)
   105
   106  def usage():
   107      print "Usage: dteeth [--system] "
   108
   109  def main():
   110      try:
   111          opts, args = getopt.getopt(sys.argv[1:], "h", ["help", "system"])
   112      except getopt.GetoptError, err:
   113          # print help information and exit:
   114          print str(err) # will print something like "option -a not recognized"
   115          usage()
   116          sys.exit(2)
   117
   118      if len(args) == 0:
   119          usage()
   120          sys.exit(2)
   121
   122      use_system = False
   123      for o, a in opts:
   124          if o in ("-h", "--help"):
   125              usage()
   126              sys.exit()
   127          if o == "--system":
   128              use_system = True
   129          else:
   130              assert False, "unhandled option"
   131
   132      if use_system:
   133          bus=dbus.SystemBus()
   134      else:
   135          bus=dbus.SessionBus()
   136
   137      for arg in args:
   138          show_connection(bus, arg, "")
   139
   140  if __name__ == "__main__":
   141      main()

dteeth是我寫的第一個超過10行的python腳本。對於熟悉python的讀者,dteeth應該是很簡單的。不過我仍是簡單解釋一下dteeth的主要邏輯。

2.三、dteeth的主要邏輯

main函數分析命令行,對命令行上指定的每一個鏈接調用show_connection函數。 show_connection在打印鏈接名後調用show_obj從根對象"/"開始遍歷鏈接的對象樹。

show_obj對輸入對象調用Introspect方法,返回的xml數據交由_introspect_parser處理。 _introspect_parser會從xml數據中分出inerface和node。 show_obj對inerface調用show_iface顯示。 show_obj對node會遞歸調用show_obj,實現對象樹的遍歷。

2.四、_introspect_parser的輸出格式

_introspect_parser.process_introspection_data函數分析Introspect方法返回的xml數據。爲了瞭解_introspect_parser的輸出格式,咱們能夠寫個小腳本:

$ cat ti.py
#!/usr/bin/env python
import dbus
import _introspect_parser
bus=dbus.SessionBus()
obj=bus.get_object('org.freesmartphone.ogsmd', '/org/freesmartphone/GSM/Device')
iface=dbus.Interface(obj, 'org.freedesktop.DBus.Introspectable')
xml=iface.Introspect();
data = _introspect_parser.process_introspection_data(xml)
print data

能夠用這個腳本直接打印process_introspection_data返回的數據。下面是整理後的輸出:

{
	'interfaces': 
	{
		u'org.freedesktop.DBus.Introspectable': 
		{
			'signals': {}, 
			'methods': 
			{
				u'Introspect': 
				(
					[], 
					[{'type': u's', 'name': u'data'}]
				)
			}, 
			'properties': {}
		}, 
		u'org.freedesktop.DBus.Properties': 
		{
			'signals': {}, 
			'methods': 
			{
				u'Set': 
				(
					[{'type': u's', 'name': u'interface'}, {'type': u's', 'name': u'propname'}, {'type': u'v', 'name': u'value'}], 
					[]
				), 
				u'GetAll': 
				(
					[{'type': u's', 'name': u'interface'}], 
					[{'type': u'a{sv}', 'name': u'props'}]
				), 
				u'Get': 
				(
					[{'type': u's', 'name': u'interface'}, {'type': u's', 'name': u'propname'}], 
					[{'type': u'v', 'name': u'value'}]
				)
			}, 
			'properties': {}
		}, 
		u'org.freesmartphone.GSM.SMS': 
		{
			'signals': 
			{
				u'IncomingMessage': 
				(
					[{'type': u's', 'name': None}, {'type': u's', 'name': None}, {'type': u'a{sv}', 'name': None}],
				)
			},
			'methods': 
			{
				u'SendMessage': 
				(
					[{'type': u's', 'name': u'number'}, {'type': u's', 'name': u'contents'}, {'type': u'a{sv}', 'name': u'featuremap'}], 
					[{'type': u'i', 'name': u'arg3'}]
				)
			},
			'properties': {}
		}
	}, 
	'child_nodes': []
}

全部字符串前面都有前綴u,表示這些字符串都是Unicode編碼。在python中,字典用{},元組用(),列表用[]。從括號咱們就能看出數據格式。

咱們看到process_introspection_data返回返回一個字典。這個字典有兩個映射。一個映射的鍵值是"interfaces",另外一個映射的鍵值是"child_nodes"。

  • 映射"child_nodes"的值是一個列表,列出全部子節點的名稱。

  • 映射"interfaces"的值仍是一個字典。這個字典的每一個映射的鍵值是一個接口名稱。每一個映射的值類型仍是字典, 這個字典有3個映射,映射的鍵值分別是'signals'、'methods'和'properties',映射的值類型都是字典。

    • 'signals'對應字典的每一個鍵值是一個信號名稱。每一個映射的值類型是元組。這個元組只有一個元素,類型是列表, 即信號的參數列表。

      • 參數列表的元素類型是字典。這個字典有2個映射,映射的鍵值分別是'type'和'name'。'type'是參數類型,'name'是參數名稱。 映射的值類型都是字符串。

    • 'methods'對應字典的每一個鍵值是一個方法名稱。每一個映射的值類型是元組。這個元組有兩個元素,類型是列表, 分別是方法的輸入參數列表和輸出參數列表。參數列表的元素類型和信號的參數列表相同。

    • 我看到'properties'映射都是空的,就沒有研究。

三、python基礎

簡單介紹一下與dteeth有關的python語法。

3.一、代碼塊和縮進

python用縮進來區分語句所屬的代碼塊,從類定義、函數到for、if的代碼塊都是用縮進來去區分的。沒有縮進的代碼塊是腳本的主體代碼。一個 腳本文件也被稱做一個模塊。無論模塊被直接運行仍是被其它模塊導入,主體代碼都會在載入時被執行。例如dteeth的主體代碼只有兩句:

   140  if __name__ == "__main__":
   141      main()

__xxx__這樣的標誌符一般是python的系統變量。若是模塊被導入,__name__的值是模塊的名字。若是模塊被直接執行,__name__的值是"__main__"。咱們一般在模塊被直接執行時,調用主函數或模塊的測試函數。

3.二、腳本文件格式

python腳本的起始行一般是:/p>

     1  #!/usr/bin/env python

env是一個能夠修改環境變量並執行程序的工具,它能夠自動在系統路徑中搜索要執行的程序。 python腳本文件必須以0A爲換行符,默認僅支持ASCII字符。若是要寫中文註釋(顯然是不提倡的),能夠在起始行後用如下語句將文件指定爲utf-8編碼:

     2  # -*- coding: utf-8 -*-

這時,文件必須被保存爲沒有BOM的utf-8編碼文件。

3.三、列表、元組和字典

列表相似於C的數組,列表元素用[]包括。元組是不可變的列表,元組元素用()包括。元組的元素個數和類型在建立後就不能改變了。元組中基本類型的值是不能改變的,但若是元組的一個元素是列表,咱們能夠改變列表內容,即咱們能夠改變元組中可變元素的內容。例如:

>>> a=(1,2,['abc'])
>>> a[2]='def'
Traceback (most recent call last):
  File "", line 1, in 
TypeError: 'tuple' object does not support item assignment
>>> a[2][0]='def'
>>> a
(1, 2, ['def'])

字典是鍵-值對的集合,字典元素用{}包括。

四、結束語

本文介紹了一個叫做dteeth的python腳本。這個腳本邏輯很簡單,讀者能夠根據須要修改或擴充。講了這麼多dbus,咱們尚未接觸C代碼。下一講,咱們討論dbus的C實例。

 

 

 

dbus-glib是dbus底層接口的一個封裝。本講咱們用dbus-glib作一個dus接口,並寫一個客戶程序。

一、接口

1.一、編寫接口描述文件

首先編寫接口描述文件。咱們要實現的鏈接的公共名是"org.freesmartphone.ogsmd",接口描述文件以下:

$ cat smss.xml
<?xml version="1.0" encoding="UTF-8" ?>

<node name="/org/freesmartphone/GSM/Device">
  <interface name="org.freesmartphone.GSM.SMS">
    <method name="SendMessage">
       <arg name="number" type="s"/>
       <arg name="contents" type="s"/>
       <arg name="featuremap" type="a{sv}"/>
       <arg type="i" direction="out"/>
    </method>
    <signal name="IncomingMessage">
       <arg name="address" type="s"/>
       <arg name="contents" type="s"/>
       <arg name="features" type="a{sv}"/>
    </signal>
  </interface>
</node>

咱們要在鏈接"org.freesmartphone.ogsmd"中實現對象"/org/freesmartphone/GSM /Device"。這個對象有接口"org.freesmartphone.GSM.SMS"。這個接口有一個SendMessage方法和一個 IncomingMessage信號。

SendMessage方法和IncomingMessage信號除了兩個字符串參數外,還有一個a{sv}參數,這是一個哈希表,即python 的字典。鍵-值對的鍵類型是字符串,值類型是VARIANT。這個接口是openmoko fso接口的一部分。但爲簡單起見,本例在哈希表部分,只用三個鍵值。

  • 鍵"alphabet"對應的值類型是字符串。
  • 鍵"csm_num"對應的值類型是INT32。
  • 鍵"csm_seq"對應的值類型是INT32。

請注意方法和信號名應採用單詞連寫,首字母大寫的格式。

1.二、由接口描述文件生成綁定文件

有一個叫dbus-binding-tool的工具,它讀入接口描述文件,產生一個綁定文件。這個文件包含了dbus對象的接口信息。在主程序中我 們經過dbus_g_object_type_install_info函數向dbus-glib登記對象信息(DBusGObjectInfo結構)。

本例使用了autotool,在Makefile.am中能夠這樣調用dbus-binding-tool:

smss-glue.h: smss.xml
	$(LIBTOOL) --mode=execute dbus-binding-tool --prefix=gsm_sms --mode=glib-server --output=smss-glue.h $(srcdir)/smss.xml

"--prefix"參數定義了對象前綴。設對象前綴是$(prefix),則生成的DBusGObjectInfo結構變量名就是 dbus_glib_$(prefix)_object_info。綁定文件會爲接口方法定義回調函數。回調函數的名稱是這樣的:首先將xml中的方法名 稱轉換到所有小寫,下劃線分隔的格式,而後增長前綴"$(prefix)_"。例如:若是xml中有方法SendMessage,綁定文件就會引用一個名 稱爲$(prefix)_send_message的函數。

綁定文件還會爲接口方法生成用於散集(Unmarshaling)的函數。在dbus消息中,方法參數是以流格式存在的。該函數將方法參數由數據流 還原到glib的數據格式,並傳入方法的回調函數。本例中,dbus-binding-tool生成如下的smss-glue.h:

$ cat smss-glue.h
/* Generated by dbus-binding-tool; do not edit! */


#ifndef __dbus_glib_marshal_gsm_sms_MARSHAL_H__
#define __dbus_glib_marshal_gsm_sms_MARSHAL_H__

#include        <glib-object.h>

G_BEGIN_DECLS

#ifdef G_ENABLE_DEBUG
#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
#define g_marshal_value_peek_char(v)     g_value_get_char (v)
#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
#define g_marshal_value_peek_int(v)      g_value_get_int (v)
#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
#define g_marshal_value_peek_long(v)     g_value_get_long (v)
#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
#define g_marshal_value_peek_float(v)    g_value_get_float (v)
#define g_marshal_value_peek_double(v)   g_value_get_double (v)
#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
#define g_marshal_value_peek_param(v)    g_value_get_param (v)
#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
#define g_marshal_value_peek_object(v)   g_value_get_object (v)
#else /* !G_ENABLE_DEBUG */
/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
 *          Do not access GValues directly in your code. Instead, use the
 *          g_value_get_*() functions
 */
#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
#endif /* !G_ENABLE_DEBUG */


/* BOOLEAN:STRING,STRING,BOXED,POINTER,POINTER (/tmp/dbus-binding-tool-c-marshallers.3YAGNU:1) */
extern void dbus_glib_marshal_gsm_sms_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER (GClosure     *closure,
                                                                                    GValue       *return_value,
                                                                                    guint         n_param_values,
                                                                                    const GValue *param_values,
                                                                                    gpointer      invocation_hint,
                                                                                    gpointer      marshal_data);
void
dbus_glib_marshal_gsm_sms_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER (GClosure     *closure,
                                                                        GValue       *return_value,
                                                                        guint         n_param_values,
                                                                        const GValue *param_values,
                                                                        gpointer      invocation_hint,
                                                                        gpointer      marshal_data)
{
  typedef gboolean (*GMarshalFunc_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER) (gpointer     data1,
                                                                                 gpointer     arg_1,
                                                                                 gpointer     arg_2,
                                                                                 gpointer     arg_3,
                                                                                 gpointer     arg_4,
                                                                                 gpointer     arg_5,
                                                                                 gpointer     data2);
  register GMarshalFunc_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER callback;
  register GCClosure *cc = (GCClosure*) closure;
  register gpointer data1, data2;
  gboolean v_return;

  g_return_if_fail (return_value != NULL);
  g_return_if_fail (n_param_values == 6);

  if (G_CCLOSURE_SWAP_DATA (closure))
    {
      data1 = closure->data;
      data2 = g_value_peek_pointer (param_values + 0);
    }
  else
    {
      data1 = g_value_peek_pointer (param_values + 0);
      data2 = closure->data;
    }
  callback = (GMarshalFunc_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER) (marshal_data ? marshal_data : cc->callback);

  v_return = callback (data1,
                       g_marshal_value_peek_string (param_values + 1),
                       g_marshal_value_peek_string (param_values + 2),
                       g_marshal_value_peek_boxed (param_values + 3),
                       g_marshal_value_peek_pointer (param_values + 4),
                       g_marshal_value_peek_pointer (param_values + 5),
                       data2);

  g_value_set_boolean (return_value, v_return);
}

G_END_DECLS

#endif /* __dbus_glib_marshal_gsm_sms_MARSHAL_H__ */

#include 
static const DBusGMethodInfo dbus_glib_gsm_sms_methods[] = {
  { (GCallback) gsm_sms_send_message, dbus_glib_marshal_gsm_sms_BOOLEAN__STRING_STRING_BOXED_POINTER_POINTER, 0 },
};

const DBusGObjectInfo dbus_glib_gsm_sms_object_info = {
  0,
  dbus_glib_gsm_sms_methods,
  1,
"org.freesmartphone.GSM.SMS/0SendMessage/0S/0number/0I/0s/0contents/0I/0s/0featuremap/0I/0a{sv}/0arg3/0O/0F/0N/0i/0/0/0",
"org.freesmartphone.GSM.SMS/0IncomingMessage/0/0",
"/0"
};

在包含綁定文件前,咱們必須聲明綁定文件要引用的回調函數。

2 對象

2.1 對象定義

dbus-glib用GObject實現dbus對象。因此咱們首先要實現一個對象。在本例中,咱們實現一個GsmSms對象,它繼承了GObject:

$ cat gsm_sms.h
#ifndef GSM_SMS_H
#define GSM_SMS_H

typedef struct GsmSms GsmSms;
typedef struct GsmSmsClass GsmSmsClass;

struct GsmSms
{
  GObject parent;
};

struct GsmSmsClass
{
  GObjectClass parent;
};

#define GSM_SMS_TYPE                  (gsm_sms_get_type ())

GType gsm_sms_get_type (void);
gboolean gsm_sms_send_message (GsmSms *obj, const char *number, const char *contents, GHashTable *featuremap, int *ret, GError **error);
void gsm_sms_emit_incoming_message(GsmSms *obj, const char * address, const char * contents, GHashTable *hash);

#endif

GObject的對象定義雖然繁瑣,但有固定的套路。依樣畫葫蘆,畫多了就習慣了。咱們在gsm_sms.h中聲明瞭 gsm_sms_send_message函數。 gsm_sms_send_message函數是在gsm_sms.c中實現的,在綁定文件smss-glue.h中用到。由於主程序要使用綁定文件中的 對象信息,因此應由主程序包含綁定文件。主程序只要在包含綁定文件前包含gsm_sms.h,編譯器就不會抱怨gsm_sms_send_message 函數未聲明。

2.2 信號的列集函數

列集(Marshaling)是將數據從某種格式存爲流格式的操做;散集(Unmarshaling)則是列集的反操做,將信息從流格式中還原出 來。在綁定文件中,dbus-binding-tool自動生成函數將方法參數從dbus消息中還原出來,即實現了散集。那麼咱們怎麼把信號參數由 glib的數據結構轉換到消息中的數據流呢?

由於GsmSms對象有一個信號,因此在對象類初始化函數gsm_sms_class_init中,咱們要調用g_signal_new建立信號。 g_signal_new要求咱們提供一個列集函數。

glib有一些標準的列集函數,在gmarshal.h中定義。例如g_cclosure_marshal_VOID__STRING,這個函數適 合只有一個字符串參數的信號。若是gmarshal.h沒有提供適合的列集函數,咱們能夠用一個叫glib-genmarshal的工具自動生成列集函 數。後面咱們會看到,不管是標準列集函數仍是生成的列集函數都是既能夠用於列集也能夠用於散集,這些函數一般都被稱做列集函數。

使用glib-genmarshal前,咱們一樣要準備一個輸入文件:

$ cat sms-marshal.list
# see glib-genmarshal(1) for a detailed description of the file format,
# possible parameter types are:
#   VOID        indicates   no   return   type,  or  no  extra
#               parameters. if VOID is used as  the  parameter
#               list, no additional parameters may be present.
#   BOOLEAN     for boolean types (gboolean)
#   CHAR        for signed char types (gchar)
#   UCHAR       for unsigned char types (guchar)
#   INT         for signed integer types (gint)
#   UINT        for unsigned integer types (guint)
#   LONG        for signed long integer types (glong)
#   ULONG       for unsigned long integer types (gulong)
#   ENUM        for enumeration types (gint)
#   FLAGS       for flag enumeration types (guint)
#   FLOAT       for single-precision float types (gfloat)
#   DOUBLE      for double-precision float types (gdouble)
#   STRING      for string types (gchar*)
#   PARAM       for GParamSpec or derived types  (GParamSpec*)
#   BOXED       for boxed (anonymous but reference counted) types (GBoxed*)
#   POINTER     for anonymous pointer types (gpointer)
#   OBJECT      for GObject or derived types (GObject*)
#   NONE        deprecated alias for VOID
#   BOOL        deprecated alias for BOOLEAN
VOID:STRING,STRING,BOXED

咱們須要的函數返回類型是VOID,參數是STRING,STRING,BOXED。在Makefile.am中能夠這樣調用glib-genmarshal:

sms-marshal.h: sms-marshal.list
	$(LIBTOOL) --mode=execute glib-genmarshal --header sms-marshal.list --prefix=sms_marshal > sms-marshal.h

sms-marshal.c: sms-marshal.list
	$(LIBTOOL) --mode=execute glib-genmarshal --body sms-marshal.list --prefix=sms_marshal > sms-marshal.c

"--prefix"和函數原型決定輸出函數名。若是"--prefix=sms_marshal",函數原型 是"OID:STRING,STRING,BOXED",生成的列集函數名就必然是 sms_marshal_VOID__STRING_STRING_BOXED。

在本例中自動生成的文件內容以下:

$ cat sms-marshal.h

#ifndef __sms_marshal_MARSHAL_H__
#define __sms_marshal_MARSHAL_H__

#include        <glib-object.h>

G_BEGIN_DECLS

/* VOID:STRING,STRING,BOXED (sms-marshal.list:24) */
extern void sms_marshal_VOID__STRING_STRING_BOXED (GClosure     *closure,
                                                   GValue       *return_value,
                                                   guint         n_param_values,
                                                   const GValue *param_values,
                                                   gpointer      invocation_hint,
                                                   gpointer      marshal_data);

G_END_DECLS

#endif /* __sms_marshal_MARSHAL_H__ */

$ cat sms-marshal.c

#include        <glib-object.h>


#ifdef G_ENABLE_DEBUG
#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
#define g_marshal_value_peek_char(v)     g_value_get_char (v)
#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
#define g_marshal_value_peek_int(v)      g_value_get_int (v)
#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
#define g_marshal_value_peek_long(v)     g_value_get_long (v)
#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
#define g_marshal_value_peek_float(v)    g_value_get_float (v)
#define g_marshal_value_peek_double(v)   g_value_get_double (v)
#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
#define g_marshal_value_peek_param(v)    g_value_get_param (v)
#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
#define g_marshal_value_peek_object(v)   g_value_get_object (v)
#else /* !G_ENABLE_DEBUG */
/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
 *          Do not access GValues directly in your code. Instead, use the
 *          g_value_get_*() functions
 */
#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
#endif /* !G_ENABLE_DEBUG */


/* VOID:STRING,STRING,BOXED (sms-marshal.list:24) */
void
sms_marshal_VOID__STRING_STRING_BOXED (GClosure     *closure,
                                       GValue       *return_value,
                                       guint         n_param_values,
                                       const GValue *param_values,
                                       gpointer      invocation_hint,
                                       gpointer      marshal_data)
{
  typedef void (*GMarshalFunc_VOID__STRING_STRING_BOXED) (gpointer     data1,
                                                          gpointer     arg_1,
                                                          gpointer     arg_2,
                                                          gpointer     arg_3,
                                                          gpointer     data2);
  register GMarshalFunc_VOID__STRING_STRING_BOXED callback;
  register GCClosure *cc = (GCClosure*) closure;
  register gpointer data1, data2;

  g_return_if_fail (n_param_values == 4);

  if (G_CCLOSURE_SWAP_DATA (closure))
    {
      data1 = closure->data;
      data2 = g_value_peek_pointer (param_values + 0);
    }
  else
    {
      data1 = g_value_peek_pointer (param_values + 0);
      data2 = closure->data;
    }
  callback = (GMarshalFunc_VOID__STRING_STRING_BOXED) (marshal_data ? marshal_data : cc->callback);

  callback (data1,
            g_marshal_value_peek_string (param_values + 1),
            g_marshal_value_peek_string (param_values + 2),
            g_marshal_value_peek_boxed (param_values + 3),
            data2);
}

2.3 對象實現

準備好列集函數後,咱們來實現GsmSms。

$ cat -n gsm_sms.c
     1  #include <dbus/dbus-glib.h>
     2  #include <stdio.h>
     3  #include <stdlib.h>
     4  #include <string.h>
     5  #include "gsm_sms.h"
     6  #include "sms-marshal.h"
     7  #include "sms_features.h"
     8
     9  enum
    10  {
    11      INCOMING_MESSAGE,
    12      LAST_SIGNAL
    13  };
    14
    15  static guint signals[LAST_SIGNAL];
    16
    17  G_DEFINE_TYPE(GsmSms, gsm_sms, G_TYPE_OBJECT)
    18
    19  static void gsm_sms_init (GsmSms *obj)
    20  {
    21  }
    22
    23  static void gsm_sms_class_init (GsmSmsClass *klass)
    24  {
    25      signals[INCOMING_MESSAGE] = g_signal_new (
    26          "incoming_message",
    27          G_OBJECT_CLASS_TYPE (klass),
    28          G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
    29          0,
    30          NULL, NULL,
    31          sms_marshal_VOID__STRING_STRING_BOXED,
    32          G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING,
    33          sms_get_features_type());
    34  }
    35
    36  gboolean gsm_sms_send_message (GsmSms *obj, const char *number, const char *contents,
    37      GHashTable *featuremap, int *ret, GError **error)
    38  {
    39      printf("number=%s/n", number);
    40      printf("contents=%s/n", contents);
    41      sms_show_features(featuremap);
    42      *ret = strlen(contents);
    43      return TRUE;
    44  }
    45
    46  void gsm_sms_emit_incoming_message(GsmSms *obj, const char * address,
    47      const char * contents, GHashTable *hash)
    48  {
    49      g_signal_emit (obj, signals[INCOMING_MESSAGE], 0, address, contents, hash);
    50  }

在類初始化函數gsm_sms_class_init中,咱們調用g_signal_new建立了信號。g_signal_new函數的原型是:

guint g_signal_new (const gchar *signal_name,
    GType itype,
    GSignalFlags signal_flags,
    guint class_offset,
    GSignalAccumulator accumulator,
    gpointer accu_data,
    GSignalCMarshaller c_marshaller,
    GType return_type,
    guint n_params,
    ...);

31行提供了列集函數。32-33行是返回值類型和參數類型。其中第三個參數調用了函數sms_get_features_type(在 sms_features.h中聲明)。由於a{sv}類型的參數處理起來比較繁瑣,我專門寫了一個sms_features模塊處理,後面會介紹。

在主程序中登記對象信息時,對象信息把SendMessage方法和gsm_sms_send_message函數以及自動生成的散集函數聯繫起 來。當客戶程序調用SendMessage方法時,dbus-glib會經過對象信息表格找到回調函數和散集函數,用散集函數從method_call消 息中取出參數傳入回調函數gsm_sms_send_message。 gsm_sms_send_message調用sms_show_features函數處理a{sv}參數。 sms_show_features也在sms_features模塊定義,後面會介紹。

gsm_sms模塊提供了一個gsm_sms_emit_incoming_message函數供外部模塊調用。調用這個函數能夠發射一個信號。在真實環境中,只有外部事件發生後纔會發射信號。本例中會有測試代碼發射信號。

3 主程序

3.1 登記dbus服務器

下面就是主程序

$ cat -n smss.c
     1  #include <dbus/dbus-glib.h>
     2  #include <stdio.h>
     3  #include <stdlib.h>
     4  #include <glib/giochannel.h>
     5  #include "gsm_sms.h"
     6  #include "smss-glue.h"
     7  #include "sms_features.h"
     8
     9  #define SMSS_DEBUG
    10
    11  static void lose (const char *str, ...)
    12  {
    13      va_list args;
    14      va_start (args, str);
    15      vfprintf (stderr, str, args);
    16      fputc ('/n', stderr);
    17      va_end (args);
    18      exit (1);
    19  }
    20
    21  static void lose_gerror (const char *prefix, GError *error)
    22  {
    23      if (error) {
    24          lose ("%s: %s", prefix, error->message);
    25      }
    26      else {
    27          lose ("%s", prefix);
    28      }
    29  }
    30
    31  static void shell_help(void)
    32  {
    33      printf( "/ts/tsend signal/n"
    34          "/tq/tQuit/n"
    35          );
    36  }
    37
    38  void emit_signal(GsmSms *obj)
    39  {
    40      GHashTable *features = sms_create_features("ucs2", 3, 1);
    41      gsm_sms_emit_incoming_message(obj, "12345678901", "hello signal!", features);
    42      sms_release_features(features);
    43  }
    44
    45  #define STDIN_BUF_SIZE    1024
    46  static gboolean channel_cb(GIOChannel *source, GIOCondition condition, gpointer data)
    47  {
    48      int rc;
    49      char buf[STDIN_BUF_SIZE+1];
    50      GsmSms *obj = (GsmSms *)data;
    51
    52      if (condition != G_IO_IN) {
    53          return TRUE;
    54      }
    55
    56      /* we've received something on stdin.    */
    57      printf("# ");
    58      rc = fscanf(stdin, "%s", buf);
    59      if (rc <= 0) {
    60          printf("NULL/n");
    61          return TRUE;
    62      }
    63
    64      if (!strcmp(buf, "h")) {
    65          shell_help();
    66      } else if (!strcmp(buf, "?")) {
    67          shell_help();
    68      } else if (!strcmp(buf, "s")) {
    69          emit_signal(obj);
    70      } else if (!strcmp(buf, "q")) {
    71          exit(0);
    72      } else {
    73          printf("Unknown command `%s'/n", buf);
    74      }
    75      return TRUE;
    76  }
    77
    78  int main (int argc, char **argv)
    79  {
    80      DBusGConnection *bus;
    81      DBusGProxy *bus_proxy;
    82      GError *error = NULL;
    83      GsmSms *obj;
    84      GMainLoop *mainloop;
    85      guint request_name_result;
    86      GIOChannel *chan;
    87
    88  #ifdef SMSS_DEBUG
    89      g_slice_set_config(G_SLICE_CONFIG_ALWAYS_MALLOC, TRUE);
    90  #endif
    91      g_type_init ();
    92
    93      dbus_g_object_type_install_info (GSM_SMS_TYPE, &dbus_glib_gsm_sms_object_info);
    94
    95      mainloop = g_main_loop_new (NULL, FALSE);
    96
    97      bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
    98      if (!bus)
    99          lose_gerror ("Couldn't connect to system bus", error);
   100
   101      bus_proxy = dbus_g_proxy_new_for_name (bus, "org.freedesktop.DBus",
   102          "/", "org.freedesktop.DBus");
   103
   104      if (!dbus_g_proxy_call (bus_proxy, "RequestName", &error,
   105          G_TYPE_STRING, "org.freesmartphone.ogsmd",
   106          G_TYPE_UINT, 0,
   107          G_TYPE_INVALID,
   108          G_TYPE_UINT, &request_name_result,
   109          G_TYPE_INVALID))
   110          lose_gerror ("Failed to acquire org.freesmartphone.ogsmd", error);
   111
   112      obj = g_object_new (GSM_SMS_TYPE, NULL);
   113      dbus_g_connection_register_g_object (bus, "/org/freesmartphone/GSM/Device", G_OBJECT (obj));
   114
   115      printf ("service is running/n");
   116      chan = g_io_channel_unix_new(0);
   117      g_io_add_watch(chan, G_IO_IN, channel_cb, obj);
   118      g_main_loop_run (mainloop);
   119
   120      exit (0);
   121  }

93行調用dbus_g_object_type_install_info登記GsmSms類的接口信息。97行鏈接會話總線。 101-102行在會話總線上爲鏈接"org.freedesktop.DBus"的"/"對象的接口"org.freedesktop.DBus"創建 代理。 104-109行經過接口代理調用"RequestName"方法,請求公共名"org.freesmartphone.ogsmd"。

請求公共名成功後,112行創建GsmSms對象。113行登記GsmSms對象,登記時指定對象路徑"/org/freesmartphone/GSM/Device",並傳入對象指針。118行進入主循環等待客戶消息。

3.2 IO Channel

我想增長一個敲鍵測試信號發射。但我又必須在glib主循環裏等待dbus消息。怎樣才能既等待dbus消息,又等待敲鍵呢?這種狀況可使用 glib的IO Channels。glib的IO Channels容許咱們在glib主循環等待指定的文件或socket句柄。

要使用IO Channels,首先包含"glib/giochannel.h"。116行用句柄0(即標準輸入)建立一個GIOChannel。 117行爲咱們建立的GIOChannel登記回調函數。咱們在回調函數channel_cb中處理敲鍵,發射信號。

3.3 編譯運行

讀者能夠從這裏下載完整的示例程序。下集會介紹本例的autotool工程。目前,咱們先編譯運行一下,解壓後執行:

$ ./configure
$ make
$ cd src
$ ./smss
service is running
h
#       s       send signal
        q       Quit

鍵入h後回車,能夠看到敲鍵的幫助信息。

我想找個客戶程序測試一下,dbus-send不能發a{sv}這樣複雜的參數。咱們能夠用d-feet測試,或者寫個python腳本:

$ cat ./smsc.py 
#!/usr/bin/env python
import dbus
bus=dbus.SessionBus()
bus_obj=bus.get_object('org.freesmartphone.ogsmd', '/org/freesmartphone/GSM/Device')
iface=dbus.Interface(bus_obj, 'org.freesmartphone.GSM.SMS')
ret=iface.SendMessage('1234567890', 'hello from python',
{'alphabet':'gsm','csm_num':8,'csm_seq':2})
print "SendMessage return %d" % (ret)

執行smsc.py,在服務器端看到:

$ ./smss
service is running
h
#       s       send signal
        q       Quit
number=1234567890
contents=hello from python
csm_num=8
alphabet=gsm
csm_seq=2

說明服務器能夠正常工做。主程序的89行要求glib直接用malloc分配內存,這樣用valgrind才能檢查到內存泄漏(若是有的話)。咱們能夠這樣運行smss以檢查是否有內存泄漏:

$ valgrind --tool=memcheck --leak-check=full ./smss

四、複雜的數據類型

在dbus中怎樣處理複雜的數據類型?第一個建議是儘可能不要使用複雜的數據類型。但若是確實須要呢?有的網友建議用GArray做爲容器,無論什麼參數,在客戶端都手工放入GArray,在服務器端再本身取出來。這確實是個思路,比較適合服務器和客戶端都是本身開發的狀況。還有一篇"How to pass a variant with dbus-glib" 介紹了怎樣用GValue傳遞複雜的數據類型,讀者能夠參考。

下面看看在咱們的例子中是怎樣處理a{sv}參數的:

$ cat sms_features.h
#ifndef SMS_FEATURES_H
#define SMS_FEATURES_H

#include <glib-object.h>

GHashTable *sms_create_features(const char * alphabet, int csm_num, int csm_seq);

GType sms_get_features_type(void);

void sms_release_features(GHashTable *features);

void sms_show_features(GHashTable *features);

#endif

sms_features.h聲明瞭幾個函數。這個例子的服務器、客戶端都會調用。如下是這些函數的實現:

$ cat -n sms_features.c
     1  #include "sms_features.h"
     2
     3  static void release_val(gpointer data)
     4  {
     5      GValue *val = (GValue *)data;
     6      g_value_unset(val);
     7      g_free(val);
     8  }
     9
    10  GHashTable *sms_create_features(const char * alphabet, int csm_num, int csm_seq)
    11  {
    12      GHashTable *hash;
    13      GValue *val;
    14
    15      hash = g_hash_table_new_full  (g_str_hash, NULL, NULL, release_val);
    16
    17      val = g_new0(GValue, 1);
    18      g_value_init (val, G_TYPE_STRING);
    19      g_value_set_string (val, alphabet);
    20      g_hash_table_insert(hash, "alphabet", val);
    21
    22      val = g_new0(GValue, 1);
    23      g_value_init (val, G_TYPE_INT);
    24      g_value_set_int (val, csm_num);
    25      g_hash_table_insert(hash, "csm_num", val);
    26
    27      val = g_new0(GValue, 1);
    28      g_value_init (val, G_TYPE_INT);
    29      g_value_set_int (val, csm_seq);
    30      g_hash_table_insert(hash, "csm_seq", val);
    31
    32      return hash;
    33  }
    34
    35  GType sms_get_features_type(void)
    36  {
    37      return dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE);
    38  }
    39
    40  void sms_show_features(GHashTable *features)
    41  {
    42      GList *keys = g_hash_table_get_keys(features);
    43      gint len = g_list_length(keys);
    44      gint i;
    45
    46      for (i = 0; i < len; i++) {
    47          gchar  *key = g_list_nth_data(keys, i);
    48          GValue *val = g_hash_table_lookup(features, key);
    49
    50          g_print("%s=", key);
    51          switch (G_VALUE_TYPE(val)) {
    52          case G_TYPE_STRING:
    53              g_print("%s/n", g_value_get_string(val));
    54              break;
    55          case G_TYPE_INT:
    56              g_print("%d/n", g_value_get_int(val));
    57              break;
    58          default:
    59              g_print("Value is of unmanaged type!/n");
    60          }
    61      }
    62
    63      g_list_free(keys);
    64  }
    65
    66  void sms_release_features(GHashTable *features)
    67  {
    68      g_hash_table_destroy(features);
    69  }
    70

sms_get_features_type調用dbus_g_type_get_map建立a{sv}類型。服務器在建立信號時用到。客戶端在調用方法和註冊信號時都會用到。 sms_create_features調用g_hash_table_new_full建立哈希表,在建立的同時登記了值對象的清理函數。在sms_release_features調用g_hash_table_destroy銷燬哈希表時,建立時登記的值對象清理函數會被調用。

五、客戶端

5.一、代碼

客戶端程序以下:

$ cat -n smsc.c
     1  #include <dbus/dbus-glib.h>
     2  #include <stdio.h>
     3  #include <stdlib.h>
     4  #include <string.h>
     5  #include <glib/giochannel.h>
     6  #include "sms-marshal.h"
     7  #include "sms_features.h"
     8
     9  #define SMSC_DEBUG
    10
    11  static void lose (const char *str, ...)
    12  {
    13      va_list args;
    14      va_start (args, str);
    15      vfprintf (stderr, str, args);
    16      fputc ('/n', stderr);
    17      va_end (args);
    18      exit (1);
    19  }
    20
    21  static void lose_gerror (const char *prefix, GError *error)
    22  {
    23      if (error) {
    24          lose ("%s: %s", prefix, error->message);
    25      }
    26      else {
    27          lose ("%s", prefix);
    28      }
    29  }
    30
    31  static void incoming_message_handler (DBusGProxy *proxy, const char *address, const char *contents, GHashTable *features, gpointer user_data)
    32  {
    33      printf ("Received message with addree /"%s/" and it says: /n%s/n", address, contents);
    34      sms_show_features(features);
    35  }
    36
    37  static void send_message(DBusGProxy *remote_object)
    38  {
    39      GError *error = NULL;
    40      GHashTable *features;
    41      int ret;
    42
    43      features = sms_create_features ("gsm", 8, 2);
    44      printf("SendMessage ");
    45
    46      if (!dbus_g_proxy_call (remote_object, "SendMessage", &error,
    47          G_TYPE_STRING, "10987654321", G_TYPE_STRING, "hello world",
    48          sms_get_features_type(), features, G_TYPE_INVALID,
    49          G_TYPE_INT, &ret, G_TYPE_INVALID))
    50          lose_gerror ("Failed to complete SendMessage", error);
    51
    52      printf("return %d/n", ret);
    53      sms_release_features(features);
    54  }
    55
    56  static void shell_help(void)
    57  {
    58      printf( "/ts/tsend message/n"
    59          "/tq/tQuit/n"
    60          );
    61  }
    62
    63  #define STDIN_BUF_SIZE    1024
    64  static gboolean channel_cb(GIOChannel *source, GIOCondition condition, gpointer data)
    65  {
    66      int rc;
    67      char buf[STDIN_BUF_SIZE+1];
    68      DBusGProxy *remote_object = (DBusGProxy *)data;
    69
    70      if (condition != G_IO_IN) {
    71          return TRUE;
    72      }
    73
    74      /* we've received something on stdin.    */
    75      printf("# ");
    76      rc = fscanf(stdin, "%s", buf);
    77      if (rc <= 0) {
    78          printf("NULL/n");
    79          return TRUE;
    80      }
    81
    82      if (!strcmp(buf, "h")) {
    83          shell_help();
    84      } else if (!strcmp(buf, "?")) {
    85          shell_help();
    86      } else if (!strcmp(buf, "s")) {
    87          send_message(remote_object);
    88      } else if (!strcmp(buf, "q")) {
    89          exit(0);
    90      } else {
    91          printf("Unknown command `%s'/n", buf);
    92      }
    93      return TRUE;
    94  }
    95
    96  int main (int argc, char **argv)
    97  {
    98      DBusGConnection *bus;
    99      DBusGProxy *remote_object;
   100      GError *error = NULL;
   101      GMainLoop *mainloop;
   102      GIOChannel *chan;
   103      guint source;
   104      GType features_type;
   105
   106  #ifdef SMSC_DEBUG
   107      g_slice_set_config(G_SLICE_CONFIG_ALWAYS_MALLOC, TRUE);
   108  #endif
   109      g_type_init ();
   110      mainloop = g_main_loop_new (NULL, FALSE);
   111
   112      bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
   113      if (!bus)
   114          lose_gerror ("Couldn't connect to session bus", error);
   115
   116      remote_object = dbus_g_proxy_new_for_name (bus, "org.freesmartphone.ogsmd",
   117          "/org/freesmartphone/GSM/Device",
   118          "org.freesmartphone.GSM.SMS");
   119      if (!remote_object)
   120          lose_gerror ("Failed to get name owner", NULL);
   121
   122      features_type = sms_get_features_type();
   123      dbus_g_object_register_marshaller (sms_marshal_VOID__STRING_STRING_BOXED, G_TYPE_NONE, G_TYPE_STRING, G_TYPE_STRING,
   124          features_type, G_TYPE_INVALID);
   125      dbus_g_proxy_add_signal (remote_object, "IncomingMessage", G_TYPE_STRING, G_TYPE_STRING, features_type, G_TYPE_INVALID);
   126      dbus_g_proxy_connect_signal (remote_object, "IncomingMessage", G_CALLBACK (incoming_message_handler), NULL, NULL);
   127
   128      chan = g_io_channel_unix_new(0);
   129      source = g_io_add_watch(chan, G_IO_IN, channel_cb, remote_object);
   130      g_main_loop_run (mainloop);
   131      exit (0);
   132  }

112行鏈接會話總線。116-118行在會話總線上獲取鏈接"org.freesmartphone.ogsmd"的對象"/org/freesmartphone/GSM/Device" 的接口"org.freesmartphone.GSM.SMS"的接口代理對象。

123行調用dbus_g_object_register_marshaller向dbus-glib登記列集函數。 125行調用dbus_g_proxy_add_signal增長對信號IncomingMessage的監聽。126行登記信號IncomingMessage的回調函數。 123行登記的仍是咱們用glib-genmarshal生成的函數sms_marshal_VOID__STRING_STRING_BOXED。 dbus-glib使用這個函數從signal消息中取出信號參數,傳遞給回調函數,即執行散集操做。這說明glib-genmarshal生成的列集函數既能夠用於列集,也能夠用於散集。

客戶端程序一樣用IO Channel接受用戶輸入。129行在登記回調函數時將指向接口代理對象的指針做爲參數傳入。回調函數channel_cb在用戶鍵入's'命令後經過send_message函數調用org.freesmartphone.GSM.SMS接口對象的SendMessage方法。 107行的設置G_SLICE_CONFIG_ALWAYS_MALLOC一樣是爲了用valgrind檢查內存泄漏。

5.二、執行

咱們先運行 dbus-monitor,而後運行smss,再運行smsc。先在smsc中鍵入's'回車調用SendMessage方法。而後在smss中鍵入's'回車發送IncomingMessage信號。而後在smsc中鍵入'q'回車退出。最後在smss中鍵入'q'回車退出。

$ ./smss
service is running
number=10987654321
contents=hello world
csm_num=8
alphabet=gsm
csm_seq=2
h
#       s       send signal
        q       Quit
s
# q
$ ./smsc
h
#       s       send message
        q       Quit
s
# SendMessage return 11
Received message with addree "12345678901" and it says: 
hello signal!
csm_num=3
alphabet=ucs2
csm_seq=1
q

咱們能夠看到打印出來的信號和消息。對於同一件事情,不一樣的層次的觀察者會看到不一樣的細節,下表是dbus-monitor看到的東西:

 

smss鏈接會話總線。會話總線發NameOwnerChanged信號,通知惟一名":1.21"被分配。 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.21"
   string ""
   string ":1.21"
smss向會話總線發送Hello取得本身的惟一名":1.21"。 method call sender=:1.21 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=Hello
smss調用AddMatch要求接收會話總線的NameOwnerChanged信號。 method call sender=:1.21 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='signal',sender='org.freedesktop.DBus',path='/org/freedesktop/DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
smss調用AddMatch要求接收會話總線發送的全部信號。 method call sender=:1.21 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='signal',sender='org.freedesktop.DBus',path='/',interface='org.freedesktop.DBus'"
smss調用GetNameOwner獲取鏈接"org.freedesktop.DBus"的惟一名。 method call sender=:1.21 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=GetNameOwner
   string "org.freedesktop.DBus"
會話總線發送NameOwnerChanged信號,通知惟一名爲":1.21"的鏈接得到了公衆名"org.freesmartphone.ogsmd"。 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string "org.freesmartphone.ogsmd"
   string ""
   string ":1.21"
smss請求公衆名"org.freesmartphone.ogsmd"。分配公衆名在前,請求公衆名在後,應該是監控過程顛倒了消息次序。 method call sender=:1.21 -> dest=org.freedesktop.DBus path=/; interface=org.freedesktop.DBus; member=RequestName
   string "org.freesmartphone.ogsmd"
   uint32 0
smsc鏈接會話總線。會話總線發NameOwnerChanged信號,通知惟一名":1.22"被分配。 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.22"
   string ""
   string ":1.22"
smss向會話總線發送Hello取得本身的惟一名":1.22"。 method call sender=:1.22 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=Hello
smsc調用AddMatch要求接收會話總線的NameOwnerChanged信號。 method call sender=:1.22 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='signal',sender='org.freedesktop.DBus',path='/org/freedesktop/DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
smsc調用AddMatch要求接收鏈接'org.freesmartphone.ogsmd'中對象'/org/freesmartphone/GSM/Device'的'org.freesmartphone.GSM.SMS'接口的信號。 method call sender=:1.22 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=AddMatch
   string "type='signal',sender='org.freesmartphone.ogsmd',path='/org/freesmartphone/GSM/Device',interface='org.freesmartphone.GSM.SMS'"
smsc調用GetNameOwner獲取鏈接"org.freesmartphone.ogsmd"的惟一名。 method call sender=:1.22 -> dest=org.freedesktop.DBus path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=GetNameOwner
   string "org.freesmartphone.ogsmd"
smsc調用鏈接'org.freesmartphone.ogsmd'中對象'/org/freesmartphone/GSM/Device'的'org.freesmartphone.GSM.SMS'接口的SendMessage方法。 method call sender=:1.22 -> dest=org.freesmartphone.ogsmd path=/org/freesmartphone/GSM/Device; interface=org.freesmartphone.GSM.SMS; member=SendMessage
   string "10987654321"
   string "hello world"
   array [
      dict entry(
         string "csm_seq"
         variant int32 2
      )
      dict entry(
         string "alphabet"
         variant string "gsm"
      )
      dict entry(
         string "csm_num"
         variant int32 8
      )
   ]
smss向smsc發送method return消息,返回SendMessage方法的輸出參數。 method return sender=:1.21 -> dest=:1.22 reply_serial=5
   int32 11
smss發送IncomingMessage信號。 signal sender=:1.21 -> dest=(null destination) path=/org/freesmartphone/GSM/Device; interface=org.freesmartphone.GSM.SMS; member=IncomingMessage
   string "12345678901"
   string "hello signal!"
   array [
      dict entry(
         string "csm_seq"
         variant int32 1
      )
      dict entry(
         string "alphabet"
         variant string "ucs2"
      )
      dict entry(
         string "csm_num"
         variant int32 3
      )
   ]
會話總線通知鏈接":1.22",即smsc的鏈接已經切斷。 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.22"
   string ":1.22"
   string ""
會話總線通知擁有公共名"org.freesmartphone.ogsmd"的鏈接已經切斷。 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string "org.freesmartphone.ogsmd"
   string ":1.21"
   string ""
會話總線通知擁有惟一名":1.21"的鏈接已經切斷。即smss已經終止。 signal sender=org.freedesktop.DBus -> dest=(null destination) path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameOwnerChanged
   string ":1.21"
   string ":1.21"
   string ""

 

六、工程

我提供下載的文件要用make distcheck製做的,其中包含了一些自動生成的文件。執行./clean.sh能夠刪掉自動生成的文件,只留下我建立的文件:

$ find . -type f
./clean.sh
./Makefile.am
./autogen.sh
./src/gsm_sms.h
./src/Makefile.am
./src/sms-marshal.list
./src/smss.xml
./src/smss.c
./src/gsm_sms.c
./src/sms_features.h
./src/sms_features.c
./src/smsc.c
./configure.ac

前面已經介紹過全部的源文件。咱們再看看工程文件:

$ cat autogen.sh
#! /bin/sh
touch `find .`
aclocal
autoconf
autoheader
touch NEWS README AUTHORS ChangeLog
automake --add-missing

$ cat Makefile.am
SUBDIRS = src
EXTRA_DIST = autogen.sh clean.sh

autogen.sh創建工程環境。在執行clean.sh後,執行autogen.sh從新生成configure等工程文件。其中的touch命令是爲了防止文件有未來的時間戳。由於我在虛擬機中運行ubuntu,因此可能會出現這類問題。 Makefile.am將autogen.sh clean.sh也做爲發佈文件。最重要的工程文件是"configure.ac"和"src/Makefile.am"。

6.一、configure.ac

$ cat -n configure.ac
     1  AC_INIT()
     2  AM_INIT_AUTOMAKE(hello-dbus5, 0.1)
     3  AM_CONFIG_HEADER(config.h)
     4
     5  AC_PROG_CC
     6
     7
     8  # Dbus detection
     9  PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.1, have_dbus=yes, have_dbus=no)
    10
    11  if test x$have_dbus = xno ; then
    12      AC_MSG_ERROR([DBus development libraries not found])
    13  fi
    14  AM_CONDITIONAL(HAVE_DBUS, test x$have_dbus = xyes)
    15
    16  AC_SUBST(DBUS_CFLAGS)
    17  AC_SUBST(DBUS_LIBS)
    18
    19
    20  # Glib detection
    21  PKG_CHECK_MODULES(DBUS_GLIB, gobject-2.0 >= 2.6, have_glib=yes, have_glib=no)
    22
    23  if test x$have_glib = xno ; then
    24      AC_MSG_ERROR([GLib development libraries not found])
    25  fi
    26
    27  AM_CONDITIONAL(HAVE_GLIB, test x$have_glib = xyes)
    28
    29  AC_SUBST(DBUS_GLIB_CFLAGS)
    30  AC_SUBST(DBUS_GLIB_LIBS)
    31
    32
    33  AC_OUTPUT([Makefile
    34             src/Makefile])

8-17行檢查dbus庫,它們會生成編譯常數DBUS_CFLAGS和DBUS_LIBS。 20-30行檢查dbus-glib庫,它們會生成編譯常數DBUS_GLIB_CFLAGS和DBUS_GLIB_LIBS。

6.二、src/Makefile.am

$ cat -n src/Makefile.am
     1  INCLUDES = /
     2          $(DBUS_CFLAGS)                          /
     3          $(DBUS_GLIB_CFLAGS)                     /
     4          -DDBUS_COMPILATION
     5
     6  LIBS = /
     7          $(DBUS_LIBS)                            /
     8          $(DBUS_GLIB_LIBS)                       /
     9          -ldbus-glib-1
    10
    11  # smss
    12  noinst_PROGRAMS = smss
    13
    14  BUILT_SOURCES = smss-glue.h sms-marshal.h sms-marshal.c
    15  smss_SOURCES = $(BUILT_SOURCES) smss.c gsm_sms.c sms_features.c
    16  noinst_HEADERS = gsm_sms.h sms_features.h
    17
    18
    19  smss-glue.h: smss.xml
    20          $(LIBTOOL) --mode=execute dbus-binding-tool --prefix=gsm_sms --mode=glib-server --output=smss-glue.h $(srcdir)/smss.xml
    21
    22  sms-marshal.h: sms-marshal.list
    23          $(LIBTOOL) --mode=execute glib-genmarshal --header sms-marshal.list --prefix=sms_marshal > sms-marshal.h
    24
    25  sms-marshal.c: sms-marshal.list
    26          $(LIBTOOL) --mode=execute glib-genmarshal --body sms-marshal.list --prefix=sms_marshal > sms-marshal.c
    27
    28  CLEANFILES = $(BUILT_SOURCES)
    29
    30  EXTRA_DIST = smss.xml sms-marshal.list
    31
    32  # smss
    33  noinst_PROGRAMS += smsc
    34  smsc_SOURCES= smsc.c sms-marshal.c sms_features.c

19-20行由接口描述文件smss.xml生成存根文件smss-glue.h。22-26行由列集接口定義生成包含列集函數的代碼。

七、結束語

本文介紹了一個簡單的dbus-glib的例子,包括服務器和客戶端。第一講中還有一個加法例子,若是你理解了本文的例子,那個例子就更簡單了。 dbus-glib源代碼中有兩個例子:

  • example-service和example-client演示方法調用。這個例子的接口描述文件中有個參數類型寫錯了,將(us)寫成(ss),運行時會出錯。可能做者想演示一下接口定義與代碼實現不一致的後果吧。讀者能夠從這裏下載我修改過的代碼。
  • example-signal-emitter和example-signal-recipient演示信號發射。這個例子中,example-signal-recipient調用example-signal-emitter的方法請求發送信號。實際上信號應該是來自服務器側的信息。我將其改爲在example-signal-emitter中敲鍵發送信號。讀者能夠從這裏下載我修改過的代碼。

好了,《dbus實例講解》到此結束。其實個人全部文章只是但願能讓這複雜的世界簡單一點。

相關文章
相關標籤/搜索