1.什么是D-Bus
D-Bus是一种高级的进程间通信机制,它由freedesktop.org项目提供,使用GPL许可证发行。D-Bus最主要的用途是在Linux桌面环境为进程提供通信,同时能将Linux桌面环境和Linux内核事件作为消息传递到进程。注册后的进程可通过总线接收或传递消息,进程也可注册后等待内核事件响应,例如等待网络状态的转变或者计算机发出关机指令。目前,D-Bus已被大多数Linux发行版所采用,开发者可使用D-Bus实现各种复杂的进程间通信任务。
2.D-Bus的相关概念
D-Bus是一个消息总线系统,其功能已涵盖进程间通信的所有需求,并具备一些特殊的用途。D-Bus是三层架构的进程间通信系统,其中包括:
接口层:接口层由函数库libdbus提供,进程可通过该库使用D-Bus的能力。
总线层:总线层实际上是由D-Bus总线守护进程提供的。它在Linux系统启动时运行,负责进程间的消息路由和传递,其中包括Linux内核和Linux桌面环境的消息传递。
包装层:包装层一系列基于特定应用程序框架的Wrapper。
Linux发行版都会提供两种Message Bus:System Bus和Session Bus。System Bus 主要用于内核和一些系统全局的service之间通信;Session Bus 主要用于桌面应用程序之间的通信。
dbus的通信结构图如下图所示:
dbus总线通信,通过分层分级的方式实现各种各样的复杂进程间通信。dbus通信分为三层:Service、Object、Interface。
Service
应用程序注册的形如"com.mycompany.myapp"的名字为程序与D-bus总线的连接的名称,用来对应用程序进行定位。相当于对程序的连接起了一个比较好记得地址,类似于网站的域名。应用程序和DBus之间的连接也被称为Service。
Object
当应用程序连接到Message Bus上时,该应用程序可以在Bus上创建多个Object(我们可以把D-Bus的object理解成面向对象语言里的object)。Service通过Object 为其他应用程序提供访问接口。因为在Message Bus上,一个应用程序可以对应多个Object,所以不同的Object必须由Object Path(类似于文件系统的路径)来区分。Object Path的格式如"/component/user"。
Serivce和Object Path之间相互独立,没有任何关联,注册时可以根据需要命名.
Interface
D-Bus通过Signal/Method来发送和接收Message。Signal/Method可以理解为QT中的Signal/Slot这个概念。一个Object可以提供多个Method/Signal,这些Method/Signal的集合又组成了Interface。
因此,D-Bus的这些概念从大到小可以表示为:dbus->Service->Object->[Interface]-> Method/Signal。
在程序之间的通讯过程中,我们通过Service,Object,Interface组合起来,找到信号需要执行的应用程序中的代码。
3.D-Bus在Qt中的使用方法
服务端的程序
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "handler.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
Ui::Widget *ui;
Handler m_handler;
public slots:
void service_get(QString st);
void send_msg();
signals:
void send_to_client(QString);
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
#include "handler.h"
#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusMessage>
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
QDBusConnection bus = QDBusConnection::sessionBus();
//注册名称为domm.test的服务
QDBusConnection::sessionBus().unregisterService("domm.test");
if(!bus.registerService("domm.test"))
{
qDebug() << bus.lastError().message();
exit(1);
}
//注册名路径为/ceshi/registry的Object
//导出对应的槽函数和信号
bus.registerObject("/ceshi/registry", this,
QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals);
//将/ceshi/registry对象com.citos.test2接口下的send_to_service信号
//关联对应的槽函数
QDBusConnection::sessionBus().connect(QString(""),
QString("/ceshi/registry"),"com.citos.test2","send_to_service",
this,SLOT(service_get(QString)));
connect(ui->sendBtn, SIGNAL(clicked(bool)),this ,SLOT(send_msg()));
setWindowTitle("服务端");
}
Widget::~Widget()
{
delete ui;
}
void Widget::service_get(QString st)
{
ui->receive_LineEdit->clear();
ui->receive_LineEdit->setText(st);
}
void Widget::send_msg()
{
QString content = ui->send_LineEdit->text();
//创建针对/ceshi/registry对象中com.citos.test1接口下的
//send_to_client信号
QDBusMessage message = QDBusMessage::createSignal("/ceshi/registry",
"com.citos.test1","send_to_client");
message << content;
QDBusConnection::sessionBus().send(message);
ui->send_LineEdit->clear();
}
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
客户端程序
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QtDBus/QDBusConnection>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void Client_get(QString st);
void Send_Msg();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusMessage>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//将Serivce(domm.test)Object(/ceshi/registry)Interface(com.citos.test2)
//发送的信号send_to_client关联到槽函数Client_get
QDBusConnection::sessionBus().connect(QString("domm.test"),
QString("/ceshi/registry"),"com.citos.test2",
"send_to_client",this,SLOT(Client_get(QString)));
connect(ui->sendBtn, SIGNAL(clicked(bool)),this, SLOT(Send_Msg()));
setWindowTitle("客户端");
}
Widget::~Widget()
{
delete ui;
}
void Widget::Send_Msg()
{
QString msg = ui->sendLineEdit->text();
//发送Object(/ceshi/registry)Interface(com.citos.test2)下的信号
//send_to_service到服务端
QDBusMessage dbusMsg = QDBusMessage::createSignal("/ceshi/registry",
"com.citos.test2", "send_to_service");
dbusMsg << msg;
QDBusConnection::sessionBus().send(dbusMsg);
ui->sendLineEdit->clear();
}
void Widget::Client_get(QString st)
{
ui->ReceiveLineEdit->clear();
ui->ReceiveLineEdit->setText(st);
}
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
4.dbus调试工具
//linux下通过dbus-monitor命令来监测系统的dbus消息
dbus-monitor [--system | --session | --address ADDRESS]
[--monitor | --profile | --pcap | --binary ] [watch expressions]
--system 监控系统的dbus消息
--session 监控所有的会话消息(默认)
--profile 使用简要的输出格式
--monitor 使用监控的输出格式(默认)
//监听特定服务特定对象特定接口发送的特定消息
//监听org.cehsi.info服务,在interface接口下发送的信号
dbus-monitor "type='signal',sender='org.cehi.info',interface='org.cehsi.test'"
type:调用的类型,包括:method_call. signal
sender:用来筛选发送者
interface:用来指定对应的接口