0
点赞
收藏
分享

微信扫一扫

dbus总线通信的原理和使用

Brose 2022-02-14 阅读 128

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:用来指定对应的接口
举报

相关推荐

0 条评论