> 文档中心 > 从IPC到分布式软总线的随笔

从IPC到分布式软总线的随笔

在Linux 系统中, 客观来说,缺乏相对开发者比较友好的进程通信框架。谈到Linux上进程间通信,一般都会想起管道(匿名、有名)、信号/信号灯、共享内存、消息队列和socket。这些都是偏低层的技术,有没有方便开发者使用的技术或者框架呢?软件总线以及分布式软总线或许是一种不错的候选。

Linux 中的进程间通信一瞥

Linux环境下通信机制众多,各种通信方式都有其适用的场合。

管道是Linux支持的最初Unix IPC机制之一,是实现方法最简单的一种通信机制。但是,只能以半双工的形式在进程间进行通信。

信号是多种通信机制中唯一一种异步方式进行通信的机制。信号方式通信传输的数据量较少,侧重于控制进程根据不同的信号触发不同的行为。

消息队列是在内核中开辟的一组链表,以队列的形式接收和发送信息,适用于传输数据量较少的场合。消息队列与管道通信相比,其优势是对每一个消息可以指定特定消息类型,接收的时候不需要按队列次序,而是可以根据自定义条件接收特定类型的消息。但在消息信息的发送进程—操作系统内核和内核—接收进程间复制时需要额外占用CPU的时间。

共享内存通信机制在进程间可以传送大量的数据,并且由于读写进程均将共享内存块视为本进程所有,可直接读写,因此传输速度最快。但由于多个进程对共享内存块的访问必须以互斥形式进行,因此还需要信号量机制予以配合。

信号量机制通过信号量值的变化来控制多进程互斥的访问共享资源,或者协调多个进程并发执行的节奏,并未在进程之间实际的传输数据。

基于 Socket 的进程间通信机制是现在所有网络操作系统必不可少的基础功能,大多数现代进程间通信框架都是基于Socket 完成的。

久远一点的DCOP

大约从KDE2.0 开始,都包含了非常强大的部件,叫做“Desktop COmmunication Protocol”,简称为 DCOP,从开发者的角度来看,利用 DCOP 可以很方便地将强大的脚本功能添加到应用程序中。从用户的角度来看,利用 DCOP 可以容易地控制 KDE 应用程序,并可以将它们以强大而有趣方式组合起来。

6f25bf91d1c94dc454707087dda81bd9.jpeg

就其本质而言,DCOP 是一个操作于socket之上的轻量级进程间通信机制,由一个服务器(即 dcopserver,它在 KDE 启动时会自动启动)和任意多个客户端(支持 DCOP 的应用程序)构成。DCOP客户段之间可以通过服务器互相发送消息,要求执行函数,等等。

kdebindings 软件包中含对 Java 的 Qt/KDE 绑定,可以在 Java 中使用 Qt/KDE 类,还包括对 C、Perl、Python 的绑定,也可以在这些语言中使用 DCOP,还包括了 XParts,将非 KDE 应用程序作为一个 KPart 嵌入使用。

DCOP 一般用于动态管理Linux运行时软件配置框架,一般的Linux软件在运行时读取配置文件后,所有的参数不可再次调整,而Dcop能够在启动软件后,再次根据需求去配置软件各项参数。

普遍使用的D-Bus

现如今,在Linux 中使用广泛的D-Bus 又是什么呢? 

从IPC到分布式软总线的随笔

D-Bus是一个有面向对象接口的协议框架,以及应用程序用户互相发现和监视的守护进程。也就是说,这是一个进程间的通讯系统,是由两个守护进程,一个是系统范围,一个是用户会话范围,提供了生命周期内的跟踪、服务激活、安全检查等高级功能。这样的守护进程可以启动服务以便给其它程序提供某些功能。D-Bus 可以看作DCOP的升级版,比DCOP要复杂一些,而且DCOP主要用作桌面应用之间的通信。

但是,D-bus 也不是一个普遍适用的通讯系统,这一点和Corba等明显不同。在设计之初,D-Bus 设计被用来作为用户交互接口与系统服务之间的解耦和通信,以及系统服务之间的通信。对于 D-Bus而言,由于不信任对端发来的数据,一定要做复杂的校验,导致比直接使用socket读写数据慢2.5倍,甚至比DCOP乃至Corba等通讯机制都要慢一些。

Corba,又是一个久远的存在,20多年前的Corba 实现Orbit都要比D-Bus快,Corba和D-Bus都使用了二机制的通讯协议,但Corba 更通用和开放。然而,D-Bus很多地方都是硬编码,所以D-Bus要比Corba简单得多。

DCOM 是Windows 下的IPC系统,类似于Corba,由于老码农已经多年不涉及Windows 平台的软件开发了,也不知道现在发展到怎样的程度了。

面向嵌入式的ubus

OpenWrt 提供的ubus,类似于Linux桌面系统的D-Bus,目标也是提供系统级的进程间通信功能。在设计理念上基本一致,但与D-Bus相比减少了内存空间的占用,可以更适合嵌入式Linux低内存和低CPU性能的特殊环境。

