linux OS 下的轻量级微内核及其在边缘设备中的应用

现在国内工业App 的概念很热,不过我看到许多号称工业App的东西都只不过是windows 下传统的工业应用程序而已,例如CAD软件,热分析软件,齿轮设计软件等等。甚至将传统的工业进销存软件换个马甲出来蹭热度来了。到底什么是工业App,如果将传统的工业应用软件称为工业App,那就有点牵强,也毫无意义。笔者认为,所谓App,它沿用了移动终端的概念,代表了部署使用非常便捷,客户体验好,能够在各种同类机器上安装。一般是指在某个特定软硬件平台上运行的小程序。例如Android,ipad ,iphone上的App。

所以说,开发真正意义上的工业App,首先得有一个设备平台,例如,GE 公司的predix就是这样一个平台。以前我们介绍的Harting 公司的mica 也是一个小型的平台。现在工业App 平台还处于发展初期,还没有一个平台足以吸引大量第三方开发者为其开发App 的程度。微软,研化,西门子,Arm等公司都在研发中。

我们一直研究工业边缘设备的相关软硬件技术,在新设计的边缘设备modular2Edge 中,采用了docker 容器技术来部署和管理App,实现了原来云端应用的环境,但是总觉得运行在docker 中的App 如何相互信息交换,如何统一的访问IO子系统,这些问题并没有解决。工业App 还需要更好的生态。于是,我们最近探索性地设计了一个类似操作系统微内核的基础服务层base Service。实现App 间的消息交换和IO 子系统的访问。

本文分享baseservice 设计初期的一些想法,并且在不断地完善中。希望得到一些意见。

    工业App vs 手机App  

    工业App 比手机中的App 更加复杂。 相比手机App而言,它们有如下不同:

1 使用多种程序设计语言来编写应用软件

它们不会像手机App那样,单一地使用一种计算机语言来开发,例如android 搞了一个Davlik虚拟机,使用java来编写App,比如 python 更适合编写数据分析和可视化软件,而nodeJS,nodeRED 适合编写web 服务器程序和人机界面(HMI)。

2 支持更多的通信协议

工业App将连接更多的物理设备,使用更多的通信协议。比如与传感器通信使用TCP/IP,modbus,canbus fieldbus 等网络和协议。与云端服务器之间可能采取HTTP RestFull API,websocket或者其它一些协议。

3 更多的协同运行

       App 相互之间的协同和消息交换会比手机App 更多。比如一个App 完成数据采集,另外的App 实现数据存储,可视化。以及人工智能算法和数据分析。

4 要求更高的实时性

工业App 实时性要求高。

     如此看来,不同App 的消息传递,协同操作以及管理,和多处理器的异种机互联十分相像。

LinuxOS 下部署工业App 的方式。普遍采用了docker容器技术来管理App 程序,但是提供App之间的信息交换机制也非常重要,这种交换机制实现下面的功能:

1 App 与底层硬件,物理设备,基础服务的访问。.

2  App 之间的相互协同操作。

      

现在市面上的边缘设备普遍采用轻量级的消息系统MQTT作为信息交换工具。例如GE 公司的predix 就是采纳MQTT来实现所谓的数据总线(这里沿用了硬件的总线概念,其实就是一个消息交换机制,微软Azure 也有类似的称呼 )。在我们初步的实验中发现,MQTT 作为短数据,低频发的物联网应用是可以的。但是应对工业物联网应用而言,并不是一个好的选择。传输效率并不高。云端操作系统往往是牺牲效率,提高可维护性和灵活性。而工业边缘设备的效率也十分重要。

在本文我们探讨如何使用操作系统微内核的思想,为工业App开发一个基于远程过程调用(RPC)的消息交换机制,达到简化工业App的信息交换和物理设备访问。我们称这个模块为基础服务(base service)。

 

微内核思想

