本文轉自Roboter's blog
對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中的訊息由一個訊息頭(標識是哪一種訊息)和訊息資料群組成,比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底層介面,需要檢查寄件者名稱和訊號名稱,然後決定怎麼做。
Glib綁定介面在"dbus/dbus-glib.h"
標頭檔中定義。
dbus和glib的資料類型映射如下:
| D-Bus basic type |
GType |
Free function |
Notes |
BYTE |
G_TYPE_UCHAR |
|
|
BOOLEAN |
G_TYPE_BOOLEAN |
|
|
INT16 |
G_TYPE_INT |
|
Will be changed to a G_TYPE_INT16 once GLib has it |
UINT16 |
G_TYPE_UINT |
|
Will be changed to a G_TYPE_UINT16 once GLib has it |
INT32 |
G_TYPE_INT |
|
Will be changed to a G_TYPE_INT32 once GLib has it |
UINT32 |
G_TYPE_UINT |
|
Will be changed to a G_TYPE_UINT32 once GLib has it |
INT64 |
G_TYPE_GINT64 |
|
|
UINT64 |
G_TYPE_GUINT64 |
|
|
DOUBLE |
G_TYPE_DOUBLE |
|
|
STRING |
G_TYPE_STRING |
g_free |
|
OBJECT_PATH |
DBUS_TYPE_G_PROXY |
g_object_unref |
The returned proxy does not have an interface set; use
dbus_g_proxy_set_interface to invoke methods |
Container type mappings
dbus資料也有包容器類型,像DBUS_TYPE_ARRAY 和 DBUS_TYPE_STRUCT,dbus的資料類型可以是嵌套的,如有一個數組,內容是字串的數組集合。
但是,並不是所有的類型都有普通的使用,DBUS_TYPE_STRUCT應該可以包容非基本類型的資料類型。glib綁定嘗試使用比較明顯的方式進行聲明。
| D-Bus type signature |
Description |
GType |
C typedef |
Free function |
Notes |
as |
Array of strings |
G_TYPE_STRV |
char ** |
g_strfreev |
|
v |
Generic value container |
G_TYPE_VALUE |
GValue * |
g_value_unset |
The calling conventions for values expect that method callers have allocated return values; see below. |
同時定義了新的數群組類型集合。
| D-Bus type signature |
Description |
GType |
C typedef |
Free function |
Notes |
ay |
Array of bytes |
DBUS_TYPE_G_BYTE_ARRAY |
GArray * |
g_array_free |
|
au |
Array of uint |
DBUS_TYPE_G_UINT_ARRAY |
GArray * |
g_array_free |
|
ai |
Array of int |
DBUS_TYPE_G_INT_ARRAY |
GArray * |
g_array_free |
|
ax |
Array of int64 |
DBUS_TYPE_G_INT64_ARRAY |
GArray * |
g_array_free |
|
at |
Array of uint64 |
DBUS_TYPE_G_UINT64_ARRAY |
GArray * |
g_array_free |
|
ad |
Array of double |
DBUS_TYPE_G_DOUBLE_ARRAY |
GArray * |
g_array_free |
|
ab |
Array of boolean |
DBUS_TYPE_G_BOOLEAN_ARRAY |
GArray * |
g_array_free |
|
定義了字典類型
| D-Bus type signature |
Description |
GType |
C typedef |
Free function |
Notes |
a{ss} |
Dictionary mapping strings to strings |
DBUS_TYPE_G_STRING_STRING_HASHTABLE |
GHashTable * |
g_hash_table_destroy |
|
This
entry was posted on Sunday, August 5th, 2007 at 9:57 pm and is filed
under Uncategorized. You can follow any responses to this entry through
the RSS 2.0
feed.
You can leave a response
, or trackback
from your own site.
client端編寫
我們的程式在使用dbus的時候,首先需要串連上dbus,使用dbus_g_bus_get獲得dbus串連。然後可以建立代理對象。
需要調用方法的時候,可以有兩種方式:1.同步調用,使用dbus_g_proxy_call發送方法請求到遠端對象,dbus會阻塞等待遠端對象
的回應,輸出參數裡將會帶有相應的回應資料,以G_TYPE_INVALID作為終止符。2.非同步呼叫,使用
dbus_g_proxy_begin_call,它將返回一個DBusGPendingCall對象,可以使用
dbus_g_pending_call_set_notify串連到自己的處理函授中。
可以使用dbus_g_proxy_add_signal 和
dbus_g_proxy_connect_signal來串連訊號,dbus_g_proxy_add_signal用來聲明訊號處理函數,屬於必須被
調用的介面,dbus_g_proxy_connect_signal可以調用多次。
Generated Bindings
使用內建的xml檔案,可以很方便地自動建立出便於使用的dbus代理對象。如下的一個xml檔案描述了了一個方法:
<?xml version="1.0" encoding="UTF-8" ?>
<node name="/com/example/MyObject">
<interface name="com.example.MyObject">
<method name="ManyArgs">
<arg type="u" name="x" direction="in" />
<arg type="s" name="str" direction="in" />
<arg type="d" name="trouble" direction="in" />
<arg type="d" name="d_ret" direction="out" />
<arg type="s" name="str_ret" direction="out" />
</method >
</interface >
</node >
“in”標識輸入參數,“out”標識輸出參數。
使用dbus-binding-tool工具來產生標頭檔,如dbus-binding-tool –mode=glib-client
my-object.xml > my-object-bindings.h,會產生如下的內嵌函式原型:
/* This is a blocking call */
gboolean
com_example_MyObject_many_args (DBusGProxy *proxy, const guint IN_x,
const char * IN_str, const gdouble IN_trouble,
gdouble* OUT_d_ret, char ** OUT_str_ret,
GError **error);
/* This is a non-blocking call */
DBusGProxyCall*
com_example_MyObject_many_args_async (DBusGProxy *proxy, const guint IN_x,
const char * IN_str, const gdouble IN_trouble,
com_example_MyObject_many_args_reply callback,
gpointer userdata);
/* This is the typedef for the non-blocking callback */
typedef void
(*com_example_MyObject_many_args_reply)
(DBusGProxy *proxy, gdouble OUT_d_ret, char * OUT_str_ret,
GError *error, gpointer userdata);
所有函數的第一個參數都是DBusGProxy對象,一般是使用dbus_g_proxy_new_*函數建立出來的。用戶端發送方法請求可以增加
標記,目前只有org.freedesktop.DBus.GLib.NoReply標記,dbus可以不要回應訊息,沒有“out”參數,這樣運算速度
會快一點。
This
entry was posted on Sunday, August 5th, 2007 at 11:07 pm and is filed
under Uncategorized. You can follow any responses to this entry through
the RSS 2.0
feed.
You can leave a response
, or trackback
from your own site.