进程间通信一例
文章目录
背景
我最近在折腾blockly4pi,这是一个教育项目,致力于将编程带入到基础教育,通过使用blockly,我们将原子操作封装为积木块,学生只需要操作积木块就能做到程序能做到的事
已经封装为积木的原子操作,除了blockly提供的基本编程要素(循环、条件、逻辑等),我们还积木化了虚拟角色的操作以及硬件操作。
硬件的积木化不只是对硬件的操作积木化,硬件本身也被设计为积木
用户通过拖拽web试验平台拖拽积木块,将生成对应程序代码(我目前选择生成python,其实js也是很棒的选择),之后这些代码在树莓派里运行以操控硬件。
我们的硬件积木与树莓派之间采用wifi通信,硬件积木自然是李老师做的(对李老师有兴趣的可以参考我之前的文章),目前的原型机便是文章开头的那张图
我们之前为了省心采用了类似http(实际是socket)的通信机制(请求-答复-中断)来做树莓派和积木节点之间的通信,虽然架构简单,不过带来了一个麻烦,实时性不高的问题,近期重新调整为长链接
问题
我们最初放弃长链接的一个原因是:对硬件积木的操作指令是用户生成的,用户每次在web界面搭建好积木点击运行,树莓派里新起python进程。如此一来长连接就不能建立在动态生成的脚本里。因为脚本是动态执行的,随时可能因为用户新的提交而中断(执行新的).
思路
于是我想到用ipc(进程间通信)来解决这个问题: 在树莓派中使用两个进程来做这件事(分别以进程一和进程二表示):
- 用户在web页面拖拽积木块生成代码,之后代码作为脚本运行在树莓派中,此为进程一,它是动态的 (为了保证清晰和教学方便,只允许在单次提交中使用多线程,不允许多次脚本并行)
- 进程二运行在后台(不中断),负责与硬件节点保持长连接,该进程同时作为server,等待进程二传递的控制硬件节点的消息,同时将消息传递给硬件积木
也就是说进程二是用户动态生成的脚本与硬件积木的中间人,使用这个中间人的目的是保持长链接不中断
从数据的视角来看,进程一只是个管道,它只负责传递消息,管道本是静态的,我们使用载荷来承载变化的消息
实现
一旦进入实现部分,岔路就多了,尽管可能都通往罗马,但有些路荆棘遍布,有些路一马平川,如何选择,感觉有时候比分析重要,这可能是工程问题中偏艺术的一环
就我而言,在不熟悉的领域,我便好走人多的一条路,即便迷路的话,指路人也多
尽管进程间通信的方法很多,我选择Socket(套接口),Socket为目前Linux上最为广泛使用的一种的进程间通信机制,与其他的Linux通信机制不同之处在于除了它可用于单机内的进程间通信以外,还可用于不同机器之间的进程间通信
代码
考虑到这部分可能对其他同学也有帮助,我将这部分的具体实现放过来(只展示原理)
进程二为
|
|
进程一与进程二通信部分为:
|
|
从这里我们可以看到,我们实际把中断进行了转移,把与远程主机的中断移到了本地。如此一来延迟变得非常小
扫尾
细心的小伙伴可能会看到,我们这里没有做掉线重连、启动顺序、错误处理之类的机制,这些保证健壮性的工作我放到源码里,有兴趣的同学可以自行阅读
需要说明的是等待机器上线后连接
部分的代码我用的是《python network programming cookbook》中第三章的源码,周末逛书店看到这部分代码很漂亮,就直接拿过来用,替换了我之前自己写的
FAQ
进程二中的server可以使用web server吗
可以的,不采用的原因是web server比socket啰嗦太多
为何不用zeromq
我其实偏好zeromq,关于zeromq的优点,我之前笔记里又记录:消息队列中间件学习笔记 ,zeromq 是个野心勃勃而欣欣向荣的项目
没用它的主要原因是,硬件开发者,包李老师,偏好用socket
尽管我可以在进程二与硬件积木中使用socket连接(迁就硬件这边),在进程一与进程二之间使用zeromq,不过获得的好处并不明显,还导致了不一致,感觉不划算
2017-04-29补充
目前能想到使用zeromq的一个场景是:多个用户能一起用浏览器来控制我们的硬件积木(我们在此只关注通信部分,其他方面暂不关注) ,也就是说需要1-N连接(1是server,N是client),直接使用REQUEST/RESPONSE模型就行
这里给个示例代码(照抄这里):
客户端
|
|
服务端:
|
|
client可以在server没有启动的时候上线(不会报错),只要服务端上线就不会丢消息,如此一来启动问题变得十分轻松,不必保证server先上线
其次client可以有多个,server能同时处理它们,不会引起混乱
单是以上两点就省下我们许多工作
下一步打算如何改进
使用websocket,目前已经在开发环境里用了,具体原因之后有机会说
参考
文章作者 种瓜
上次更新 2017-04-28