Erlang 与 c 函数 对接
一种编程语言同其他的语言结合互操作,充分利用现有资源,是很常见的事情. 也有特定的编程语言很擅长同其他语言的结合,如LUA. 在Erlang中也有很多方法同其他的编程语言结合.如通过内链驱动,原生实现函数(NIF)等. Erlang虚拟机中通过一种端口的对象概念同外部程序进行通信,外部程序在端口处取得数据进行处理,处理完成后再送回端口,Erlang虚拟机创建端口的进程会将请求数
一种编程语言同其他的语言结合互操作,充分利用现有资源,是很常见的事情. 也有特定的编程语言很擅长同其他语言的结合,如LUA. 在Erlang中也有很多方法同其他的编程语言结合.如通过内链驱动,原生实现函数(NIF)等.
Erlang虚拟机中通过一种端口的对象概念同外部程序进行通信,外部程序在端口处取得数据进行处理,处理完成后再送回端口,Erlang虚拟机创建端口的进程会将请求数据发到端口,再等待收回结果.所以说这需要一个协议,有了协议不同的语言间就可以交互了.
下面有两个非常简单的c语言函数,准备再erlang中调用:
example1.c
1 int sum(int x,int y){
2
3 return x + y;
4 }
5
6 int twice(int x){
7
8 return x*2;
9 }
定义的规则如下:
1. sum(x,y) 的调用会被编译成字节序列 [1, x, y]
2. twice(x) 的调用会被编译成字节序列[2, x]
3. 所有的数据包都带有两个字节的长度头
4. 返回值,参数都是一个字节长度
有了上述的这几条约定,就应该可以完成简单的函数的调用了.接下来就是双方都按这些规则交换数据就可以了,也就是所谓的driver程序了.
example1_driver.c
<span style="font-size:14px;"> 1 #include <stdio.h>
2 #include <stdlib.h>
3
4 typedef unsigned char byte;
5
6 int main(){
7
8 int function = 0;
9 int arg1 = 0;
10 int arg2 = 0;
11 int result = 0;
12 byte buf[100] = {0};
13
14 while(read_cmd(buf) > 0){
15
16 function = buf[0];
17
18 if(function == 1){
19 arg1 = buf[1];
20 arg2 = buf[2];
21 result = sum(arg1,arg2);
22 } else if(function == 2){
23 arg1 = buf[1];
24 result = twice(arg1);
25 }else{
26 exit(0);
27 }
28
29
30 buf[0] = result;
31 write_cmd(buf,1);
32 }
33 } </span>
上面是C语言端的实现逻辑,它会不断的从标准输入取得计算的数据,计算之后在输出到标准输出中,这里所谓的标准输入输出,也就是Erlang的port. Erlang的端口进程会负责启动这个C程序,同时对它进行管理,Erlang端口进程不在了,也会杀掉这个C程序, 当这个C程序意外的挂掉之后,Erlang也会负责重新启动它. 这里面还涉及到两个实现的具体函数,不会影响整个逻辑. 贴出来如下:
<span style="font-size:14px;"> 1 #include <unistd.h>
2
3 typedef unsigned char byte;
4
5 int read_cmd(byte *buf)
6 {
7 int len;
8
9 if(read_exact(buf,2) != 2)
10 return -1;
11
12 len = (buf[0] << 8 | buf[1]);
13
14 return read_exact(buf,len);
15 }
16
17 int write_cmd(byte *buf,int len)
18 {
19 byte li;
20
21 li = (len >> 8) & 0xff;
22 write_exact(&li,1);
23 li = len & 0xff;
24 write_exact(&li,1);
25
26 return write_exact(buf,len);
27 }
28
29 int read_exact(byte *buf,int len){
30 int i = 0;
31 int got = 0;
32
33 do {
34 if((i = read(0,buf + got, len-got)) <= 0)
35 return i;
36 got = got + i;
37 }while(got < len);
38
39 return len;
40 }
41
42 int write_exact(byte *buf,int len){
43 int i = 0;
44 int wrote = 0;
45
46 do{
47 if((i = write(1,buf + wrote,len - wrote)) <= 0)
48 return i;
49 wrote = wrote + i;
50 }while(wrote < len);
51
52 return len;
53 } </span><span style="font-size:18px;"> </span>
Erlang 这边同样是按照规则实现函数调用:
example1.erl
<span style="font-size:14px;"> 1 -module(example1).
2
3 -export([start/0,stop/0]).
4 -export([twice/1,sum/2]).
5
6 start() ->
7 register(example1,spawn(
8 fun() ->
9 process_flag(trap_exit,true),
10 Port = open_port({spawn,"./example1"},[{packet,2}]),
11 loop(Port)
12 end)).
13
14
15 stop() ->
16 ?MODULE ! stop.
17
18 twice(X) -> call_port({twice,X}).
19 sum(X,Y) -> call_port({sum,X,Y}).
20
21 call_port(Msg) ->
22 ?MODULE ! {call, self(), Msg},
23 receive
24 {?MODULE,Result} ->
25 Result
26 end.
27
28 loop(Port) ->
29 receive
30 {call, Caller, Msg} ->
31 Port ! {self(),{command,encode(Msg)}},
32 receive
33 {Port,{data,Data}} ->
34 Caller ! {?MODULE,decode(Data)}
35 end,
36 loop(Port);
37 stop ->
38 Port ! {self(),close},
39 receive
40 {Port,closed} ->
41 exit(normal)
42 end;
43 {'EXIT',Port,Reason} ->
44 exit({port_terminated,Reason})
45 end.
46
47 encode({sum,X,Y}) -> [1,X,Y];
48 encode({twice,X}) -> [2,X].
49
50 decode([Int]) -> Int.
</span>
下面的问题就是编译,试一试了:
erlc example1.erl
gcc -o example1 example1.c example_driver.c erl_comm.c
example1:start().
example:sum(23,43).
example:twice(10).
更多推荐
所有评论(0)