By Kong-dao Xing
Linux 系统IPC种类:
- 信号
- 管道
- 命名管道
- 信号量
- 消息队列
- 共享内存
- 内存映射文件
- 套接字
DBus 概念
总线
持久化的系统总线(system bus)
- 系统开机引导时就启动, system bus 由操作系统和后台进程使用, 安全性好。
会话总线(session bus)
- 用户登录时启动
在指定的对象中调用指定的方法,需要知道的参数如下:
Address -> Bus Name -> Path -> Interface -> Method
DBus 库
1. 函数库libdbus,用于两个应用程序互相联系和交互消息。
2. 一个基于libdbus构造的消息总线守护进程(dbus-daemon),可同时与多个应用程序相连,
并能把来自一个应用程序的消息路由到0或者多个其他程序。
3. 基于特定应用程序框架的封装库或捆绑(wrapper libraries or bindings )。
例如,libdbus-glib和libdbus-qt,还有绑定在其他语言,
例如 Python 的。大多数开发者都是使用这些封装库的API,
因为它们简化了D-Bus编程细节。libdbus被有意设计成为更高层次绑定的底层后端(low-levelbackend )。
大部分libdbus的 API仅仅是为了用来实现绑定。
优点
低延迟:DBus一开始就是用来设计成避免来回传递和允许异步操作的。因此虽然在Application和Daemon之间是通过socket实现的,但是又去掉了socket的循环等待,保证了操作的实时高效。
低开销:DBus使用一个二进制的协议,不需要转化成像XML这样的文本格式。因为DBus是主要用来机器内部的IPC,而不是为了网络上的IPC机制而准备的.所以它才能够在本机内部达到最优效果。
高可用性:DBus是基于消息机制而不是字节流机制。它能自动管理一大堆困难的IPC问题。同样的,DBus库被设计来让 程序员 能够使用他们已经写好的代码。而不会让他们放弃已经写好的代码,被迫通过学习新的IPC机制来根据新的IPC特性重写这些代码。
绝大部分的 linux 桌面环境都使用的dbus作为进程间通信。 比如:GNOME 和KDE
支持golang, c/c++, python 语言
D-Bus进程通信简单框架
D-Bus进程通信简单框架
Create DBus
获取system bus
QDBusConnection systemBus = QDBusConnection::systemBus();
获取session bus
QDBusConnection sessionBus = QDBusConnection::sessionBus();
注册服务
bool
QDBusConnection::registerService (
const
QString & serviceName )
注册对象接口
bool QDBusConnection::registerObject (const QString & path, QObject * object, RegisterOptions options = ExportAdaptors ) //常用Option选项 QDBusConnection::ExportAdaptors QDBusConnection::ExportNonScriptableSlots QDBusConnection::ExportNonScriptableSignals QDBusConnection::ExportNonScriptableProperties QDBusConnection::ExportNonScriptableInvokables// Q_INVOKABLE QDBusConnection::ExportAllContents = QDBusConnection::ExportNonScriptableSlots|QDBusConnection::ExportNonScriptableSignals |QDBusConnection::ExportNonScriptableInvokables|QDBusConnection::ExportScriptableProperties
例子:
Dbus Server
// person.h
#include <QObject>
class Person :public QObject {
Q_OBJECT
Q_CLASSINFO("D-Bus Interface","com.brion.interface")
public:
explicit Person(QObject *parent = 0);
signals:
void nameChanged(QString);
void ageChanged(int);
public slots:
QString name()const {return m_name; }
// can't be reference
void setName(QString name) {
m_name = name;
}
int age()const {return m_age; }
void setAge(int age) {
m_age = age;
}
private:
QString m_name;
int m_age;
};
// main.cpp
#include <QtDBus/QDBusConnection>
#include <person.h>
int main(int argc,char *argv[]) {
QApplication a(argc, argv);
QDBusConnection sessionBus = QDBusConnection::sessionBus();
if (sessionBus.registerService("com.brion.service")) {
sessionBus.registerObject("/",new Person(),
QDBusConnection::ExportAllContents);
}
return a.exec();
}
d-feet 查看dbus 服务
DBus Client
// main.cpp
#include <QCoreApplication>
#include <QDBusConnection>
#include <QDBusInterface>
#include <QDBusReply>
#include <QDebug>
int main(int argc,char *argv[]) {
QCoreApplication a(argc, argv);
QDBusInterface interface("com.brion.service","/","com.brion.interface");
interface.call("setName","Brion");
QDBusReply<QString> reply = interface.call("name");
if (reply.isValid()) {
qDebug()<<"name = "<<reply.value();
}
interface.call("setName","ASML");
reply = interface.call("name");
if (reply.isValid()) {
qDebug()<<"name = "<<reply.value();
}
return a.exec();
}
输出:
name =
"Brion"
name =
"ASML"
客户端调用服务端的函数
方式一
// 传参数
QDBusMessage msg = QDBusMessage::createMethodCall("com.brion.service",
"/","com.brion.interface","setName");
msg << QString("Brion");
QDBusMessage response = QDBusConnection::sessionBus().call(msg);
// 获取返回值
QDBusMessage msg = QDBusMessage::createMethodCall("com.brion.service",
"/","com.brion.interface","name");
QDBusMessage response = QDBusConnection::sessionBus().call(msg);
// 判断Method 是否被正确返回
if(response.type() == QDBusMessage::ReplyMessage)
{
// QDBusMessage的arguments不仅可以用来存储发送的参数,也用来存储返回值
// 这里取得 name 的返回值
QString name= response.arguments().takeFirst().toString();
}
方式二
QDBusInterface interface("com.brion.service","/",
"com.brion.interface",
QDBusConnection::sessionBus());
if(!interface.isValid())
{
qDebug() << qPrintable(QDBusConnection::sessionBus().lastError().message());
exit(1);
}
// 调用 setName,
interface.call("setName","Brion");
// 调用 name,
QDBusReply<QString> reply = interface.call("name");
if(reply.isValid())
{
QString value = reply.value();
qDebug()<<"value = "<<value ;
}
方式三: 异步调用
QDBusPendingCall async = interface->asyncCall("setName","Brion");
// async.waitForFinished ()
QDBusPendingCallWatcher *watcher =new QDBusPendingCallWatcher(async,this);
QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
this, SLOT(callFinishedSlot(QDBusPendingCallWatcher*)));
void MyClass::callFinishedSlot(QDBusPendingCallWatcher *call)
{
QDBusPendingReply<QString> reply = *call;
if (!reply.isError()) {
QString name= reply.argumentAt<0>();
qDebug()<<"name = "<<name;
}
call->deleteLater();
}
客户端接收服务端信号
方式一:
QDBusConnection::sessionBus().connect("com.brion.service","/",
"com.brion.interface",
"ageChanged",this,
SLOT(onAgeChanged(int)));
方式二:
QDBusInterface *interface =new QDBusInterface ("com.brion.service","/",
"com.brion.interface",DBusConnection::sessionBus());
QObject::connect(&interface, SIGNAL(ageChanged(int)), object, SLOT(onAgeChanged(int)));
QtDBus 默认支持的数据类型
| Qt type |
D-Bus equivalent type |
|---|---|
| Qt type |
D-Bus equivalent type |
| uchar | BYTE |
| bool | BOOLEAN |
| short | INT16 |
| ushort | UINT16 |
| int | INT32 |
| uint | UINT32 |
| qlonglong | INT64 |
| qulonglong | UINT64 |
| double | DOUBLE |
| QString | STRING |
| QDBusVariant | VARIANT |
| QDBusObjectPath | OBJECT_PATH |
| QDBusSignature | SIGNATURE |
同时支持
QStringList, QByteArray
自定义类型
方式一:
struct MyStructure {
int count;
QString name;
};
Q_DECLARE_METATYPE(MyStructure)
// 服务端和客户端都要重载
QDBusArgument &operator<<(QDBusArgument &argument,const MyStructure &mystruct)
{
argument.beginStructure();
argument << mystruct.count << mystruct.name;
argument.endStructure();
return argument;
}
// Retrieve the MyStructure data from the D-Bus argument
const QDBusArgument &operator>>(const QDBusArgument &argument, MyStructure &mystruct)
{
argument.beginStructure();
argument >> mystruct.count >> mystruct.name;
argument.endStructure();
return argument;
}
qRegisterMetaType<MyStructure>("MyStructure");
qDBusRegisterMetaType<MyStructure>();
方式二:
利用QByteArray实现自定义类型
class MyClass {
private:
int count;
QString name;
}
QDataStream & operator<< (QDataStream& stream,const MyClass& myclass)
{
stream<<myclass.count;
stream<<myclass.name;
}
QDataStream & operator>> (QDataStream& stream,const MyClass& myclass)
{
stream>>myclass.count;
stream>>myclass.name;
}
服务端先把数据写入QByteArray。 客户端通过QDataStream把数据从QByteArray读出
编写Adaptor
如果注册对象时,使用QDBusConnection::ExportAllContents, 会导致很多的接口都暴露给用户。 为此, 可以编写Adaptor来控制接口
// adaptor.h
#include <QDBusAbstractAdaptor>
#include <QDebug>
class Adaptor :public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface","com.brion.interface")
Q_CLASSINFO("D-Bus Introspection",""
"<interface name=\"com.brion.interface\">\n"
" <method name=\"setAge\">\n"
" <arg type=\"i\" direction=\"in\"/>"
" </method>\n"
" <signal name=\"ageChanged\">\n"
" <arg type=\"i\" direction=\"out\"/>"
" </signal>"
"</interface>\n"
"")
public:
Adaptor::Adaptor(QObject* parent = 0) : QDBusAbstractAdaptor(parent) {
// 是否转发signals
setAutoRelaySignals(true);
}
signals:
void ageChanged(int age);
public slots:
void setAge(int age) {
QMetaObject::invokeMethod(parent(),"setAge", Q_ARG(int, age));
}
};
// person.h
#include <QObject>
class Person :public QObject
{
Q_OBJECT
public:
Person::Person(QObject *parent) : QObject(parent) {
m_age = 0;
new Adaptor(this);
}
signals:
void nameChanged(QString);
void ageChanged(int);
public slots:
QString name()const {return m_name; }
void setName(QString name) {
m_name = name;
}
int age()const {return m_age; }
void setAge(int age) {
m_age = age;
emit ageChanged(m_age);
}
private:
QString m_name;
int m_age;
};
XML数据类型定义
基本类型
| type |
code |
|---|---|
| type |
code |
| BYTE | y |
| BOOLEAN | b |
| INT16 | n |
| UINT16 | q |
| INT32 | i |
| UINT32 | u |
| INT64 | x |
| UINT64 | t |
| DOUBLE | d |
| STRING | s |
| VARIANT | v |
| OBJECT_PATH | o |
void setAge(int age)
<method name=\"setAge\"> <arg type=\"i\" direction=\"in\"/> // in 传参 </method>
int age() const
<method name=\"age\"> <arg type=\"i\" direction=\"out\"/> // out 返回值 </method>
void setName(QString name)
<method name=\"setName\"> <arg type=\"s\" direction=\"in\"/> </method>
void setNames(QStringList names)
<method name=\"setNames\"> <arg type=\"as\" direction=\"in\"/> </method>
void setNames(QByteArray ba)
<method name=\"setNames\"> <arg type=\"ay\" direction=\"in\"/> </method>
自定义类型
类和结构体, 不允许空结构体
struct MyStruct
{
int key;
QString value;
}
void setCustom(MyStruct my)
<method name=\"setCustom\"> <arg type=\"(is)\" direction=\"in\"/> </method>
void setCustoms(QList<MyStruct> mystructs)
<method name=\"setCustoms\"> <arg type=\"a(is)\" direction=\"in\"/> </method>
struct
MyStruct
{
QMap<QString, QVariant> maps;
}
void setCustom(MyStruct my)
<method name=\"setCustom\">
<arg type=\"(a{sv})\" direction=\"in\"/>
</method>
键值队
void setDict(QMap<int, QString> dict);
<method name=\"setDict\">
<arg type=\"a{is}\" direction=\"in\"/>
</method>
权限控制
通过配置文件来实现权限控制。
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> <busconfig> <type>session/system</type> <policy context="default"> <deny send_destination="com.brion.service"/> </policy> <policy user="service"> <!-- Only service user can own the "com.brion.service" service --> <allow own="com.brion.service"/> <allow send_destination="com.brion.service"/> </policy> <policy user="system"> <allow send_destination="com.brion.service"/> </policy> <policy group="systemapps"> <allow send_destination="com.brion.service" send_interface="com.brion.interface" send_member="setAge" send_path="/" send_type="method_call"/> <allow send_destination="com.brion.service" send_interface="com.brion.interface" send_member="ageChanged" send_path="/" send_type="signal"/> <deny send_destination="com.brion.service" send_interface="com.brion.service" send_member="age" send_path="/" send_type="method_call"/> <deny send_destination="com.brion.service" send_interface="com.brion.interface" send_member="nameChanged" send_path="/" send_type="signal"/> </policy> <policy at_console="true"> <allow send_destination="xxx.xx.xx"/> <allow send_interface="xxx.xx.xx"/> </policy> </busconfig>
所有 context=”default” 的策略被应用
所有 group=”connection’s user’s group” 的策略以不定的顺序被应用
所有 user=”connection’s auth user” 的策略以不定顺序被应用
所有 at_console=”true” 的策略被应用
所有 at_console=”false” 的策略被应用
所有 context=”mandatory” 的策略被应用
后应用的策略会覆盖前面的策略。
dbus 常用命令
dbus-send
调用函数
dbus-send --session --type=method_call --dest=com.brion.service / com.brion.interface.setName "string:Brion"
发送信号
dbus-send --session --type=signal --dest=com.brion.service / com.brion.interface.ageChanged int32:10000
dbus-monitor
监听dbus-daemon的行为
手动启动dbus-daemon
DBUS_VERBOSE=1 dbus-daemon --session --print-address
启动后会得到一个地址
unix:abstract=/tmp/dbus-YcjSNNPJHg,guid=18b385acdbd58611ffd3196b4beb69f0
设置环境变量 DBUS_SESSION_BUS_ADDRESS
DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-YcjSNNPJHg,guid=18b385acdbd58611ffd3196b4beb69f0
再启动dbus server 和dbus client 都会用这个dbus-daemon 来通信。
附:示例代码:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。