作为软件工程师,我有多次在要求完成指定任务时感到浑身一冷的经历。其中有一次,我必须在一些新的硬件基础设施和云基础设施之间写一个接口,这些硬件需要 C 语言,而云基础设施主要是用 Python。
实现的方式之一是 用 C 写扩展模块 ,Python 支持 C 扩展的调用。快速浏览文档后发现,这需要编写大量的 C 代码。这样做的话,在有些情况下效果还不错,但不是我喜欢的方式。另一种方式就是将两个任务放在不同的进程中,并使用 ZeroMQ 消息库 在两者之间交换消息。
在发现 ZeroMQ 之前,遇到这种类型的情况时,我选择了编写扩展的方式。这种方式不算太差,但非常费时费力。如今,为了避免那些问题,我将一个系统细分为独立的进程,通过 通信套接字 发送消息来交换信息。这样,不同的编程语言可以共存,每个进程也变简单了,同时也容易调试。
ZeroMQ 提供了一个更简单的过程:
编写一小段 C 代码,从硬件读取数据,然后把发现的东西作为消息发送出去。
使用 Python 编写接口,实现新旧基础设施之间的对接。
Pieter Hintjens 是 ZeroMQ 项目发起者之一,他是个拥有 有趣视角和作品 的非凡人物。
准备
本教程中,需要:
一个 C 编译器(例如 GCC 或 Clang )
libzmq 库
Python 3
ZeroMQ 的 Python 封装
Fedora 系统上的安装方法:
$ dnf install clang zeromq zeromq-devel python3 python3-zmq
Debian 和 Ubuntu 系统上的安装方法:
$ apt-get install clang libzmq5 libzmq3-dev python3 python3-zmq
如果有问题,参考对应项目的安装指南(上面附有链接)。
编写硬件接口库
因为这里针对的是个设想的场景,本教程虚构了包含两个函数的操作库:
fancyhw_init() 用来初始化(设想的)硬件
fancyhw_read_val() 用于返回从硬件读取的数据
将库的完整代码保存到文件 libfancyhw.h 中:
#ifndef LIBFANCYHW_H
#define LIBFANCYHW_H
#include <stdlib.h>
#include <stdint.h>
// This is the fictitious hardware interfacing library
void fancyhw_init(unsigned int init_param)
{
srand(init_param);
}
int16_t fancyhw_read_val(void)
{
return (int16_t)rand();
}
#endif
这个库可以模拟你要在不同语言实现的组件间交换的数据,中间有个随机数发生器。
设计 C 接口
下面从包含管理数据传输的库开始,逐步实现 C 接口。
需要的库
开始先加载必要的库(每个库的作用见代码注释):
// For printf()
#include <stdio.h>
// For EXIT_*
#include <stdlib.h>
// For memcpy()
#include <string.h>
// For sleep()
#include <unistd.h>
#include <zmq.h>
#include “libfancyhw.h”
必要的参数
定义 main 函数和后续过程中必要的参数:
int main(void)
{
const unsigned int INIT_PARAM = 12345;
const unsigned int REPETITIONS = 10;
const unsigned int PACKET_SIZE = 16;
const char *TOPIC = “fancyhw_data”;
...
初始化
所有的库都需要初始化。虚构的那个只需要一个参数:
fancyhw_init(INIT_PARAM);
ZeroMQ 库需要实打实的初始化。首先,定义对象 context,它是用来管理全部的套接字的:
void *context = zmq_ctx_new();
if (!context)
{
printf(“ERROR: ZeroMQ error occurred during zmq_ctx_new(): %s\n”, zmq_strerror(errno));
return EXIT_FAILURE;
</