DBUS 的學習 概念清晰


dbus里面 namepath 怎么確定的,xml的不准確

后來發現這個應該是在寫debus server的時候自己制定的,xml只是理論上應該和這個保持一致 

 

D-Bus三層架構

D-Bus是一個為應用程序間通信的消息總線系統, 用於進程之間的通信。它是個3層架構的IPC 系統,包括:

1、函數庫libdbus ,用於兩個應用程序互相聯系和交互消息。

2、一個基於libdbus構造的消息總線守護進程,可同時與多個應用程序相連,並能把來自一個應用程序的消息路由到0或者多個其他程序。

3、基於特定應用程序框架的封裝庫或捆綁(wrapper libraries or bindings )。例如,libdbus-glib和libdbus-qt,還有綁定在其他語言,例如Python的。大多數開發者都是使用這些封裝庫的API,因為它們簡化了D-Bus編程細節。libdbus被有意設計成為更高層次綁定的底層后端(low-levelbackend )。大部分libdbus的 API僅僅是為了用來實現綁定。

 

  在D-Bus中,“bus”是核心的概念,它是一個通道:不同的程序可以通過這個通道做些操作,比如方法調用、發送信號和監聽特定的信號。在一台機器上總線守護有多個實例(instance)。這些總線之間都是相互獨立的。

一個持久的系統總線(system bus):

它在引導時就會啟動。這個總線由操作系統和后台進程使用,安全性非常好,以使得任意的應用程序不能欺騙系統事件。它是桌面會話和操作系統的通信,這里操作系統一般而言包括內核和系統守護進程。這種通道的最常用的方面就是發送系統消息,比如:插入一個新的存儲設備;有新的網絡連接;等等。

還將有很多會話總線(session buses):

這些總線當用戶登錄后啟動,屬於那個用戶私有。它是用戶的應用程序用來通信的一個會話總線。同一個桌面會話中兩個桌面應用程序的通信,可使得桌面會話作為整體集成在一起以解決進程生命周期的相關問題。這在GNOME和KDE桌面中大量使用。

  對於一些遠程的工作站,在system bus中可能會有一些問題,例如熱插拔,是否需要通知遠端的terminal,這會使得kernel暴露一些設備的能力,不過,我們現在關心D-Bus,是因為手持終端設備的使用,這些將不會出現。在Internet Tablet,也包括我們的手機系統,所有的應用程序都是使用一個用戶ID運行的,所以只有一個會話通道,這一點是和Linux桌面系統是有明顯區別的。

  D-Bus是低延遲而且低開銷的,設計得小而高效,以便最小化傳送的往返時間。另外,協議是二進制的,而不是文本的,這樣就排除了費時的序列化過程。從開發者的角度來看,D-BUS 是易於使用的。有線協議容易理解,客戶機程序庫以直觀的方式對其進行包裝。D-Bus的主要目的是提供如下的一些更高層的功能:

A、結構化的名字空間

B、獨立於架構的數據格式

C、支持消息中的大部分通用數據元素

D、帶有異常處理的通用遠程調用接口

E、支持廣播類型的通信

 

Bus daemon總線守

       Bus daemon是一個特殊的進程:這個進程可以從一個進程傳遞消息給另外一個進程。當然了,如果有很多applications鏈接到這個通道上,這個 daemon進程就會把消息轉發給這些鏈接的所有程序。在最底層,D-Bus只支持點對點的通信,一般使用本地套接字(AF_UNIX)在應用和bus daemon之間通信。D-Bus的點對點是經過busdaemon抽象過的,由busdaemon來完成尋址和發送消息,因此每個應用不必要關心要把消息發給哪個進程。D-Bus發送消息通常包含如下步驟(正常情況下):

創建和發送消息 給后台bus daemon進程,這個過程中會有兩個上下文的切換。

后台bus daemon進程會處理該消息,並轉發給目標進程,這也會引起上下文的切換目標程序接收到消息,然后根據消息的種類,做不同的響應:要么給個確認、要么應答、還有就是忽略它。最后一種情況對於“通知”類型的消息而言,前兩種都會引起進一步的上下文切換。

綜上原因,如果你准備在不同的進程之間傳遞大量的數據,D-Bus可能不是最有效的方法,最有效的方法是使用共享內存,但是對共享內存的管理也是相當復雜的。

 

D-Bus進程通信簡單框

 

 

 

D-Bus常見概

原生對象和對象路

所有使用D-BUS的應用程序都包含一些對象, 當經由一個D-BUS連接收到一條消息時,該消息是被發往一個對象而不是整個應用程序。在開發中程序框架定義着這樣的對象,例如Java,GObject,QObject等等,在D-Bus中成為native object

