1. 程式人生 > >DBUS入門筆記

DBUS入門筆記

DBUS簡介

學習網址:https://dbus.freedesktop.org/doc/dbus-tutorial.html

DBUS三層:
libdbus,訊息分發的守護程序Dbus daemon,應用程式框架的封裝庫或繫結 (For example, libdbus-glib and libdbus-qt)

libdbus僅僅支援one-to-one connection

關於傳遞,物件是message = header (filed) + body (param)
header常包括body中的資料型別資訊。

DBUS的應用場景:
同一會話中的桌面應用程式
桌面應用程式和OS(系統級別的守護程序)

DBUS可以解決傳統Linux IPC不能應付的遠端系統管理員問題:


           A gap in current Linux support is that policies with any sort of
           dynamic "interact with user" component aren't currently
           supported. For example, that's often needed the first time a network
           adapter or printer is connected, and
to determine appropriate places to mount disk drives. It would seem that such actions could be supported for any case where a responsible human can be identified: single user workstations, or any system which is remotely administered. This is
a classic "remote sysadmin" problem, where in this case hotplugging needs to deliver an event from one security domain (operating system kernel, in this case) to another (desktop for logged-in user, or remote sysadmin). Any effective response must go the other way: the remote domain taking some action that lets the kernel expose the desired device capabilities. (The action can often be taken asynchronously, for example letting new hardware be idle until a meeting finishes.) At this writing, Linux doesn't have widely adopted solutions to such problems. However, the new D-Bus work may begin to solve that problem.

DBUS和其他IPC相比具有的特點:

非同步處理的二進位制協議
安全可靠的連線
訊息匯流排是守護程序
通訊機制的部署與實現都是明確的
和DCOP通訊機制類似
安全特性,支援系統訊息

Object Path
是用於讓上層繫結找到native object(比如QObject,GObject)

介面
DBus使用了字串定義來表示不同的interface,介面包括了訊號和方法。

代理
proxy object用來代表遠端程序中的物件。
代理給開發者簡化了不少工作量。開發者可以使用代理呼叫遠端物件的方法並得到返回值,這就像是呼叫本地方法一樣簡單。
虛擬碼比較:


        In pseudocode, programming without proxies might look like this:

          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();
          }


        Programming with proxies might look like this:

          Proxy proxy = new Proxy(getBusConnection(), "/remote/object/path");
          Object returnValue = proxy.MethodName(arg1, arg2);

Bus name
當應用程式連線上bus daemon, daemon會立刻給該應用程式分配一個獨一無二的connection name。它通常是以冒號開頭。
與之對應,我們可以定義一個容易識別的domain name,與dbus name對應。

Dbus Address
指明server在哪裡監聽,client在哪裡連線
一般,我們使用Dbus Daemon來分發訊息,所以address由環境變數決定。但也可以重新定義自己的server,這樣的話需要指明依賴於server name上的通訊機制。

概念圖:Address -> [Bus Name] -> Path -> [Interface] -> Method
被方括弧包圍的物件是開發者不定義要提供的,需要分情況判斷。

4種類型的message
call message(呼叫object的函式),return message(返回計算的結果),error message(呼叫函式發生異常),signal message(emit signal時產生)
在呼叫遠端方法的過程中,DBUS有兩種message發揮著重要作用,分別是call message和return message。在call message中包含著一種序列號,return message具有相同的序列號,這樣就具備了匹配的作用。
call message包括:遠端程序的bus name,函式名,函式引數,object path,介面名(可選)

傳送訊號
Dbus也能傳送訊號,通過繫結,會有接收者來作出反應。

QTDBUS 學習案例:remotecontrolledcar

學習網址:http://doc.qt.io/qt-5/qtdbus-index.html

D-Bus Concept Analogy Name format
Service name Network hostnames Dot-separated (“looks like a hostname”)
Object path URL path component Slash-separated (“looks like a path”)
Interface Plugin identifier Dot-separated

例子:
remotecontrolledcar
object path:/com/trollech/examples/car
interface name:org.example.Examples.CarInterface
服務端註冊服務:

    QDBusConnection connection = QDBusConnection::sessionBus();
    connection.registerObject("/Car", car);
    connection.registerService("org.example.CarExample");

XML產生介面

<node name="/com/trollech/examples/car">
 <interface name="org.example.Examples.CarInterface">
  <method name="accelerate"/>
  <method name="decelerate"/>
  <method name="turnLeft"/>
  <method name="turnRight"/>
  <signal name="crashed"/>
 </interface>
</node>

Qt D-Bus XML compiler 是 qdbusxml2cpp,這兩者的產出:the interface (proxy) class or the adaptor class(服務端)
interface:
pro檔案加上 DBUS_ADAPTORS += car.xml
產生標頭檔案 qdbusxml2cpp -p car_interface.h: car.xml
產生cpp檔案 qdbusxml2cpp -i car_interface.h -p :car_interface.cpp car.xml
adaptor:
pro檔案加上 DBUS_INTERFACES += car.xml
呼叫相關的命令
產生標頭檔案 qdbusxml2cpp -a car_adaptor.h: car.xml
產生cpp檔案 qdbusxml2cpp -i car_adaptor.h -a :car_adaptor.cpp car.xml

客戶端 訪問遠端的物件:

#include "car_interface.h"

car = new org::example::Examples::CarInterface("org.example.CarExample", "/Car",
                           QDBusConnection::sessionBus(), this);

關於CarInterface:

typedef ::OrgExampleExamplesCarInterfaceInterface CarInterface;

/*
 * Proxy class for interface org.example.Examples.CarInterface
 */
class OrgExampleExamplesCarInterfaceInterface: public QDBusAbstractInterface

OrgExampleExamplesCarInterfaceInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = nullptr);
//QDBusAbstractInterface 提供了D-Bus的介面,用於本地訪問遠端的介面。

效果:

在mac上貌似沒有dbus-daemon,需要自己下載,配置
我安裝後的目錄是 /usr/local/Cellar/dbus/1.12.8/
Mac上由Launchd管理守護程序,有點像linux中的init。
為了讓dbus-daemon成為守護程序,我們需要將她的service plist檔案放到Launchd的配置目錄中。
這些目錄有:

# 登陸後程序啟動
~/Library/LaunchAgents
/Library/LaunchAgents
/System/Library/LaunchAgents

# 系統啟動後程序啟動
/Library/LaunchDaemons
/System/Library/LaunchDaemons

dbus安裝目錄下面有 org.freedesktop.dbus-session.plist
label string即之前提到過的service name

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>org.freedesktop.dbus-session</string>

    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/Cellar/dbus/1.12.8/bin/dbus-daemon</string>
        <string>--nofork</string>
        <string>--session</string>
    </array>

    <key>Sockets</key>
    <dict>
        <key>unix_domain_listener</key>
        <dict>
            <key>SecureSocketWithKey</key>
            <string>DBUS_LAUNCHD_SESSION_BUS_SOCKET</string>
        </dict>
    </dict>
</dict>

他可以被放在:/Users/weiyang/Library/LaunchAgents
我的mac上lantern是開機自啟的,所以我仿照org.getlantern.plist在ProgramArguments的</array>下面
加上了如下的key。

    <key>RunAtLoad</key>
    <true/>

並將她的檔案屬性改成:

然後重啟登陸,可以發現有了dbus-daemon

然後mac上也能運行了:

Launchd 參考文章:https://blog.csdn.net/astarring/article/details/69055218