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不能应付的远端系统管理员问题:
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
DBUS和其他IPC相比具有的特点:
异步处理的二进制协议
安全可靠的连接
消息总线是守护进程
通信机制的部署与实现都是明确的
和DCOP通信机制类似
安全特性,支持系统消息
Object Path
是用于让上层绑定找到native object(比如QObject,GObject)
接口
DBus使用了字符串定义来表示不同的interface,接口包括了信号和方法。
代理
proxy object用来代表远端进程中的对象。
代理给开发者简化了不少工作量。开发者可以使用代理调用远端对象的方法并得到返回值,这就像是调用本地方法一样简单。
伪代码比较:
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
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
服务端注册服务:
;
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上也能运行了: