dbus通訊與接口介紹

 DBUS是一種高級的進程間通訊機制。DBUS支持進程間一對一和多對多的對等通訊,在多對多的通信時,須要後臺進程的角色去分轉消息,當一個進程發消息給另一個進程時,先發消息到後臺進程,再經過後臺進程將信息轉發到目的進程。DBUS後臺進程充當着一個路由器的角色。node

    DBUS中主要概念爲總線,鏈接到總線的進程可經過總線接收或傳遞消息,總線收到消息時,根據不一樣的消息類型進行不一樣的處理。DBUS中消息分爲四類:linux

    1.  Methodcall消息:將觸發一個函數調用 ;算法

    2.  Methodreturn消息:觸發函數調用返回的結果;api

    3.  Error消息:觸發的函數調用返回一個異常 ;網絡

    4.  Signal消息:通知,能夠看做爲事件消息。session

1.2  DBUS應用場景

 

    根據DBUS消息類型可知,DBUS提供一種高效的進程間通訊機制,主要用於進程間函數調用以及進程間信號廣播。數據結構

1 . 函數調用多線程

    DBUS能夠實現進程間函數調用,進程A發送函數調用的請求(Methodcall消息),通過總線轉發至進程B。進程B將應答函數返回值(Method return消息)或者錯誤消息(Error消息)。架構

2 . 消息廣播併發

    進程間消息廣播(Signal消息)不須要響應,接收方須要向總線註冊感興趣的消息類型,當總線接收到「Signal消息」類型的消息時,會將消息轉發至但願接收的進程。

1.3  DBUS通訊特色

    DBUS是一種低延遲、低開銷、高可用性的進程間通訊機制。其協議是二進制的,避免序列化的過程,通訊效率較高。DUBUS能夠提供一些更高層的功能:

    1.  結構化的名字空間;

    2.  獨立於架構的數據格式;

    3.  支持消息中的大部分通用數據元素;

    4.  帶有異常處理的通用遠程調用接口;

    5.  支持廣播類型的通訊。
 

2. 技術實現

2.1 實現原理

    DBUS是一種高級的IPC通訊機制,通訊流程如圖 2‑1所示。在DBUS通訊過程當中,存在一個後臺進程(BUS Daemon Process)。後臺進程和普通進程間信息交互是經過域套接字進行通訊。

 
圖 2-1 DBUS通訊原理

     如圖 2‑1所示,進程1(Process1)需先鏈接到總線(dbus_bus_get),其次構造消息(dbus_message_new_signal),而後發送消息(dbus_connection_send)到後臺進程。後臺進程接收消息,而後根據消息類型對消息進行不一樣處理(bus_dispatch_matches)。

     進程2(Process2)接收消息前須要鏈接到總線,並告知總線本身但願獲得的消息類型(dbus_bus_add_match),而後等待接收消息(dbus_connection_pop_message)。進程2(Process2)收到總線轉發的消息時會根據消息類型,作不一樣的處理(如果信號類型則不須要發送返回值給總線)。

2.2 鏈接到總線

    進程間通訊前,須要鏈接到總線。調用dbus_bus_get函數鏈接進程到總線,創建進程和總線之間的鏈接(DBusConnection)。創建鏈接後,須要爲這個鏈接註冊名稱,方便後面對這個鏈接進行操做,調用dbus_bus_request_name函數對鏈接進行註冊名稱。

    創建鏈接和註冊名稱是在程序開始時執行,程序結束時,調用dbus_connection_close函數關閉一個鏈接。函數接口聲明如程序清單 2‑1所示。

程序清單 2-1 創建、註冊名稱和關閉鏈接

 

[plain]  view plain  copy
 
  1. DBusConnection  *dbus_bus_get  (DBusBusType  type,  DBusError   *error)             /*  創建和總線的鏈接  */  
  2.   
  3. int  dbus_bus_request_name  (DBusConnection   *connection,  
  4.                              const char         *name,  
  5.                              unsigned int        flags,  
  6.                              DBusError        *error)                                   /*  註冊鏈接名稱      */  
  7.   
  8. void  dbus_connection_close  (DBusConnection  *connection)                           /*  關閉鏈接          */  

 