對於底層的D-Bus協議,即libdbus API,並不理會這些native object,它們使用的是一個叫做object path的概念。通過object path,高層編程可以為對象實例進行命名,並允許遠程應用引用它們。這些名字看起來像是文件系統路徑,例如一個對象可能叫做“/org/kde/kspread/sheets/3/cells/4/5”。易讀的路徑名是受鼓勵的做法,但也允許使用諸如“/com/mycompany/c5yo817y0c1y1c5b”等,只要它可以為你的應用程序所用。Namespacing的對象路徑以開發者所有的域名開始(如 /org/kde)以避免系統不同代碼模塊互相干擾

簡單地說:一個應用創建對象實例進行D-Bus的通信,這些對象實例都有一個名字,命名方式類似於路徑,例如/com/mycompany,這個名字在全局(session或者system)是唯一的,用於消息的路由

 

方法和信號Methodsand Signals

每一個對象有兩類成員:方法和信號。方法就是JAVA中同樣概念,方法是一段函數代碼,帶有輸入和輸出。信號是廣播給所有興趣的其他實體,信號可以帶有數據payload

在 D-BUS 中有四種類型的消息:方法調用(method calls)、方法返回(method returns)、信號(signals)和錯誤(errors)。要執行 D-BUS 對象的方法,您需要向對象發送一個方法調用消息。它將完成一些處理(就是執行了對象中的Method,Method是可以帶有輸入參數的。)並返回,返回消息或者錯誤消息。信號的不同之處在於它們不返回任何內容:既沒有“信號返回”消息,也沒有任何類型的錯誤消息

 

接口Interface

每一個對象支持一個或者多個接口,接口是一組方法和信號,接口定義一個對象實體的類型。D-Bus對接口的命名方式,類似org.freedesktop.Introspectable。開發人員通常將使用編程語言類的的名字作為接口名字

 

Proxies

代理對象用來表示其他的remote object。當我們觸發了proxy對象的method時,將會在D-Bus上發送一個method_call的消息,並等待答復,根據答復返回。使用非常方便,就像調用一個本地的對象

 

Bus Names總線名

  當一個應用連接到bus daemon,daemon立即會分配一個名字給這個連接,稱為unique connection name ,這個唯一標識的名字以冒號:開頭,例如:34-907,這個名字在daemon的整個生命周期是唯一的。但是這種名字總是臨時分配,無法確定的,也難以記憶,因此應用可以要求有另外一個名字well-known name 來對應這個唯一標識,就像我們使用域名來對應IP地址一樣。例如可以使用com.mycompany來映射:34-907。應用程序可能會要求擁有額外的周知名字(well-knownname )。例如,你可以寫一個規范來定義一個名字叫做 com.mycompany.TextEditor

你的協議可以指定自己擁有這個名字,一個應用程序應該在路徑/com/mycompany /TextFileManager下有一個支持接口org.freedesktop.FileHandler的對象。應用程序就可以發送消息到這個總線名字,對象,和接口以執行方法調用。  

ps:我覺得這里說法有問題,應該是在根據bus name :   org.freedesktop.FileHandler 找到一個bus path是 :  /com/mycompany /TextFileManager 的對象,支持method的調用

當一個應用結束或者崩潰是,OS kernel會關閉它的總線連接。總線發送notification消息告訴其他應用,這個應用的名字已經失去他的owner。當檢測到這類notification時,應用可以知道其他應用的生命周期。這種方式也可用於只有一個實例的應用,即不開啟同樣的兩個應用的情況

 

連接建立有server和client,對於bus daemon,應用就是client,daemon是server。一個D-Bus的地址是指server用於監聽,client用於連接的地方,例如unix:path=/tmp/abcedf標識server將在路徑/tmp/abcedf的UNIX domain socket監聽。地址可以是指定的TCP/IP socket或者其他在或者將在D-Bus協議中定義的傳輸方式

  如果使用bus daemon,libdbus將通過讀取環境變量自動獲取session bus damon的地址,通過檢查一個指定的UNIX domain socket路徑獲取system bus的地址。如果使用D-bus,但不是daemon,需要定義那個應用是server,那個是client,並定義一套機制是他們認可server的地址,這不是通常的做法

  通過上面的描述,我們可以獲得下面的視圖

Address –> [BusName] –> Path –> Interface –> Method

bus name不是必要的,它只在daemon的情況下用於路由,點對點的直接連接是不需要的