微内核(Microkernel)的概念是由Richard Rashid在卡内基梅隆(Carnegie-Mellon)大学开发Mach操作系统时提出的,目标是建立一个基于消息传送(message passing)机制的最小内核,以便在此基础上建造对其它操作系统的模拟层来模拟其它操作系统的特性。

微内核的功能被划分为独立的过程,每个过程叫做一个服务器。所有的服务器都保持独立并运行在各自的地址空间。因此,就不可能像单模块内核那样直接调用函数,而是通过消息传递处理微内核通信:系统采用了进程间通信(IPC)机制,因此,各种服务器之间通过IPC机制互通消息,互换“服务”。

微内核设计带来了良好的兼容性、扩充性、灵活性、移植性、可靠性和网络支持。

  相比之下,传统的操作系统内核称为单内核(Monolithic kernel)。内核就是把它从整体上作为一个单独的大过程来实现,并同时运行在一个单独的地址空间。因此,这样的内核通常以单个静态二进制文件的形式存放于磁盘。所有内核服务都在这样的一个大内核空间中运行。内核之间的通信是微不足道的,因为大家都运行在内核态,并身处同一地址空间:内核可以直接调用函数,这与用户空间没有什么区别。这种模式的支持者认为单模块具有简单和高性能的特点。大多数Unix系统都设计为单内核。

  微内核将功能分为多个服务,服务之间通过RPC 交换信息的模式,与容器化的工业App 的运行机制非常相似。因此我们在设计工业App 运行平台base service 时,采纳了微内核的思想。

这样做的好处是:

  1.      对各种硬件的访问抽象成 base service 的RPC 方法。
  2.     所有的App 之间的相互协同,消息传递归纳成单一的baseservice RPC 方法。
  3.     通过App 可以扩展baseservice 的RPC 方法。

App 程序统一成RPC 方式,简化的程序设计的复杂性。也为各种App 的相互协同操作,理清了头绪。

工业App 的信息交换方式

 典型的工业App 的信息交换方式有下面三种:

   1 Request/Respond 模式

     这种方式类似于传统程序库API 的方式。由调用者发出Request,被调用的函数执行相应的运算和操作。然后给出相应的响应。在松耦合系统中,这种调用是通过某种协议来实现的。所有在很久之前,就出现的基于TCP/IP的远程过程调用(RPC)方法。

   2 流数据(stream data)模式

     类似于视频流,调用方发送启动后,被调用方连续地发送数据给调用方,直到发送停止stop。这种方式在工业App 中比较常见。

  3 异步事件模式

     当某个硬件,服务或者App 产生一个事件后,主动发送的消息。这类似于硬件中断。不是依靠轮询同步来读取状态,而是事件发生时发出。

对应的消息交换协议

  对应于上面的三种信息交换方式,常用的网络协议由下面三种:

http Restfull API

这是典型的client/server 协议,在网站中普遍使用。baseservice 支持http Restfull API 访问服务。

websocket

在web 上实现了双向TCP协议,baseservice 主要采用websocket 通信协议。

mqtt

这是一种异步轻量级消息系统。baseservice 采用MQTT 实现事件的通知协议。比如当某一个告警信号发生时,可以通过mqtt 发布一个topic 消息。

RPC 协议

   在base service 中,使用了基于websocket 的RPC 协议,为了满足上述三种数据格式。我们对传统RPC 协议做了扩充。它们包括下面几类:

    method only 模式

     没有返回值的方法

    method-result 模式

普通的RPC 模式,一个方法,一个返回值

    multi-result模式

数据流模式,一个方法,多个返回值。

   method -mqtt 模式

MQTT 模式,一个方法,结果通过MQTT 发送。

 

jsonrpc协议

   baseservice PRC 的信息交换格式采用jsonrpc 格式。

jsonrpc 是一种轻量级远程过程调用协议。讲起来它非常简单,就是使用json 描述RPC 过程的数据格式。它并没有规定传输方式,所以可以在TCP,HTTP,Websocket MQTT 各种传输层实现。

