Qt扫盲-Qt D-Bus概述

news2025/1/11 18:37:40

Qt D-Bus概述

  • 一、概述
  • 二、总线
  • 三、相关概念
    • 1. 消息
    • 2. 服务名称
    • 3. 对象的路径
    • 4. 接口
    • 5. 备忘单
  • 四、调试
  • 五、使用Qt D-Bus 适配器
    • 1. 在 D-Bus 适配器中声明槽函数
      • 1. 异步槽
      • 2. 只输入槽
      • 3. 输入输出槽
      • 4. 自动回复
      • 5. 延迟回复

一、概述

D-Bus是一种进程间通信(IPC)和远程过程调用(RPC)机制,最初是为Linux开发的,用一个统一的协议取代现有的和竞争的IPC解决方案。它还被设计为允许系统级进程(如打印机和硬件驱动程序服务)和普通用户进程之间的通信。

它使用快速的二进制消息传递协议,由于其低延迟和低开销,适合于同机通信。它的规范目前由 freedesktop.org 项目定义,所有各方都可以使用。

通信通常通过称为“总线”(因此得名)的中央服务器应用程序进行,但也可以直接进行应用程序到应用程序的通信。当在总线上通信时,应用程序可以查询哪些其他应用程序和服务可用,以及按需激活其中一个。

二、总线

D-Bus总线用于需要多对多通信的情况。为了实现这一点,在任何应用程序可以连接到总线之前启动一个中央服务器:该服务器负责跟踪连接的应用程序,并负责将消息从源路由到目的地。

此外,D-Bus还定义了两个众所周知的总线,称为系统总线和会话总线。这些总线的特殊之处在于它们具有良好定义的语义:一些服务被定义为可以在这些总线中的一个或两个中找到。

例如,希望查询附加到计算机上的硬件设备列表的应用程序可能会与系统总线上可用的服务通信,而提供打开用户web浏览器的服务可能会在会话总线上找到。

在系统总线上,还可以找到关于允许每个应用程序提供哪些服务的限制。因此,可以合理地确定,如果存在某个服务,它是由受信任的应用程序提供的。

三、相关概念

1. 消息

在底层,应用程序通过D-Bus相互发送消息进行通信。消息用于转发远程过程调用以及与之相关的应答和错误。当在总线上使用时,消息有目的地,这意味着它们只被路由到感兴趣的各方,避免了由于“蜂群”或广播造成的拥塞。

然而,有一种特殊类型的消息称为“信号消息”(基于Qt的信号和槽机制的概念),它没有预定义的目的地。由于其目的是在一对多上下文中使用,因此信号消息被设计为通过“选择加入”机制工作。

Qt D-Bus模块将消息的低级概念完全封装为Qt开发人员熟悉的更简单的面向对象方法。在大多数情况下,开发人员无需担心发送或接收消息。

2. 服务名称

当通过总线进行通信时,应用程序获得所谓的 “服务名称” :这是该应用程序选择被同一总线上的其他应用程序所知道的方式。通信总得知道是谁收谁发嘛。服务名称由D-Bus总线守护进程代理,用于将消息从一个应用程序路由到另一个应用程序。与服务名称类似的概念是IP地址和主机名:一台计算机通常有一个IP地址,并可能有一个或多个与之关联的主机名,这取决于它向网络提供的服务。

另一方面,如果不使用总线,也不会使用服务名称。如果我们再次将其与计算机网络进行比较,这将等同于点对点网络:由于对等点是已知的,因此不需要使用主机名来查找它或它的IP地址。

D-Bus服务名称的格式实际上与主机名非常相似:它是由点分隔的字母和数字序列。常见的做法甚至是根据定义该服务的组织的域名来命名服务名称

例如,D-Bus服务是由freedesktop.org定义的,可以在总线上的服务名称下找到它:

 org.freedesktop.DBus

就像 deepin中的部分代码用到的这种服务名称,也是用的域名表示方式,java中的很多包名也用的域名表示

QDBusConnection sessionBus = QDBusConnection::sessionBus();
if (!sessionBus.interface()->isServiceRegistered("com.deepin.daemon.InputDevices")) {
	qDebug() << "DBusError" << "com.deepin.daemon.InputDevices";
return;
}

