Linux 系统IPC种类:
- 信号
- 管道
- 命名管道
- 信号量
- 消息队列
- 共享内存
- 内存映射文件
- 套接字
DBus 概念
总线
持久化的系统总线(system bus)
会话总线(session bus)
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仅仅是为了用来实现绑定。
优点
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::ExportScriptableProperties
QDBusConnection::ExportNonScriptableInvokables // Q_INVOKABLE
QDBusConnection::ExportAllContents =
QDBusConnection::ExportNonScriptableSlots|QDBusConnection::ExportNonScriptableSignals
|QDBusConnection::ExportNonScriptableInvokables|QDBusConnection::ExportScriptableProperties
例子:
Dbus Server
// person.h
#include
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
#include
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();
}
DBus Client
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QDBusInterface interface("com.brion.service", "/",
"com.brion.interface");
interface.call("setName", "Brion");
QDBusReply reply = interface.call("name");
if (reply.isValid()) {
qDebug()<<"name = "<
输出:
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不仅可以用来存储发送的参数,也用来存储返回值
// 这里取得 checkIn 的返回值
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 reply = interface.call("name");
if(reply.isValid())
{
QString value = reply.value();
qDebug()<<"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 reply = *call;
if (!reply.isError()) {
QString name= reply.argumentAt<0>();
qDebug()<<"name = "<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 |
---|---|
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");
qDBusRegisterMetaType();
方式二:
利用QByteArray实现自定义类型
class MyClass
{
private:
int count;
QString name;
}
QDataStream & operator<< (QDataStream& stream, const MyClass& myclass)
{
stream<> (QDataStream& stream, const MyClass& myclass)
{
stream>>myclass.count;
stream>>myclass.name;
}
服务端先把数据写入QByteArray。 客户端通过QDataStream把数据从QByteArray读出
编写Adaptor
如果注册对象时,使用QDBusConnection::ExportAllContents, 会导致很多的接口都暴露给用户。 为此, 可以编写Adaptor来控制接口
// adaptor.h
#include
#include
class Adaptor : public QDBusAbstractAdaptor
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "com.brion.interface")
Q_CLASSINFO("D-Bus Introspection", ""
"\n"
" \n"
" "
" \n"
" \n"
" "
" "
" \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
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 |
---|---|
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)
// in 传参
int age() const
// out 返回值
void setName(QString name)
void setNames(QStringList names)
void setNames(QByteArray ba)
// 有待测试
自定义类型
类和结构体, 不允许空结构体
struct MyStruct
{
int key;
QString value;
}
void setCustom(MyStruct my)
void setCustoms(QList
struct MyStruct
{
QMap
}
void setCustom(MyStruct my)
键值队
void setDict(QMap
权限控制
通过配置文件来实现权限控制。
session/system
所有 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 来通信。
参考:
http://blog.csdn.net/mznewfacer/article/details/7475265