D-Bus Tutorial

D-Bus Tutorial

What is D-Bus?

D-Bus是一個用於進程間通訊的系統。從架構上來講,分爲三部分:java

  • 一個libdbus庫,容許兩個app鏈接,交換信息
  • 一個基於libdbus的daemon,各類app能夠鏈接到daemon。Daemon將信息從一個app路由到其餘apps。
  • 基於app framework的封裝庫或者綁定。好比,libdbus-glib和libdebus-qt。也有針對語言的綁定,好比Python。大多開發者直接使用的是這些封裝庫,這些庫聲明瞭Dbus相關的編程細節。libdbus做爲底層後臺,用來支持上層的bindings。libdbus的不少API只對binding的實現有效。

libdbus只支持one-to-one類型的鏈接,如同原始套接字那樣,區別是,發送的不是字節流(byte streams)而是消息。消息包含消息header和body,header標明信息類型,body包含消息主體內容。 Libdbus也能夠容許實現特定的傳輸通道,從而來完成好比像認證之類的應用細節。python

消息總線守護進程將D-bus上鍊接的全部程序構成一個輪形hub。輪子上的每個輪輻都是基於libdbus的一對一鏈接,鏈接了一個app。app經過輪輻向daemon發送消息,daemon在合適的時機將消息轉發到其餘已鏈接的app。daemon能夠理解爲一個路由器。編程

總線守護進程在系統中有多個實例。好比全局惟一的實例,一個如同sendmail或者Apache的系統daemon,對於接收什麼樣消息用來進程間通訊,有着很嚴格的安全要求(system bus?)。另一些daemon實例由用戶登陸時創建,用於該用戶的session中的app之間通訊。api

系統級的daemon和per-user daemon是分離的。session內的IPC不會影響系統級的message bus,反之亦然。安全

D-Bus applications

有許多技術用來實現IPC或者網絡信息傳遞,好比:CORBA, DCE, DCOM, DCOP, XML-RPC, SOAP, MBUS, Internet Communications Engine (ICE)。每一種都是針對特定種類的應用程序量身定作的。
Dbus爲兩種狀況而生:網絡

  • 同一desktop session中的app之間信息傳遞,這樣就將session視爲一個總體,解決進程生命週期的問題。
  • desktop session與操做系統之間的信息傳遞,即session和kernel或者其餘系統daemon之間的通訊。

Concepts

圖片描述

Native Objects and Object Paths

開發者在本身的編程架構中會定義object,一般基於一個基礎的類,好比 java.lang.Object, GObject, QObject, python's base Object, or whatever. 咱們稱爲native object。session

dbus協議和libdbus中的api不關心native object如何定義,而是提供了object path的概念。有了object path,上層的bindings就可以命名native object實例,而且容許遠程的app引用這些object。
object path如同文件系統中的文件路徑。好比,一個object能夠被命名爲/org/kde/kspread/sheets/3/cells/4/5。易於理解的路徑很棒,若有須要的話,是你也能夠命名爲諸如 /com/mycompany/c5yo817y0c1y1c5b。
名稱中指定命名空間是明智的作法,好比將路徑開頭設置爲你開發的domain名稱。這樣能夠保證同一進程中的不一樣模塊互不干擾。架構

Methods and Signals

每一個object都有成員(members),成員分爲方法(methods)和信號(signals)。方法(methods)是object能夠調用的一組操做,帶有輸入或者輸出參數。信號被廣播到任何一個對該信號感興趣的對象;信號也能夠承載數據。app

引用方法或者信號都是經過它們的名字,好比Frobate或者OnClickeddom

Interfaces

一個object只是一個或多個interface。一個interface中包含了一組方法和信號(methods and signals),概念如同 GLib or Qt or Java中的方法和信號。接口定義了object實例的類型。
dbus用簡單的命名空間字符串標識一個interface,好比org.freedesktop.Introspectable;大多數綁定會將這些interface直接映射到對應的編程語言結構,好比Java接口或者C++的純虛類。

Proxies

代理(proxy)object是native object,用於表明其餘進程中的遠程object。底層的DBus API建立一個Methods call,發送,而後接收,處理這些回覆的消息。能夠把代理看作是一個普通的native object,可是當你調用代理中的方法,綁定(bindings)將該操做轉化爲
dbus Method call消息,等待遠端回覆消息,將返回值解包,而後返回到native 方法......

如下是一個僞代碼的例子,不使用proxy:

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 proxy = new Proxy(getBusConnection(), "/remote/object/path");
  Object returnValue = proxy.MethodName(arg1, arg2);

Bus Names

每當app鏈接dbus daemon時,daemon爲app分配一個惟一的鏈接名字,以冒號開頭。一個bus name永遠指向同一個app,不會被複用。例如:34-907。冒號後面的數字除了它們的惟一性沒有意義。
app也能夠請求便於理解的名字。好比,com.mycompany.TextEditor。對應的object的路徑能夠爲/com/mycompany/TextFileManager,interface爲org.freedesktop.FileHandler