// 监控全局鼠标信号。
sessionBus.connect("com.deepin.daemon.InputDevices",
	"/com/deepin/api/XEventMonitor", "com.deepin.api.XEventMonitor", "ButtonPress",
	this, SLOT(ButtonPressEvent(int, int, int, QString)));

3. 对象的路径

与网络主机一样,应用程序通过导出对象向其他应用程序提供特定的服务。这些对象是分层组织的,很像从QObject派生的类所拥有的父子关系。然而,一个不同之处在于存在 “根对象” 的概念,所有对象都有作为最终父对象的根对象。
如果我们继续对Web服务进行类比,对象路径等同于URL的路径部分:
在这里插入图片描述

与它们类似,D-Bus中的对象路径的形式类似于文件系统中的路径名称:它们是由斜杠分隔的标签,每个标签由字母、数字和下划线(“_”)组成。它们必须总是以斜杠开始,不能以斜杠结束。

4. 接口

接口类似于c++抽象类和Java的interface关键字,并声明了在调用者和被调用者之间建立的“契约”。也就是说,它们建立了可用的方法、信号和属性的名称,以及在建立通信时期望的任何一方的行为。

Qt在它的插件系统中使用了一个非常相似的机制:c++中的基类通过Q_DECLARE_INTERFACE()宏与一个唯一标识符相关联。

实际上,D-Bus接口名称的命名方式类似于Qt插件系统所建议的:一个标识符通常由定义该接口的实体的域名构造而成。

5. 备忘单

为方便记忆命名格式及其用途,可使用下表:

d - bus概念类比名字的格式
Service name 服务名称网络主机名点分隔(“看起来像主机名”)
Object path 对象路径URL路径组件斜杠分隔(“看起来像一条路径”)
Interface 接口插件标识符以圆点分隔

四、调试

在开发使用D-Bus的应用程序时,有时能够查看每个应用程序通过总线发送和接收的消息的相关信息是很有用的。

通过在运行每个应用程序之前设置QDBUS_DEBUG环境变量,可以在每个应用程序的基础上启用该特性。例如,在D-Bus遥控汽车示例中,我们可以通过以下方式运行控制器和汽车,使能仅对汽车进行调试:

 examples/dbus/remotecontrolledcar/controller/controller &
  QDBUS_DEBUG=1 examples/dbus/remotecontrolledcar/car/car &

有关消息的信息将写入启动应用程序的控制台中。

五、使用Qt D-Bus 适配器

适配器是附加到任何qobject派生类的特殊类,并使用D-Bus提供与外部世界的接口。适配器旨在成为轻量级类,其主要目的是中继对真实对象的调用和来自真实对象的调用,可能会验证或转换来自外部世界的输入,从而保护真实对象
与多重继承不同,适配器可以随时添加到任何对象(但不能删除),这在导出现有类时提供了更大的灵活性。适配器的另一个优点是在不同接口中的同名方法中提供类似但不相同的功能,在向对象添加新版本的标准接口时,这种情况非常常见。

为了使用适配器,必须创建继承QDBusAbstractAdaptor的类。由于这是一个标准的qobject派生类,Q_OBJECT宏必须出现在声明中,源文件必须用moc工具处理。类还必须包含一个带有“D-Bus Interface”名称的Q_CLASSINFO条目,声明它要导出的接口。每个类只支持一个条目。

类中的任何公共槽都可以通过MethodCall类型的消息通过总线访问。(更多信息请参见声明D-Bus适配器的槽函数)。课堂上的信号将通过D-Bus自动传递。然而,并非所有类型都允许信号或槽的参数列表:参见Qt D-Bus类型系统了解更多信息。

此外,使用Q_PROPERTY声明的任何属性都将通过D-Bus上的Properties接口自动公开。由于QObject属性系统不允许非可读属性,因此不可能使用适配器声明只写属性。

1. 在 D-Bus 适配器中声明槽函数