314a62b1facc156d092d7b4cf769f039.jpeg

ubus是OpenWrt的RPC工具大约是在2011年加入OpenWrt中的。为了提供各种后台进程和应用程序之间的通信机制,ubus模块由3部分组成:

  • ubusd精灵进程。

  • ubus接口库

  • ubus命令行工具

ubus模块的核心是ubusd精灵进程,在系统启动时运行,负责进程间的消息路由和传递。其他进程注册到 ubusd进程进行消息的发送和接收,这个接口是用L文件socket和TLV收发消息来实现的。每一个进程在指定命名空间下注册自己的路径。每一个路径都可以提供带有各种参数的多个函数处理过程,函数处理程序可以在完成处理后返回消息。

ubus提供的功能主要有以下4个方面:

  • 提供注册对象和方法供其他实体调用。

  • 调用其他应用程序提供的注册对象的控制接口。

  • 在特定对象上注册监听事件。

  • 向特定对象发送事件消息。

ubus主要用于两个进程之间的通信,能够以JSON格式和用户进行数据交换,不用关心消息的实际传输格式。ubus代码基于LGPL2.1发布,在OpenWrt 12.09版开始正式使用。

面向内核环境的KDBUS

kdbus是在内核里实现的D-Bus,可传输大数据块乃至GB级的消息流,可做到消息传递的零拷贝,在最坏情况下,一条消息及其回复过程不超过2次拷贝,2次验证和2次上下文切换。全部的凭据信息(用户ID,进程ID,cgroup信息,权限等)随每个消息传递,而且所有消息都有时间戳。

kdbus在内核中作为一个字符设备,先要open设备,再调用mmap()将一个消息传递区域映射到自己的地址空间。消息在这个区域组装后交给内核传输,内核简单地将消息从一个进程映射的区域拷贝到另一个进程的区域。一般地,kdbus通过memfd机制实现消息传递的零拷贝。memfd是一块带有文件描述符的内存区域,可以被“密封”,即拥有它的进程不能再改变其内容。要传递一条消息,进程先在memfd区域构造消息,密封,然后交给kdbus传输。内核可以把相应的内存页面映射到接收进程的地址空间,从而避免拷贝数据,这取决于消息的大小。消息比较小时内存映射的开销比较大,这时是直接拷贝数据。消息还可以携带对收到回复的时间限制(“方法调用窗口”)。

从IPC到分布式软总线的随笔

由于处于内核中,kdbus随时可用,不需要类似D-Bus那样的守护进程启动,Linux安全模块可以直接与其挂钩,可避免竞态条件,API也得到了简化。另外,kdbus信号广播机制采用布隆过滤器来选择接受者,也提高了广播的效率。

遗憾的是, kdbus 曾经试图合入到主流发行版的内核中,但好像没有成功,前景又让人有点捉摸不透了。

面向分布式系统的FDBUS

FDBus提供了分布式的进程间通信机制,支持跨主机的C/S通信,使用服务名而非物理地址作为寻址方式,通过各种服务和心跳重连机制确保连接的动态性和可靠性,进而保证系统内的节点可以动态增删与部署,可以任意重启,无需管理启动顺序和依赖,通信各方都能保持连接,从而把各个分立的模块组成一个牢固的整体。

8c0d522830734ca194e10895f8e8228a.jpeg

从进程间通信视角来看,FDBus和的D-Bus类似,但功能更齐全,性能更高,使用更便利,除了支持主机内的IPC,还能在多个主机之间组网。FDBus构建于socket(Unix和TCP)之上,采用protocol buffer来支持各种复杂的数据类型,也支持raw data格式,便于大量数据传输。FDBus 采用IDL来定义接口并支持自动代码生成,大大降低序列化和反序列化工作,而且支持安全策略,对访问区划分了安全等级,确保整个系统的安全性。

FDBus支持字符串形式的名字作为server地址,通过类似DNS作用的name server自动为server分配Unix domain地址和TCP端口号,实现client和server之间以服务名称进行寻址。其高性能主要体现在点对点直接通信,不通过中央Hub或Broker进行转发,目前已经在Windows,Linux和QNX上得到了验证。作为C/S模式,支持如下通信模式:

  • 带超时的同步请求/应答

  • 带超时的异步请求/应答

  • 无应答的命令请求

  • 订阅模式,实现多点广播

FDBus不仅仅是IPC机制,也是一个中间件开发框架,包含开发中间件过程中经常用到的公共组件和基础模型,提供了跨平台且功能强大的支持。源码开放之后,FDBus经过更多开发者的使用、测试和改进,逐渐成为众多中间件开发框架的候选之一。

xBus 与软件总线

除了早期的DCOP,上面的几种进程间通信机制都命名为xBus,为啥呢?在计算机领域,Bus 一词最早出现在硬件架构中, 代表总线,是一组能为多个部件分时共享的公共信息传送线路。