例如:

-> {"jsonrpc":"2.0","id":1,"method":"SUM",“params”:{“value”:[600,1]}}

<- {"jsonrpc": "2.0", "id": 1, "result":{“value”:601}

在具体实现过程中,jsonrpc 的某些参数需要进一步规定。

method-result 的简单模式是一个method,对应一个result, 但是对于流数据模式而言。一次启动method ,会有多个 result 产生。这种情况问题会变得复杂一点,但是依然可以利用jsonrpc 格式来实现。这时变成为 multi-result 模式的RPC。例如下图:

 

  Base service 的实现

我们的基本想法是在传统单内核linux上设计一个轻量级的微内核架构的服务层,实现不同App 之间的RPC 调用和访问IO 子系统。 我们称为base service。该系统使用C++ 实现。

主要功能

BaseService实现如下功能

1 websocket 服务器

2 基于websoket 的RPC 转发队列和管理

3 IO之系统访问服务

4 http restfull API

5 MQTT 客户端

   BaseService软件采用C++ 语言开发。使用了restbed,rapidjson ,paho mqtt client 等开源程序库。

 

  硬件资源访问服务

App 之间的消息交换.

主要数据结构

RPC注册表(rpc_regster)

记录了当前所有可用的rpc ,包括了 base service 提供的 rpc 过程和其它App 提供的过程。

struct prc_register

{

string name;//方法的名称

string owner;//方法的拥有者socket Key

int method_index ;//方法索引值

int type ;//方法的类型

} plist[];

plist 记录了所有可用的RPC 方法。

owner 是提供该服务的websocket 的key,如果是base service 提供的服务,owner 为“base”

method_index 方法的索引值,如果是base 的服务,按顺序分别为1到n。如果是外部App 提供的服务,该值为0

type  方法的类型 

      -0  method-only 模式

     -1  method-result 模式

     -2   multi result 模式

    -3 result to mqtt 模式

 

rpc 队列

该队列存放是当前运行的rpc 调用。当app 申请是 将RPC 插入rpc 队列,当rpc 执行结束,base service 收到 结果帧时从该队列去除。

我们并没有让完整的rpc 消息进入队列,而是直接转发到websocket 的队列中去了。只是将参数进入rpc 队列。这一做会使效率更高。

rpc_queue  {

string caller_key   //调用者socket  Key

string callee_key  //被调用者socket Key

int  income_id   //进入id

int  outging_id  //转出 id

int  timeout    //超时计数器

} rpc_queue[]

caller_key 是调用者的websocket key

callee_key 是被调用者的websocket key,如果被调用者是base service 下的IO 子系统,callee_key 为“base”。

远程过程调用RPC 的操作流程

    baseservice 以及IO 子系统提供了一组固定的内部rpc 方法,当base service 开始运行时,填入 rpc register 表(plist),外部App 提供的RPC 方法需要预先注册到baseService 中,注册时将有关信息填入rpc register 表(plist)

      method一旦注册之后,别的App 就可以远程调用这个服务了。所有的APP是向base service 调用该服务。base service 实现rpc 的转发。这样做的好处是 App 的服务扩展了base service 的服务。对于App 而言,base service 就是一个能够提供丰富服务的OS。而其中的大部分服务是由其它App 提供的。

id 的处理方式

    base service 根据owner key, income_id 和outgoing_id 来管理prc 队列的。所以对于ID 有具体的要求。

    每个端口发送的ID 是一个 0 到 5000 的循环数。

int get_going_id()
 {
     base_id=base_id+1;
     if (base_id>5000) base_id=0;
     return base_id;    
 }

这样做的目的是设计了一个发送保护窗口。

如前所述,对于流数据而言,一个启动方法,会有多个结果。如此一来,启动方法的id 可能会长期占用,甚至于永久占用。这一的话,就有可能会超越一个窗口周期(其它调用的id 超越 5000)这一就会造成混乱。

 对于流数据而言,start 的id 需要长期保留。具体实现时,我们设计了一个reverse_id_table, 当启动数据流 时,将它的income id 保留起来,直到stop 才去掉。获取id 时,如果该id 在保留id 表内,就跳跃过去。

rpc 队列的处理程序:

  

remove
int remove_queue_by_income_id(string callee_key,int income_id)
int remove_queue_by_outgoing_id(string callee_key,int outgoing_id)
// find
rpc_queue_entry find_rpc_by_outgoing_id(string key,int outgoing_id)
rpc_queue_entry find_rpc_by_caller_id(string key,int caller_id)
//insert
rpc_queue_entry queue_entry={key,income_id,going_id,0};
rpc_queue.insert(make_pair(callee_key,queue_entry));

一些 jsonrpc 的格式规定

register 注册RPC 

 var jsonrpc={
  "jsonrpc":"2.0",
  "method":"register",
  "params":{"name":method_name,type:1 },
  "id": get_outgoing_id()
 }

mult-result RPC 调用方式

  var jsonrpc={
                  "jsonrpc":"2.0",
                  "method":method_name,
                  "params":{"start":1,"value":val},
                  "id": id
                }

start=1 ,表示启动。

start=0 表示结束。

IO 子系统的固定RPC

   为了调试基本的系统,我们设计了一部分IO子系统的固定RPC ,以后可以在config.json 中定义,或者动态注册到系统中。

 

#define MAX_method 6
#define app_rpc_request        0
#define app_rpc_Result         1
#define app_rpc_Register       2
#define digitalOut_Write  	   3
#define digitalOut_Read    	   4
#define digitalOut_Flipflop    5
#define analog_Start           6
#define interruptin_Start      7
#define app_PRC_Result         0x80
#define AnalogOut              0x85
#define DigitalIn              0x86

IO 子系统

  linux OS 的运行平台支持的硬件外围接口是十分有限的。而且linux 对外围接口的支持也比较少。为了增加系统的灵活性和可移植性。我们的系统采用cortex-M 系列单片机作为外围接口的协处理器。cortex-M 单片机采用标准的IO接口与主处理器连接,比如采用uart 或者USB 接口通信。在最新的设计中,我们采用ethernet 与主处理器连接,多个cortex-M 时使用边缘设备内部的以太网交换机与主机连接。硬件架构如下:

为了提高IO系统与主机交换信息的效率,我们采纳了UDP 与主处理器通信。

UDP 包格式

 UDP package {

uint8_t method,

uint8_t pin,

uint16  id,

uint16 length,

uint8_t data[length],

}

 外设地址

外设地址(pin)为16位整型数。高八位位IO 模块编号,低八位为模块内的外设地址。

 系统的测试

  1. 为了方便地测试base service。我们在baseservice 中设计了一个测试网页,启动两个浏览器访问网页,模仿两个App。
  2.  baseservice 在windows 的WSL ubuntu 下运行。
  3. PC 机通过交换机连接两台 STM32F746 Nucleo  开发板。分别仿真了 Digital write ,InterruptIn,ADC等外设。 

目前baseservice的程序已经初步调通,一次rpc 调用的延时在 1~2 ms,调用IO子系统,大约 5~7ms。

总结

    本项目初步表明:在一个单内核Linux 上实现一个轻量级的微内核架构的消息交换层,能够简化容器化App 之间的RPC 调用和访问IO 子系统。初步实验表明 这一想法是可行的。还有许多问题可以思考:

   1    这样的架构是否可以用于云端?

   2  是否要为App 编写库,更加方便于App 对baseservice的访问?

  3 如何提高效率和响应时间。是否可以将 base service 单独部署在一个 处理器的核内?

如果你也恰巧对这个课题感兴趣,请多交流。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值