D-Bus适配器中的槽函数就像普通的公共槽函数一样被声明,但是它们的参数必须遵循一定的规则(参见Qt D-Bus类型系统了解更多信息)。如果槽函数的参数不符合这些规则,或者槽函数不是公共的,那么D-Bus将无法访问这些槽函数。

槽可以有一个const QDBusMessage &类型的参数,它必须出现在输入参数列表的末尾,在任何输出参数之前。如果存在此参数,将使用正在处理的当前消息的副本初始化,这允许被调用方获取有关调用方的信息,例如其连接名称。

槽有三种类型:

  • 异步槽
  • 只输入槽
  • 输入和输出槽

1. 异步槽

异步槽通常不向调用者返回任何应答。因此,它们不能接受任何输出参数。在大多数情况下,到运行槽的第一行时,调用方函数已经恢复工作。

然而,槽函数不能依赖于这种行为。调度和消息调度问题可能会改变槽函数运行的顺序。打算与调用者同步的代码应该提供自己的同步方法。

异步槽由方法签名中的关键字Q_NOREPLY标记,位于void返回类型和槽名之前。D-Bus Complex乒乓示例中的quit()槽函数就是这样的一个例子。

2. 只输入槽

仅输入槽是普通槽,它接受按值或常量引用传递的参数。然而,与异步槽不同,调用方通常在恢复操作之前等待被调用方完成。因此,非异步槽函数不应该阻塞,或者应该在文档中说明它们可能会阻塞。

只有输入的槽在它们的签名中没有特殊的标记,除了它们只接受通过值或常量引用传递的参数。槽可以选择将QDBusMessage参数作为最后一个参数,该参数可用于执行对方法调用消息的附加分析。

3. 输入输出槽

与仅输入槽一样,输入-输出槽是调用者等待回复的槽。但是,与只输入的应答不同,此应答将包含数据。输出数据的槽可能包含非常量引用,也可能返回一个值。但是,输出参数必须全部出现在参数列表的末尾,并且输入参数不能交错。可选地,QDBusMessage参数可能出现在输入和输出参数之间。

4. 自动回复

方法应答是由Qt D-Bus实现与输出参数(如果有的话)的内容一起自动生成的。槽函数不需要考虑构造适当的QDBusMessage对象并通过连接发送它们。

然而,这样做的可能性仍然存在。如果槽发现它需要发送一个特殊的回复,甚至是一个错误,它可以在QDBusMessage参数上使用QDBusMessage::createReply()或QDBusMessage::createErrorReply(),并使用QDBusConnection::send()发送它。如果槽函数这样做了,Qt D-Bus实现将不会生成任何应答。

警告:当调用者调用方法并等待应答时,它将只等待有限的时间。打算花费很长时间来完成的槽函数应该在文档中明确这一事实,以便调用者适当地设置更高的超时。

5. 延迟回复

在某些情况下,被调用的槽可能无法立即处理请求。当请求涉及可能阻塞的I/O或网络操作时,这种情况经常发生。
如果是这种情况,slot应该将控制返回给应用程序的主循环,以避免冻结用户界面,并在以后恢复该进程。要实现这一点,它应该利用输入参数列表末尾的额外QDBusMessage参数,并请求延迟回复。

为此,我们编写一个将请求数据存储在持久结构中的槽,并使用QDBusMessage::setDelayedReply(true)向调用者指示稍后将发送响应。

  struct RequestData
  {
      QString request;
      QString processedData;
      QDBusMessage reply;
  };

  QString processRequest(const QString &request, const QDBusMessage &message)
  {
      RequestData *data = new RequestData;
      data->request = request;
      message.setDelayedReply(true);
      data->reply = message.createReply();
      QDBusConnection::sessionBus().send(data->reply);

      appendRequest(data);
      return QString();
  }

需要使用QDBusConnection::sessionBus().send(data->reply)来显式地通知调用者响应将被延迟。在这种情况下,返回值并不重要;我们返回一个任意值来满足编译器。

当请求被处理并且应答可用时,应该使用获得的QDBusMessage对象发送应答。在我们的示例中,回复代码可能如下所示:

  void sendReply(RequestData *data)
  {
      // data->processedData has been initialized with the request's reply
      QDBusMessage &reply = &data->reply;

      // send the reply over D-Bus:
      reply << data->processedData;
      QDBusConnection::sessionBus().send(reply);

      // dispose of the transaction data
      delete data;
  }

