一种编程语言同其他的语言结合互操作,充分利用现有资源,是很常见的事情. 也有特定的编程语言很擅长同其他语言的结合,如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).















Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