簡單地說:Address是D-Bus中server用來監聽client的地址,當一個client連接上D-Bus,通常是Daemo的方式,這個client就有了一個Bus Name。其他應用可以根據消息中所帶的Bus Name,來判斷和哪個應用相關。消息在總線中傳遞的時候,傳遞到應用中,再根據objectpath,送至應用中具體的對象實例中,也就是是應用中根據Interface創建的對象。這些Interface有method和singal兩種,用來發送、接收、響應消息

 

一個例子的示意圖

 

 

D-Bus

消息通過D-Bus在進程間傳遞。有四類消息

一、Method call消息:將觸發對象的一個method 

二、Method return消息:觸發的方法返回的結

       三、Error消息:觸發的方法返回一個異常 

四、Signal消息:通知,可以看作為事件消息

 

一個消息有消息頭header,里面有field,有一個消息體body,里面有參數arguments。消息頭包含消息體的路由信息,消息體就是凈荷payload。頭字段可能包括發送者的bus名,目的地的bus名,方法或者signal名等等,其中一個頭字段是用於描述body中的參數的類型,例如“i”標識32位整數,"ii”表示凈荷為2個32為整數

 

發送Method call消息的場

  一個method call消息從進程A到進程B,B將應答一個method return消息或者error消息。在每個call消息帶有一個序列號,應答消息也包含同樣的號碼,使之可以對應起來。他們的處理過程如下

如果提供proxy,通過觸發本地一個對象的方法從而觸發另一個進程的遠端對象的方法。應用調用proxy的一個方法,proxy構造一個method call消息發送到遠端進程

對於底層的API,不使用proxy,應用需要自己構造method call消息

一個method call消息包含:遠端進程的bus name,方法名字,方法的參數,遠端進程中object path,可選的接口名字

method call消息發送到bus daemon

bus daemon查看目的地的bus name。如果一個進程對應這個名字,bus daemon將method call消息發送到該進程中。如果沒有發現匹配,bus daemon創建一個error消息作為應答返回

進程接收后將method call消息分拆。對於簡單的底層API情況,將立即執行方法,並發送一個method reply消息給bus daemon。對於高層的API,將檢查對象path,interface和method,觸發一個native object的方法,並將返回值封裝在一個method reply消息中

bus daemon收到method reply消息,將其轉發到原來的進程中進程查看method reply消息,獲取返回值。這個響應也可以標識一個error的殘生。當使用高級的捆綁,method reply消息將轉換為proxy方法的返回值或者一個exception

Bus daemon保證message的順序,不會亂序。例如我們發送兩個method call消息到同一個接受方,他們將按順序接受。接收方並不要求一定按順序回復。消息有一個序列號了匹配收發消息

 

發送Signal的場

  signal是個廣播的消息,不需要響應,接收方向daemon注冊匹配的條件,包括發送方和信號名,bus守護只將信號發送給希望接受的進程。處理流程如下

一個signal消息發送到bus daemon

signal消息包含發布該信號的interface名字,signal的名字,進程的bus名字,以及參數

任何進程都可以注冊的匹配條件(match rules)表明它所感興趣的signal。總線有個注冊match rules列表

bus daemon檢查那些進程對該信號有興趣,將信號消息發送到這些進程中

收到信號的進程決定如何處理。如果使用高層的捆綁,一個porxy對象將會十分一個native的信號。如果使用底層的API,進程需要檢查信號的發送發和信號的名字決定如果進行處理

 

Introspection

  D-Bus對象可能支持一個接口org.freedesktop.DBus.Introspectable。該接口有一個方法Introspect,不帶參數,將返回一個XML string。這個XML字符串描述接口,方法,信號

D-Bus提供兩個命令dbus-monitor,可以查看bus,dbus-send命令,可以發送消息

 

Signal的收發小例

從底層,即libdbus學習如何發送signal,以及如何監聽signal。signal在D-Bus的Daemon中廣播,為了提高效率,只發送給向daemon注冊要求該singal的對象

 

 

對於程序,第一步需要將應用和D-Bus后台建立連接,也就是和System D-Busdaemon或者Session D-Bus daemon建立連接。一旦建立,daemon會給這條連接分配一個名字,這個名字在system或者session的生命周期是唯一的,即unique connectionname,為了方便記憶,可以為這條連接分配一個便於記憶的well-known name。對於信號方式,分配這個名字不是必須的(在method_call中是需要的),因為在信號的監聽中秩序給出Interface的名字和信號名稱,在下面的例子中,可以將相關的代碼屏蔽掉,不影響運行,但是通常我們都這樣處理,尤其在復雜的程序中。在我們的例子中,定義這個BUS name為test.singal.source。當然一個好的名字,為了避免於其他應用重復,應當使用com.mycompany.myfunction之類的名字。 ,而interface的名字,一般前面和connection的BUS name一致。

 

 

 

 

Dbus data type

 

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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM