==============以下是废话,可以跳过==============
这个想法起源于楼主参加校队的电赛集训时遇到的一个练习题,需求大致是从ADC获取信息,处理之后发给电脑或者手机上的客户端软件。
我最开始粗略构想的架构是,写一个c程序从zynq的XADC暴露到iio子系统的文件读取数据,然后通过管道和一个web服务器IPC,web服务器再和手机或电脑上的客户端通讯。
电赛这几年测量题的一个趋势好像就是逐渐向这种物联网的方向发展。做电赛的人都知道,实际比赛的时间非常紧张,正式比赛时一般是把平时准备好的硬件模块拼起来。为了节省时间,软件层面上也应该采取同样的方法。软件的模块化,一提起来很多人就想到库。但除非是都写成c库然后供其他带FFI的语言调用,不然不同语言之间想重用模块是比较困难的。但面对电赛现在的趋势,软件能用多种语言构建才更为理想:性能要求高的数据处理部分用C写,物联网服务器的部分用更高级的语言实现。那么有没有一种办法能让不同语言写成的模块像拼硬件模块一样任意组合呢?
==================进入正题==================
先说一下命名管道这个东西。管道大家都不陌生,它能把上一个命令的标准输出重定向到下一个命令的标准输入上。命名管道和它差不多,不过你可以给他起个名字,然后他就像一个文件一样以那个名字存在在他被创建的目录下面。比如说在一个终端里输入
mkpipe tst
创建了一个名字叫tst的管道。然后打开另一个终端,cd到前一个终端打开的目录下,就能看到一个叫tst的文件,然后输入
cat < tst
把tst这个管道重定向给cat命令的标准输入,这时候回到上一个终端,输入
echo "t" > tst
这时候第二个终端会显示一个t。
几乎任何语言写的程序都能访问标准输入和输出,只要预先约定好一种协议和编码,就能利用命名管道实现不同语言构建的程序之间的通信。此所谓语言中立。当然这个事普通的管道操作符也能干,而且这样实现的通信是一条链,没法分叉。如果我想根据协议约定好的某个字段,把信息送到不同的程序那里呢?
有计算机网络知识的人都知道有个5层(或者7层)模型,数据从底层往上层传,每经一层丢掉一个报头。那一层也就是根据这个报头知道这个数据包该往后一层的哪个地方(哪个MAC,哪个IP,哪个port)传。我们也可以效仿这种做法,在传输的数据中加入报头。
为了解析报头,需要编写一个新的程序,我姑且称之为路由器(router)。这个程序,也是个普通的命令,接受两个参数。第一个参数 l 表示这一层的报头有多长,第二个参数是变长参数,或者说剩余参数,是一列命名管道的名字。每一层的报头存储的就是这列名字中的其中一个,router把报头读出来,然后把丢掉报头的信息发到匹配的那个命名管道里。这些命名管道连接着这一层待选择的不同的程序。router的输入也是标准输入,于是它本身也能被命名管道和上一层的程序连起来。
这样的一个多层的体系看起来是这样的:
数据生产者
命名管道
路由器
命名管道1 ... 命名管道i
1层中间件1 1层中间件2 ... 1层中间件i
命名管道
路由器
命名管道1 ... 命名管道j
2层中间件1 2层中间件2 ... 2层中间件j
命名管道
...
路由器
命名管道1 ... 命名管道k
n层中间件1 n层中间件2 ... n层中间件k
命名管道
路由器
命名管道1 ... 命名管道h
数据消费者1 ... 数据消费者h
比方说你写了个很复杂的通讯程序,生产者接收到一个字符串,需要过滤内容,压缩大小,编码之后再用从一个web服务器发走。过滤器有很多种,压缩算法有很多种,编码方式也有很多种,假定这些都是生产者知道的,于是它产生这样一段数据:
[过滤器id[压缩器id[编码器id[web服务器id[字符串载荷]]]]]
你可能希望用python来写几个不同的过滤程序,调用bzip2或者zstd等不同的压缩程序,用C++写几个不同的编码器。这都没有问题,只用创建一系列名字和那一层的中间件的种类对应的命名管道,然后每一层的路由器会根据报头把数据传给对应的中间件,最后传给服务器。
每次读完管道中的内容以后,操作系统就会把读端的程序挂起,直到有新数据写入管道。也就是说只有生产者产生了新信息,后续的程序才会动作。此所谓事件驱动。
命名管道的通讯是单向的。一般而言,上行和下行数据的处理方式是不一样的,所以需要为两个方向上的数据流分别实现这样一个结构。
不知道这种想法有没有已经在应用了,或者有更好的办法实现相同的目的,就先发出来供大家拍砖讨论。
离线