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 来通信。
附:示例代码:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Linux命令行与shell脚本编程大全 第3版
[美]布鲁姆,布雷斯纳汉 / 门佳、武海峰 / 人民邮电出版社 / 2016-8-1 / CNY 109.00
这是一本关于Linux命令行与shell脚本编程的全方位教程,主要包括四大部分:Linux命令行,shell脚本编程基础,高级shell脚本编程,如何创建实用的shell脚本。本书针对Linux系统的最新特性进行了全面更新,不仅涵盖了详尽的动手教程和现实世界中的实用信息,还提供了与所学内容相关的参考信息和背景资料。通过本书的学习,你将轻松写出自己的shell脚本。一起来看看 《Linux命令行与shell脚本编程大全 第3版》 这本书的介绍吧!