2.3 信號發送與接收

 

2.3.1 信號發送

    DBUS中信號是一種廣播的消息,當發出一個信號,全部鏈接到 DBUS 總線上並註冊了接受對應信號的進程,都會收到該信號。

    進程發出一個信號前,須要建立一個 DBusMessage 對象來表明信號,而後追加上一些須要發出的參數,就能夠發向總線了。發完以後須要釋放消息對象。信號發送的函數聲明如程序清單 2‑2所示。
程序清單2-2  信號發送接口
[cpp]  view plain  copy
 
  1. DBusMessage  *dbus_message_new_signal  (const  char  *path,  
  2.                                        const  char  *iface,  
  3.                                        const  char  *name)                       /*  建立信號類型消息      */  
  4.   
  5. void  dbus_message_iter_init_append  ( DBusMessage     *message,  
  6.                            DBusMessageIter  *iter)                /*  加入參數到信號        */  
  7.   
  8. dbus_bool_t  dbus_connection_send  ( DBusConnection  *connection,  
  9.                                      DBusMessage    *message,  
  10.                                      dbus_uint32_t    *serial)                       /*  發送信號到總線        */  
  11.   
  12. void  dbus_message_unref  (DBusMessage *message)                                 /*  釋放消息              */  

2.3.2 信號接收

    進程接收信號時,需先告知總線進程感興趣的消息,而後等待接收消息。信號接收函數聲明如程序清單 2‑3所示。
程序清單 2-3 信號接收接口
[cpp]  view plain  copy
 
  1. void  dbus_bus_add_match  ( DBusConnection  *connection,  
  2.                             const char        *rule,  
  3.                             DBusError       *error)                                 /*  告知總線感興趣的消息   */  
  4.   
  5. DBusMessage  *dbus_connection_pop_message  ( DBusConnection  *connection)         /*  接收消息               */  
  6.   
  7. dbus_bool_t  dbus_message_is_signal  (DBusMessage  *message,  
  8.                                       const char      *iface,  
  9.                                       const char     *signal_name)                    /*  判斷消息是否爲信號     */  

2.4 函數調用和提供函數調用

2.4.1 函數調用

     調用一個遠程函數與發送一個信號原理相似,須要先建立一個消息(DBusMessage),而後經過註冊在 DBUS上的名稱指定發送的對象。而後追加相應的參數,調用方法分爲兩種,一種是阻塞式的,另外一種爲異步調用。異步調用的時候會獲得一個「DBusMessage *」 類型的返回消息,從這個返回消息中能夠獲取一些返回的參數。

    函數調用的函數聲明如程序清單 2‑4所示。
程序清單 2-4 函數調用接口
[cpp]  view plain  copy
 
  1. DBusMessage  *dbus_message_new_method_call  (const char  *destination,  
  2.                                              const char  *path,  
  3.                                              const char  *iface,  
  4.                                              const char  *method)                    /*  建立一個函數調用消息    */  
  5.   
  6. void  dbus_message_iter_init_append  (DBusMessage     *message,  
  7.                           DBusMessageIter  *iter)                     /*  爲消息添加參數           */  
  8.   
  9. dbus_bool_t  dbus_connection_send_with_reply  (DBusConnection   *connection,  
  10.                                                DBusMessage      *message,   
  11.                                                DBusPendingCall  **pending_return,  
  12.                                                int            timeout_milliseconds)       /*  發送消息                */  
  13.   
  14. void  dbus_pending_call_block  (DBusPendingCall  *pending)                           /*  阻塞等待返回值           */  
  15.   
  16. DBusMessage  *dbus_pending_call_steal_reply  (DBusPendingCall  *pending)             /*  得到返回消息            */  
  17.     
  18. dbus_bool_t  dbus_message_iter_init  (DBusMessage     *message,  
  19.                           DBusMessageIter  *iter)                     /*  獲取參數                */  

