DBUS入门笔记

不问归期 提交于 2019-12-06 07:14:55

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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!