在这个例子中可以看到,当延迟应答到位时,从槽返回的值将被Qt D-Bus忽略。它们仅用于在与远程应用程序通信适配器描述时确定槽函数的签名,或者在槽函数中的代码决定不使用延迟应答的情况下。

延迟的回复本身是通过调用QDBusMessage::reply()从Qt D-Bus请求的。然后,被调用的代码负责最终向调用方发送应答。

警告-当调用者调用方法并等待应答时,它将只等待有限的时间。打算花费很长时间来完成的槽函数应该在文档中明确这一事实,以便调用者适当地设置更高的超时。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1654616.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第29章-SR技术概述

1. SR技术的产生背景 2. SR技术的基本概念 3. SR技术的基本原理 1. SR技术的产生背景 1.1 传统的路由器设备因其转发性能较低 ① 最长匹配算法的缺点&#xff0c;需要遍历整个路由表&#xff1b; ② 早期路由器多采用通用CPU进行转发处理&#xff0c;性能有限&#xff1b; ③…

第七届机电、机器人与自动化国际会议(ICMRA 2024)即将召开!

第七届机电、机器人与自动化国际会议&#xff08;ICMRA 2024&#xff09;将于2024年9月20日-22日在中国武汉举行。ICMRA 2024为各国专家学者提供一个学术交流的平台&#xff0c;讨论机电、机器人和自动化领域的最新研究成果和未来的研究方向&#xff0c;旨在能够建立起国家间&a…

(五)JSP教程——response对象

response对象主要用于动态响应客户端请求&#xff08;request&#xff09;&#xff0c;然后将JSP处理后的结果返回给客户端浏览器。JSP容器根据客户端的请求建立一个默认的response对象&#xff0c;然后使用response对象动态地创建Web页面、改变HTTP标头、返回服务器端地状态码…

【Redis7】了解Redis

1.常见数据库 1.1.键值存储数据库 如 Map 一样的key-value 对&#xff0c;典型代表就是 Redis。 1.2.列存储数据库 关系型数据库是典型的行存储数据库&#xff0c;按行存储的数据在物理层面占用的是连续存储空间&#xff0c;不适合海量数据存储。而按列存储则可实现分布式存储&…

新火种AI|马斯克聘用OpenAI泄密者,他们的梁子着实越结越深...

作者&#xff1a;小岩 编辑&#xff1a;彩云 就在最近&#xff0c;昔日就职于OpenAI的工程师Pavel Izmailov正式加入了马斯克的AI团队&#xff0c;他还在自己的推特上大张旗鼓的做着宣传&#xff1a;研究院xai。 AI工程师的跳槽本不值得惊讶&#xff0c;但Pavel的跳槽却在行…

什么牌子的洗地机质量最好?四款耐用高分产品推荐

洗地机具备了吸尘、擦拭、除菌等多种功能&#xff0c;可以一次完成多种清洁任务&#xff0c;帮助用户更高效地保持家居整洁&#xff0c;节省时间和精力&#xff0c;备受人们的喜爱。但是怎么挑选到优质的洗地机一直是大家关注的问题。今天&#xff0c;笔者将结合自己在家电行业…

mac通过termius连接Linux服务器

mac上安装 linux系统 如果有 linux服务器账号密码&#xff0c;那么上一步可忽略&#xff1b; 比如&#xff1a;直接连接阿里云或腾讯云账号 1. 安装termius 链接: https://pan.baidu.com/s/1iYsZPZThPizxqtkLPT89-Q?pwdbw6j 提取码: bw6j 官网 Termius - SSH platform for …

Linux-信号保存

1. 概念 进程执行信号的处理动作&#xff0c;称为 信号递达&#xff08;Delivery&#xff09; 信号从产生到递达之间的状态&#xff0c;称为 信号未决&#xff08;Pending&#xff09; 进程可以选择 阻塞&#xff08;Block&#xff09;某个信号 过程&#xff1a; 信号产生 ——…