能夠將:34-907(unique name)看成是IP地址,com.mycompany.TextEditor看成是域名。com.mycompany.TextEditor映射 :34-907就如溝通mycompany.com映射到192.168.0.5。

除了路由消息,bus name還可用來追蹤app生命週期。當一個app退出或者crash時,操做系統內核(operating system kernel)會斷開message bus的鏈接,而後message bus發送消息通知
其餘app該name已經失去owner了。經過追蹤這些notification,能夠檢測其餘app的生命週期。

Bus name名字還能夠檢測應用是否已經啓動,這能夠用來實現單實例啓動程序。

Addresses

應用做爲dbus的server或者client,server監聽來自client的鏈接,client鏈接到server。一旦鏈接創建,它就是一個對稱的消息流。
Dbus address指明server去哪監聽,client去哪裏鏈接。例如,地址unix:path=/tmp/abcdef指明瞭server將監聽一個Unix socket,路徑是/tmp/abcdef, client將鏈接到這個socket。
address也能指定TCP/IP sockets,或者其餘類型的傳輸方式。

Big Conceptual Picture

把上面的概念組合起來獲得如下流程:

Address -> [Bus Name] -> Path -> Interface -> Method

Message

Dbus工做原理是在兩個進程之間發送消息,若是工做在至關上層的binging中,是不會直接處理消息的。
四種類型消息:

  • Method call消息,用來調用一個object的一個方法
  • message return消息,返回一個Method的處理結果
  • 錯誤消息,返回method調用中的exception
  • 信號消息,通知一個指定信號觸發了,能夠理解成事件觸發
  • method call消息對應一個message return消息或者一個錯誤消息

每一個消息有一個消息頭,包含field和body和桉樹。能夠將header看成消息的路由信息,body看成消息體。header可能包含如下信息:發送方bus name,目的地的busname,調用的方法或者信號,等等。
header中有一個描述body中值的類型的field。好比,字母i岱廟32位整數,ii標識body有兩個32位整數。

Calling a Method

一個方法調用(method call)由兩條消息組成,一個方法調用消息從進程A發送給進程B,對應的方法返回消息從進程B發送給進程A,發送和返回消息都要通過bus daemon路由。
每一個call消息包含一串獨有的數字,reply消息也包含這個數字以便和發送方匹配。

發送消息帶有參數,會傳遞給遠程method,回覆消息可能包含一個錯誤或者遠程method返回的數據。

Dbus中的方法調用流程以下:

  • 語言相關的綁定可能提供一個代理(proxy),調用一個本進程object內的方法,其實是調用遠程進程的object上的一個方法。這種狀況下,代理上會組織一個方法調用消息發送到遠程進程。
  • 對於底層的API,應用能夠本身組織一個方法調用消息,不使用proxy。
  • 方法調用消息會包含如下信息:遠程進程的bus
    name;方法名稱;方法須要的參數;遠程進程的object路徑;包含method的接口(interface)(可選)
  • 方法調用消息被髮送到bus daemon
  • bus daemon根據目的地bus name找到遠程進程,將消息發往目標進程,若找不到,則返回錯誤
  • 接收方進程對消息進行解包。簡單的使用底層API的狀況下,可能當即調用方法並返回結果到bus
    daemon。當時用上層綁定時,binding可能檢查object路徑,接口,方法,而後將method call消息轉換爲對本地object(GObject, java.lang.Object, QObject,etc.)的調用,而後將本地方法的返回值轉換爲方法的返回消息。
  • Bus daemon收到方法的返回消息而後發送給method call的發送方
  • 發送方收到reply消息,解析數據,也可能返回錯誤。當使用綁定時,返回消息會被轉換成代理方法(proxy
    method)的返回值或者exception。

Bus daemon不會爲消息排序,也就是說,若是發送兩條method call消息到同一接收方,接收的順序與發送順序相同。接收方不必定按照接收的順序來回復消息。好比,接收方可能在兩個不一樣的線程中處理method call,哪一個消息先處理完就先返回。

Emitting a Signal

信號由一條消息組成,從一個進程發送到另外一個或多個進程。也就是說,信號是單向的廣播。信號可能包含參數(數據部分),由於它是一個廣播,沒有返回值。

信號的發送者不知道接受者是誰。接收者爲了接收信號須要向bus daemon進行註冊,基於「match rules」,這些規則包含了發送者和接受者的名字。Bus daemon只將信號發送給對該信號感興趣的接收者。

信號的流程大體以下:

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

Introspection

Dbus objects支持org.freedesktop.DBus.Introspectable,dbus的標準接口,無需參數,返回一個XML字符串,描述了接口,方法,信號。

相關文章
相關標籤/搜索