2.4.2 接收函數調用

    提供遠程函數調用,首先需告知總線進程感興趣的消息,其次從總線獲取消息並斷定消息是方法調用。而後從消息中獲取參數進行函數執行,最後建立返回消息,併發送消息至總線,由總線轉發至調用的進程。函數聲明如程序清單 2‑5所示。
程序清單 2-5 接收函數調用接口
[cpp]  view plain  copy
 
  1. void  dbus_bus_add_match  ( DBusConnection  *connection,  
  2.                             const char        *rule,  
  3.                             DBusError       *error)                                   /*  請求獲取調用消息       */  
  4.    
  5. DBusMessage  *dbus_connection_pop_message  ( DBusConnection  *connection)           /*  從總線獲取消息         */  
  6.   
  7. dbus_bool_t  dbus_message_is_method_call (DBusMessage  *message,  
  8.                                           const char     *iface,  
  9.                                           const char     *method)                       /*  斷定消息是方法調用     */  
  10.     
  11. dbus_bool_t  dbus_message_iter_init  (DBusMessage     *message,  
  12.                           DBusMessageIter  *iter)                    /*  獲取參數               */  
  13.   
  14. DBusMessage  *dbus_message_new_method_return  (DBusMessage *method_call)            /*  建立返回消息           */  
  15.   
  16. void  dbus_message_iter_init_append  ( DBusMessage     *message,  
  17.                            DBusMessageIter  *iter)                   /*  在消息中填入參數       */  
  18.   
  19. dbus_bool_t  dbus_connection_send  ( DBusConnection   *connection,  
  20.                                      DBusMessage     *message,  
  21.                                      dbus_uint32_t     *serial)                        /*  發送返回消息          */  

3. 小結

    DBUS是一種高效、易用的進程間通訊方式。本文檔介紹了DBUS的通訊原理,以信號收發和方法調用爲框架,介紹了DBUS中經常使用的函數接口。

 DBus分爲兩種類型:system bus(系統總線),用於系統(Linux)和用戶程序之間進行通訊和消息的傳遞;session bus(回話總線),用於桌面(GNOME, KDE等)用戶程序之間進行通訊。  

 

上節補存:

Name: 圖模型中的Name 在ROS的封裝體系中很是重要,全部的resource(從node到topic到service和 parameter等)都是在某個namespace中用特定的Name進行了定義。 通常來講,resource 能夠在本身的namespace中創 建新的resource,訪問和使用已有的resource。連接能夠創建在不一樣的資源之間,namespace保證了不一樣resource間的Name 不會衝突,也封裝了resource內部。

 

(能夠參考C++中namespace的概念。由於ROS分佈式系統的設計思路,對與不一樣主機上,不一樣功能區塊,均可以用namespace對其中 的resource name 包括 topic name 等 進行保護,使得name管理更加結構化,更多體如今對於代碼重用性的提升)

 

Computation graph: Computation graph是一個p2p的網絡結構。Node之間的連接關係的拓撲結構爲 mesh(網狀)

 

Node:node是一個處理計算的進程。(操做系統中的一個進程,由於要佔用端口號進行通信,多機分佈式系統中還要標明ip,詳見後面的 uri)Node在graph中使用topic service和parameter相互通信,協調工做。在不一樣的系統設計中,node承載的功能粒度也 不同。好比大部分的設備驅動都是單獨佔用一個node,爲了提升代碼複用率常常會有細粒度的劃分,而在不少視覺處理的系統避免使用多節點結構,傳遞圖像 數據會形成系統資源浪費(nodelet的應用)。Node的使用極大的增長了系統模塊化和代碼封裝的程度,也給系統帶來了一些錯誤容忍的能力。

 

Master node: master node 給ros系統中其餘節點提供命名與註冊的服務。它跟蹤節點中的publisher與 subscriber,service 的server與client,記錄其餘節點的位置(uri標明,host與port),並將這些信息通知給須要 創建連接的節點。(從分佈式系統的角度分析,ros這樣p2p網絡中集中式管理peer信息,也爲仿真環境提供虛擬時鐘 /clock 重要:ROS多機系統中,ros的全局時鐘僅是本機的系統時鐘,多機須要同步系統時間的工具來實現ros內時鐘同步,通常是ntp,pr2應用 linux中的chrony進行基於ntp的系統時間同步)

 

Message: node之間通信規定的統一數據格式,ros內部有數據格式規定語言來定義,而後由相應語言的client library中的message_generation 組件生成目標代碼。提供統一的串行化/解串行化 方法。

 

Topic:ros中廣爲使用的是異步的 publish-subscribe 通信模式。這種方式將信息的產生和使用雙方解耦。通常來講,節點沒 有通信對方那邊的信息。Node從須要的topic那取得消息,topic 能夠有多個 subscriber 與publisher。Topic 通常 用於單向,消息流通信。Node 須要同步通信交換信息時通常使用service。Topic 通常擁有很強的類型定義:一種類型的topic只能接受/ 發送特定數據類型(message type)的message。Publisher 沒有被要求類型一致性,可是接受時subscriber會檢查類型 的md5,進而報錯。

 

Service: service 用於處理ros通信中的同步通信,採用server/client 語義。每一個service type擁 有 request 與 response兩部分,對於service中的 server,ros不會檢查重名(name conflict),只有最後 註冊的server會生效,與client創建鏈接。

 

Parameter: parameter 能夠看做爲ros系統運行時中定義的全局變量,而master node 中有 parameter server 來維護這些變量。而namespace的存在使得parameter 擁有了很是清晰的層次劃分,避免了重名,並且使 得parameter訪問能夠單獨訪問也能夠樹狀訪問(層層解析namespace)

URI:定位node在分佈式系統中的位置,格式爲: protocol://host:port,protocol通常爲http或者 rosrpc, host爲 hostname 或者 ip 地址,port則爲端口號。

TCPROS:基於tcp協議的ros應用層數據協議,用於解析topic 與 service的二進制數據流。

 

  • 遠程過程調用(RPC)

ROS 通信中,節點經過遠程過程調用來實現創建鏈接,傳輸數據。ROS 中遠程過程調用採用XML-RPC 實現。遠程調用負責管理節點對計算圖 中信息的獲取與更改,還有一些全局的設置。RPC不直接支持數據的流傳輸(經過TCPROS與 UDPROS支持)。XML-RPC 優點在於支持它的語 言類型不少,而XML自己的文本屬性致使方便人調試。XML-RPC被封裝在http協議中傳輸,XML-RPC 調用時無狀態的 (stateless),沒有狀態信息須要追蹤,簡化了控制邏輯。

1》數據類型:

在XML-RPC中,方法參數和其返回值被封裝在value 的實體中,value有如下幾種固定的類型能夠選擇。(xml中體現爲value的子標籤)

string 爲ascii 碼的字符串,爲value的默認格式。不合法的字符只有& 和 <,s & <;表示。

int 或者 i4 是32位的有符號整型,十進制表示中,前綴-號則爲負數。

boolean 只有兩種取值,0和1,用於表示布爾類型。

double  實數類型,前綴-號表示負數。

dataTime.iso8601 日期時間,用iso-8601格式表示。

base64 用 base64算法編碼的二進制字符串。

array values組成的表,在data實體下一層

Struct 又稱爲map 表示關係的集合,每個struct實體是由一個 name 與 value的鍵值對組成。

2》 請求與應答:

遠程過程調用由兩個階段組成:請求(request) 與 應答(response)。 一個調用者將方法調用請求發給 被調用者,而後被調用者返回調用是否成功與相應返回值。

  1. 鏈接模式:

下面將簡述節點與其餘節點如何進行鏈接,最後初始化一個topic data的流傳輸,service 的實現有些許不一樣。

在以前對於節點與計算圖的介紹中,節點在master node處註冊 本身在publish /subscribe topic。經過 registerPublisher()和 registerSubscriber() 。節點在master處註冊完本身的 subscribe/publish topic後,master都會返回一個成功的應答,其中包含全部publisher節點的URI,然 後 subscriber去與相應的publisher創建鏈接傳輸 topic 相關信息(topic name,樹蕨類型,傳輸類型等)。有任何新的 節點去publisher 一個topic而且在master處已經完成本身的註冊時,master會給全部subscribe 這個topic的節點發 出一個publisherUpdate() 請求,裏面包含全部可用的 publisher的URI,topic 消息的數據由TCPROS協議傳輸。

簡單來講就是各個節點在 master處註冊信息,master發現有節點subscribe/publish相同的topic時,將 publisher的 信息經過 RPC 分發給各個subscriber,subscriber與publisher創建第一次鏈接,傳輸topic信 息,而後再根據publisher返回的topic 信息,創建第二次鏈接,publisher開始 傳輸具體的數據。

service

 

而對稱的過程是 註銷(unregisteration), publisher 經過遠程調用unregisterPublisher(), 然 後subscriber經過unregisterSubscriber()註銷,然而,是否關閉publisher與 subscriber間的數據流傳 輸取決於節點自己。

Service的工做原理與topic有些許不一樣,同一個service能被多個節點註冊,可是隻有最後一個能被其餘節點接受。一個節點調用 service時,經過lookupService() 遠程調用在master處查找相應service的URI。而後它將經過一個request 消 息調用service的提供者,若是成功了,service提供者將返回一個相應的response 消息,失敗了返回相應錯誤消息,(全部的消息傳輸默 認都是經過 TCPROS協議。)

subscribe_publisher

 

  • 數據流

XML-RPC爲咱們提供了方便整潔的遠程調用協議,可是它的冗長與以文本爲中心的編碼格式使得它不適合高帶寬,低延遲的數據傳輸任務。ROS定義 了本身的二進制數據流傳輸協議,減小了冗餘的協議增長從而增長帶寬,協議設計使得數據幾乎不須要解析(相對於rpc),從而減小延遲。詳細的TCPROS 協議內容能夠在 wiki.ros.org/ROS/TCPROS 找到。

(如今ROS中也有實現的UDPROS協議,能夠經過TransportHints 數據結構指定下層傳輸協議,甚至經過繼承Trasport 類 本身實現本機的進程間通信方法)。

下面撿一些TCPROS中的重點說一下:

Md5:TCPROS爲了保證兩邊傳輸數據類型一致,會在協議頭中給出topic name 的md5 hash算法處理過的值,而每次你生成新的msg 時,md5的值都會由於你內部數據類型的變更而改變,這樣就避免了新msg與 舊msg 傳輸類型不一致的問題。

Subscriber 選項tcp_nodelay :若是是「1」 則給socket 設置 TCP_NODELAY 選項,下降延遲,可能會下降傳輸效率。

Service client 選項:persistent 設置爲1,則service的連接會一直開放給多個 service request

(下面是一些理解:

  • TCPROS的協議頭佔的字節數比較固定,因此傳輸一幀中只傳輸有效位幾個字節是很是不划算的,不少狀況下能夠附上 std_msgs/header ,seq能夠檢測丟包,stamp能夠檢測消息實時性,frame_id不少狀況下必備。
  • Roscpp 對 數據流的控制api比rospy要豐富不少,好比roscpp中有對callback queue的操做,多線程回調函數的支持等,對數據傳輸要求比較高的節點仍是老老實實用cpp吧
  • 在一些對延遲要求比較高而又有一些無線通信等高延遲傳輸介質存在的應用中,能夠考慮用低延遲的方式互聯,好比xbee模塊代替wifi(本身寫一些與其餘節點的bridge),或者udpros代替tcpros,而且避免tcpros的協議頭佔用過多帶寬)
相關文章
相關標籤/搜索