Programming Erlang 第12章笔记 接口技术
接口技术
译者: | gashero |
---|
假设我们需要以Erlang接口运行以C/Python/Shell编写的程序。想要实现这些,我们需要在一个单独的操作系统进程中运行这些程序,而不是在Erlang运行时系统中,他们之间以面向字节的通道完成通信。Erlang端通过 Port 控制。创建端口的进程成为端口的连接进程。连接进程拥有特殊意义:让所有来自扩展程序的消息都会以连接进程的PID来标志。所有的扩展程序消息都会发送到连接进程。
我们可以看看连接进程(C)与端口(P)和扩展操作系统进程的关系。
从程序员的角度,端口就像是Erlang进程。你可以发送消息给它,你可以注册(register)它,等等。如果扩展程序crash了,那么连接程序就会收到退出信号,如果连接进程死掉了,扩展进程也会被kill掉。
你可能会好奇与为什么要这么做。很多编程语言允许其他语言编写的程序连接到可执行文件。而在Erlang,我们为了安全性不允许这么做。如果我们连接一个扩展程序到Erlang可执行程序,那么扩展程序的错误将会轻易的干掉Erlang。所以,其他语言编写的程序必须以单独的操作系统进程来运行。Erlang运行时系统和扩展进程通过字节流通信。
1 端口
创建端口使用如下命令:
Port=open_port(PortName,PortSettings)
这会返回端口,而如下消息是会被发送到端口的(这些消息中PidC是连接进程的PID):
Port ! {PidC,{command,Data}} :发送数据到端口
Port ! {PidC,{connect,Pid1}} :改变控制进程的PID,从PidC到Pid1
Port ! {PidC,close} :关闭端口
连接进程会从扩展程序收到如下消息:
receive {Port,{data,Data}} -> ... 数据处理 ...
下面的节,我们会编写Erlang与C结合的简单例子。C程序尽可能的简单以避开细节,直接谈接口技术。
注意,下面的例子对接口机制和协议做了加亮。编码和解码复杂的数据结构是个困难的问题,这里没有谈及。在本章末尾,我们会指出一些用于其他编程语言的接口库。
2 扩展C程序的接口
我们先从C程序开始:
int twice(int x) { return 2*x; } int sum(int x, int y) { return x+y; }
我们最终目标是从Erlang调用这些例程,我们希望看到的是这个样子(Erlang中):
X1=example1:twice(23), Y1=example1:sum(45,32),
与用户的设置有关,example1是一个Erlang模块,所有与C接口的细节都被隐藏在了模块example1中。
我们的接口需要一个主程序,用来解码Erlang程序发来的数据。在我们的例子,我们首先定义端口和扩展C程序的协议。我们使用一个超级简单的协议,并展示如何在Erlang和C中实现。协议定义如下:
- 所有包都以2字节的长度代码开头,后面跟着这些字节的数据。
- 想要调用 twice(N) ,Erlang程序必须以特定形式编码函数调用。我们假设编码是2字节序列 [1,N] ;参数1表示调用函数 twice ,后面的N代表一个1字节的参数。
- 调用 sum(N,M) ,我们编码请求到字节序列 [2,N,M] 。
- 假设返回值都是单一的字节长度的。
扩展C程序和Erlang程序都必须遵守这个协议。作为例子,我们通过 sum(45,32) 来看看工作流程:
- 端口发送字节序列 0,3,2,45,32 到扩展程序。头两个字节0,3,表示包的长度是3,代码2表示调用扩展的 sum 函数,而 45 和 32 表示调用函数的参数。
- 扩展程序从标准输入(stdin)读取这5个字节,调用 sum 函数,然后把字节序列 0,2,77 写到标准输出(stdout)。前两个字节表示包长度,后面的77是结果(仍然是1字节长度)。
我们现在写在两端遵守协议的接口,先以C程序开始。
2.1 C程序
@page 215