从IPC到分布式软总线的随笔

从总线所处的位置来看,分为片内总线和片外总线。片内总线是CPU内部的寄存器、算术逻辑部件、控制部件以及总线接口部件之间的公共信息通道,片外总线则泛指CPU与外部器件之间的公共信息通道,我们谈到的总线一般指的是片外总线。

从总线传输方式的设计视角来看,计算机总线有串行总线和并行总线,可以由一个或多个通道组成,每个通道是单线连接,数据的传输方式将根据通道的数量而有所不同。

从通信用途的视角来看,总线又可以分为3种:地址总线、数据总线、控制总线。地址总线用于指定CPU将要操作的内存地址;数据总线用于读写内存的数据,控制总线用于发送和接受信号,比如中断、设备复位等信号,CPU收到信号后进行响应,这时则需要控制总线。

在CPU、内存与外设确定的情况下,计算机的总线速度是制约计算机整体性能的关键。

计算机的软件总线是一种虚拟的存在,它是在计算机硬件总线的功能含义类比的基础上得到的定义。软件总线是软件工程人员为了进一步保证软件系统建设的规范性,以及提高计算机系统的应用价值而提出的一种设计理念。软件总线可以将各种软件进行相互连接,组成一个通用的操作平台,通常表现为一个接口界面。作为一种软件模块,软件总线为各个软件组成部分进行准确的数据传输,同时为各种软件提供虚拟共享的通道和接口。

软件总线源于分布式异构环境的搭建所提出的,软件复用、构件化以及面向对象技术的发展促进了它的形成。软件总线只是对软件的构件进行组装而不是更改,这不仅有效的提高了软件开发的工作效率,缩短了软件开发的周期。前面提到的各种xBus,都可以看作软件总线的一种实现。

HarmonyOS的分布式软总线

鸿蒙的分布式软总线是为了解决所有1+8+N设备之间的互联互通问题,在华为提出的1+8+N中:1指的是手机,8指的是车机、音箱、耳机、手表/手环、平板、大屏、PC、AR/VR,N指的是其他IOT设备。一般情况下,用户都是通过手动操作的方式进行设备之间的连接,随着外围设备越来越多,手动操作的方式不方便,甚至会影响用户的体验。HarmonyOS的分布式总线技术是为了能够让所有的设备之间能够方便、高效的互联。

从IPC到分布式软总线的随笔

HarmonyOS分布式软总线最主要的功能包括:发现、连接、组网/拓扑管理、任务总线、数据总线。其中,”发现”指的是搜索周围是否有相关设备;”连接”指的是与所发现的设备建立连接;”组网/拓扑管理”指的是对所有发现的设备进行网络拓扑管理,比如组成星状网络拓扑,或者是组成Mesh网络拓扑。”任务总线”指的是在所建立的网络拓扑基础上,用于传输小数据量信息的通路。”数据总线”指的是用于传输较大数据量信息的通路。发现与组网是分布式软总线的核心术,目前没有开到公开的细节信息。

把众多外围设备连接形成网络后,需要保证各个设备在时间上的同步。尤其是IoT设备,由于成本方面的原因,晶振的质量可能比较差,会存在相对较大的频率漂移。分布式软总线关键技术之一是时钟同步算法,将不同设备原本不同步的时钟做到统一同步。在软时钟算法的同步下,就可以进行资源的调度。软总线技术中提出了LaneHub的概念,可以理解为调度管理各通信通路的模块。通过软总线的LaneHub可以对这不同连接方式的设备进行统一调度,达到减少干扰、提升速率的目的。

在分布式软总线的基础上,华为提出了”超级终端”的概念,就是通过分布式软总线技术将手机外围的其他相关设备连接在一起,形成了所谓的”超级终端”,即个体终端变成了群体终端。

一句话小结

尽管“一切程序都会归于系统调用”,但软件工程的效率提升是业界不变的追求,从进程间通信到分布式软总线也是如此,或许,基于FDBUS就可以相对容易地开发出类似HarmonyOS的分布式软总线呢。

【参考资料与关联阅读】

  • https://gitee.com/Janisa/Dcop/

  • http://dbus.freedesktop.org/doc/dbus-faq.html

  • https://github.com/skawu/fdbus

  • https://git.openwrt.org/project/ubus.git

  • https://developer.harmonyos.com/

  • https://www.bilibili.com/video/BV16b4y1h75z?spm_id_from=333.999.0.0 

  • 软件开发中的10个认知偏差

  • 软件依赖的一知半解

  • 一文读懂 Data Mesh

  • 组件化与服务化的辨析

  • 软件架构的10个常见模式

  • 关于软件开发,都应该知道的10个常识

  • 服务计算的思考

  • 神经网络中常见的激活函数

  • 算法与模型的浅析

  • 从隐私到隐私计算

  • 一文读懂“语言模型”

  • 一文读懂中间件