flowable一对并发网关跳转的分析

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://218.75.87.38:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; h…

hadoop学习---基于Hive的聊天数据分析报表可视化案例

背景介绍&#xff1a; 聊天平台每天都会有大量的用户在线&#xff0c;会出现大量的聊天数据&#xff0c;通过对聊天数据的统计分析&#xff0c;可以更好的对用户构建精准的用户画像&#xff0c;为用户提供更好的服务以及实现高ROI的平台运营推广&#xff0c;给公司的发展决策提…

二进制,八进制,十六进制转十进制 c++

紧接着十进制转二进制&#xff0c;八进制&#xff0c;十六进制-CSDN博客这篇文章 输入一个二进制&#xff0c;八进制的数&#xff0c;怎样能转化为十进制呢&#xff1f; 原理如下&#xff1a; K进制转十进制 按权相加法展开成一个多项式&#xff0c;每项是该位的数码与相应…

Maven+Junit5 + Allure +Jenkins 搭建 UI 自动化测试实战

文章目录 效果展示Junit 5Junit 5 介绍Junit 5 与 Junit 4 对比PageFactory 模式编写自动化代码公共方法提取测试用例参数化Jenkins 搭建及配置参数化执行生成 Allure 报告Maven 常用命令介绍POM 文件效果展示 本 chat 介绍 UI 自动化测试框架的搭建: 运用 page factory 模式…

高速开箱机价格与性能解析:如何挑选适合您的开箱解决方案?

随着电商和物流行业的迅猛发展&#xff0c;高效、自动化的包装设备成为了提升工作效率、减少人工成本的必备利器。高速开箱机作为其中的重要一环&#xff0c;其性能与价格成为了许多企业和个人关注的焦点。星派将深入探讨高速开箱机的价格与性能之间的关系&#xff0c;帮助您在…

基于springboot+vue+Mysql的口腔管理平台

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

Linux无root配置Node,安装nvm

1. 安装NVM&#xff1a; curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash # 或者&#xff0c;如果你使用wget wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash 对于bash用户&#xff0c;可以运行&…

时序图详解

1.这是iic总线在回应时候的时序图&#xff0c;data in代表eeprom收到数据&#xff0c;回stm32的ack&#xff0c;数据回应&#xff0c;data out代表stm32收到eeprom的消息&#xff0c;数据输出ack回应 2.交叉线 代表在这一次输出高电平&#xff0c;或者在这一次也可能输出低电…

《武林秘籍》——闪侠惠递如何让消费者寄快递更安心!

现如今&#xff0c;网上下单寄快递的便利性让众多人享受到了电商物流飞速发展带来的红利性。今天小编直接介绍一款寄快递特别省钱的利器&#xff0c;就是利用闪侠惠递来寄快递。闪侠惠递寄快递&#xff0c;真正的实现了便宜寄快递发物流的便捷性&#xff0c;开创了低价发快递的…

Hypack 2024 简体中文资源完整翻译汉化已经全部完成

Hypack 2024 简体中文资源完整翻译汉化已经全部完成 Hypack 2024&#xff0c;资源汉化共翻译11065条。毕竟涉及测绘、水文、疏浚等专业术语太多&#xff0c;翻译有很多理解不正确的地方&#xff0c;望各位专业人员指正。 压缩包内包含Hypack 2024、Hypack 2022、Hypack 2021、…

企业如何用数字化为预提摊销业务赋能?

对于企业来说&#xff0c;想要实现系统化、智能化、自动化的预提摊销管理&#xff0c;需要做足哪些功课&#xff1f;常见场景下的业务难题又该如何破解&#xff1f;今天胜意科技就给大家介绍一下&#xff0c;企业如何通过数字化手段搞定预提摊销业务难题。 一、预提摊销痛点 在…

Java 三大特性之继承

目录 一、为什么需要继承&#xff1f; 二、继承概念 三、继承的语法 四、子类访问父类成员 五、super关键字 六、继承关系下的构造方法 七、继承关系下的初始化 八、protected关键字 九、继承的三种方式 十、final关键字 十一、继承和组合 一、为什么需要继承&#…