主页

索引

模块索引

搜索页面

interoperability [1]

distributed Erlang and ports(linked-in drivers)

distributed Erlang:

erlang manual page in ERTS (describes the BIFs)
global manual page in Kernel
net_adm manual page in Kernel
pg2 manual page in Kernel
rpc manual page in Kernel
pool manual page in STDLIB
slave manual page in STDLIB

When to use:

Ports can be used for all kinds of interoperability situations where the Erlang program and the other program runs on the same machine. Programming is fairly straight-forward.
open_port/2(erlang模块)

Linked-in drivers involves writing certain call-back functions in C. This requires very good skills as the code is linked to the Erlang runtime system.
erl_ddll模块
open_port函数 ->  Ports       -> Erl_Interface
erl_ddll函数  ->  port driver
port driver + c node -> NIFs + c node

Ports:

https://img.zhaoweiguo.com/knowledge/images/languages/erlangs/doc_interoperability_port.gif

文件:port.c:

 1/* port.c */
 2#include <stdio.h>
 3#include <unistd.h>
 4#include <fcntl.h>
 5#include <stdlib.h>
 6#include <sys/types.h>
 7#include <sys/stat.h>
 8#include <string.h>
 9
10
11typedef unsigned char byte;
12
13int read_exact(byte *buf, int len)
14{
15  int i, got=0;
16
17  do {
18    if ((i = read(0, buf+got, len-got)) <= 0)
19      return(i);
20    got += i;
21  } while (got<len);
22
23  return(len);
24}
25
26int write_exact(byte *buf, int len)
27{
28  int i, wrote = 0;
29
30  do {
31    if ((i = write(1, buf+wrote, len-wrote)) <= 0)
32      return (i);
33    wrote += i;
34  } while (wrote<len);
35
36  return (len);
37}
38
39int read_cmd(byte *buf)
40{
41  int len;
42
43  if (read_exact(buf, 2) != 2)
44    return(-1);
45  len = (buf[0] << 8) | buf[1];
46  return read_exact(buf, len);
47}
48
49int write_cmd(byte *buf, int len)
50{
51  byte li;
52
53  li = (len >> 8) & 0xff;
54  write_exact(&li, 1);
55  
56  li = len & 0xff;
57  write_exact(&li, 1);
58
59  return write_exact(buf, len);
60}
61
62int foo(arg) {
63  return (arg * 2);
64}
65
66int bar(arg) {
67  return (arg + 1);
68}
69
70int main() {
71  int fn, arg, res;
72  byte buf[100];
73
74  while (read_cmd(buf) > 0) {
75    fn = buf[0];
76    arg = buf[1];
77    
78    if (fn == 1) {
79      res = foo(arg);
80    } else if (fn == 2) {
81      res = bar(arg);
82    }
83
84    buf[0] = res;
85    write_cmd(buf, 1);
86  }
87}
88
89
90

文件:complex1.erl:

 1-module(complex1).
 2-export([start/1, stop/0, init/1]).
 3-export([foo/1, bar/1]).
 4
 5start(ExtPrg) ->
 6    spawn(?MODULE, init, [ExtPrg]).
 7stop() ->
 8    complex ! stop.
 9
10foo(X) ->
11    call_port({foo, X}).
12bar(Y) ->
13    call_port({bar, Y}).
14
15call_port(Msg) ->
16    complex ! {call, self(), Msg},
17    receive
18  {complex, Result} ->
19      Result
20    end.
21
22init(ExtPrg) ->
23    register(complex, self()),
24    process_flag(trap_exit, true),
25    Port = open_port({spawn, ExtPrg}, [{packet, 2}]),
26    loop(Port).
27
28loop(Port) ->
29  receive
30    {call, Caller, Msg} ->
31      Port ! {self(), {command, encode(Msg)}},
32      receive
33        {Port, {data, Data}} ->
34        Caller ! {complex, decode(Data)}
35      end,
36      loop(Port);
37    stop ->
38      io:format("3:~p~n", [stop1]),
39      Port ! {self(), close},
40      receive
41    {Port, closed} ->
42        io:format("2:~p~n", [Port]),
43        exit(normal)
44      end;
45    {'EXIT', Port, Reason} ->
46      io:format("1:~p~n", [Reason]),
47      exit(port_terminated)
48    end.
49
50encode({foo, X}) -> [1, X];
51encode({bar, Y}) -> [2, Y].
52
53decode([Int]) -> Int.

编译:

unix> gcc -o extprg complex.c erl_comm.c port.c
unix> erl
1> c(complex1).
{ok,complex1}

运行:

2> complex1:start("./extprg").
<0.34.0>
3> complex1:foo(3).
4
4> complex1:bar(5).
10
5> complex1:stop().
stop

Erl_Interface

说明:

基本同于Ports
有以下2个不同点:
1. As Erl_Interface operates on the Erlang external term format, the port must be set to use binaries.
2. Instead of inventing an encoding/decoding scheme, the term_to_binary/1 and binary_to_term/1 BIFs are to be used.

代码:

1.
open_port({spawn, ExtPrg}, [{packet, 2}])
=>
open_port({spawn, ExtPrg}, [{packet, 2}, binary])

2.
Port ! {self(), {command, encode(Msg)}},
receive
  {Port, {data, Data}} ->
    Caller ! {complex, decode(Data)}
end
=>
Port ! {self(), {command, term_to_binary(Msg)}},
receive
  {Port, {data, Data}} ->
    Caller ! {complex, binary_to_term(Data)}
end
for C Nodes
Binary = term_to_binary(Term)
Term = binary_to_term(Binary)

文件complex2.erl:

 1-module(complex2).
 2-export([start/1, stop/0, init/1]).
 3-export([foo/1, bar/1]).
 4
 5start(ExtPrg) ->
 6    spawn(?MODULE, init, [ExtPrg]).
 7stop() ->
 8    complex ! stop.
 9
10foo(X) ->
11    call_port({foo, X}).
12bar(Y) ->
13    call_port({bar, Y}).
14
15call_port(Msg) ->
16    complex ! {call, self(), Msg},
17    receive
18  {complex, Result} ->
19      Result
20    end.
21
22init(ExtPrg) ->
23    register(complex, self()),
24    process_flag(trap_exit, true),
25    Port = open_port({spawn, ExtPrg}, [{packet, 2}, binary]),
26    loop(Port).
27
28loop(Port) ->
29    receive
30  {call, Caller, Msg} ->
31      Port ! {self(), {command, term_to_binary(Msg)}},
32      receive
33    {Port, {data, Data}} ->
34        Caller ! {complex, binary_to_term(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)
45    end.

文件:ei.c:

 1/* ei.c */
 2
 3#include "erl_interface.h"
 4#include "ei.h"
 5
 6typedef unsigned char byte;
 7
 8int main() {
 9  ETERM *tuplep, *intp;
10  ETERM *fnp, *argp;
11  int res;
12  byte buf[100];
13  long allocated, freed;
14
15  erl_init(NULL, 0);
16
17  while (read_cmd(buf) > 0) {
18    tuplep = erl_decode(buf);
19    fnp = erl_element(1, tuplep);
20    argp = erl_element(2, tuplep);
21    
22    if (strncmp(ERL_ATOM_PTR(fnp), "foo", 3) == 0) {
23      res = foo(ERL_INT_VALUE(argp));
24    } else if (strncmp(ERL_ATOM_PTR(fnp), "bar", 3) == 0) {
25      res = bar(ERL_INT_VALUE(argp));
26    }
27
28    intp = erl_mk_int(res);
29    erl_encode(intp, buf);
30    write_cmd(buf, erl_term_len(intp));
31
32    erl_free_compound(tuplep);
33    erl_free_term(fnp);
34    erl_free_term(argp);
35    erl_free_term(intp);
36  }
37}

使用:

% 未验证
unix> gcc -o extprg -I/usr/local/otp/lib/erl_interface-3.9.2/include \\
    -L/usr/local/otp/lib/erl_interface-3.9.2/lib \\
    complex.c erl_comm.c ei.c -lerl_interface -lei -lpthread
unix> erl
1> c(complex2).
{ok,complex2}
2> complex2:start("./extprg").
<0.34.0>
3> complex2:foo(3).
4
4> complex2:bar(5).
10
5> complex2:bar(352).
704
6> complex2:stop().
stop

Port Drivers

说明:

port driver是一种linked-in driver
linked-in driver是作为Erlang程序的一部分被访问
它是shared library (SO in UNIX, DLL in Windows)
它是Erlang调用c代码最快的方式,同时也是最不安全的方式(port driver的崩溃会导致emulator挂掉)
https://img.zhaoweiguo.com/knowledge/images/languages/erlangs/doc_interoperability_portdriver.gif

说明2:

connected process: Erlang进程,所有的交流都通过此进程,终止此进程就关闭此port driver
步骤:
1.截入:通过函数erl_dll:load_driver/1: erl_ddll:load_driver(".", SharedLib)
2.using the BIF open_port/2创建port:   Port = open_port({spawn, SharedLib}, [])
3.

文件port_driver.c:

 1/* port_driver.c */
 2
 3#include <stdio.h>
 4#include "erl_driver.h"
 5
 6typedef struct {
 7    ErlDrvPort port;
 8} example_data;
 9
10static ErlDrvData example_drv_start(ErlDrvPort port, char *buff)
11{
12    example_data* d = (example_data*)driver_alloc(sizeof(example_data));
13    d->port = port;
14    return (ErlDrvData)d;
15}
16
17static void example_drv_stop(ErlDrvData handle)
18{
19    driver_free((char*)handle);
20}
21
22static void example_drv_output(ErlDrvData handle, char *buff, 
23             ErlDrvSizeT bufflen)
24{
25    example_data* d = (example_data*)handle;
26    char fn = buff[0], arg = buff[1], res;
27    if (fn == 1) {
28      res = foo(arg);
29    } else if (fn == 2) {
30      res = bar(arg);
31    }
32    driver_output(d->port, &res, 1);
33}
34
35ErlDrvEntry example_driver_entry = {
36    NULL,     /* F_PTR init, called when driver is loaded */
37    example_drv_start,    /* L_PTR start, called when port is opened */
38    example_drv_stop,   /* F_PTR stop, called when port is closed */
39    example_drv_output,   /* F_PTR output, called when erlang has sent */
40    NULL,     /* F_PTR ready_input, called when input descriptor ready */
41    NULL,     /* F_PTR ready_output, called when output descriptor ready */
42    "example_drv",    /* char *driver_name, the argument to open_port */
43    NULL,     /* F_PTR finish, called when unloaded */
44    NULL,                       /* void *handle, Reserved by VM */
45    NULL,     /* F_PTR control, port_command callback */
46    NULL,     /* F_PTR timeout, reserved */
47    NULL,     /* F_PTR outputv, reserved */
48    NULL,                       /* F_PTR ready_async, only for async drivers */
49    NULL,                       /* F_PTR flush, called when port is about 
50           to be closed, but there is data in driver 
51           queue */
52    NULL,                       /* F_PTR call, much like control, sync call
53           to driver */
54    NULL,                       /* unused */
55    ERL_DRV_EXTENDED_MARKER,    /* int extended marker, Should always be 
56           set to indicate driver versioning */
57    ERL_DRV_EXTENDED_MAJOR_VERSION, /* int major_version, should always be 
58               set to this value */
59    ERL_DRV_EXTENDED_MINOR_VERSION, /* int minor_version, should always be 
60               set to this value */
61    0,                          /* int driver_flags, see documentation */
62    NULL,                       /* void *handle2, reserved for VM use */
63    NULL,                       /* F_PTR process_exit, called when a 
64           monitored process dies */
65    NULL                        /* F_PTR stop_select, called to close an 
66           event object */
67};
68
69DRIVER_INIT(example_drv) /* must match name in driver_entry */
70{
71    return &example_driver_entry;
72}

文件complex5.erl:

 1-module(complex5).
 2-export([start/1, stop/0, init/1]).
 3-export([foo/1, bar/1]).
 4
 5start(SharedLib) ->
 6    case erl_ddll:load_driver(".", SharedLib) of
 7  ok -> ok;
 8  {error, already_loaded} -> ok;
 9  _ -> exit({error, could_not_load_driver})
10    end,
11    spawn(?MODULE, init, [SharedLib]).
12
13init(SharedLib) ->
14    register(complex, self()),
15    Port = open_port({spawn, SharedLib}, []),
16    loop(Port).
17
18stop() ->
19    complex ! stop.
20
21foo(X) ->
22    call_port({foo, X}).
23bar(Y) ->
24    call_port({bar, Y}).
25
26call_port(Msg) ->
27    complex ! {call, self(), Msg},
28    receive
29  {complex, Result} ->
30      Result
31    end.
32
33loop(Port) ->
34    receive
35  {call, Caller, Msg} ->
36      Port ! {self(), {command, encode(Msg)}},
37      receive
38    {Port, {data, Data}} ->
39        Caller ! {complex, decode(Data)}
40      end,
41      loop(Port);
42  stop ->
43      Port ! {self(), close},
44      receive
45    {Port, closed} ->
46        exit(normal)
47      end;
48  {'EXIT', Port, Reason} ->
49      io:format("~p ~n", [Reason]),
50      exit(port_terminated)
51    end.
52
53encode({foo, X}) -> [1, X];
54encode({bar, Y}) -> [2, Y].
55
56decode([Int]) -> Int.

使用:

unix> gcc -o example_drv.so -fpic -shared complex.c port_driver.c
windows> cl -LD -MD -Fe example_drv.dll complex.c port_driver.c
> erl
1> c(complex5).
{ok,complex5}
2> complex5:start("example_drv").
<0.34.0>
3> complex5:foo(3).
4
4> complex5:bar(5).
10
5> complex5:stop().
stop

jinterface:

for Java Nodes

C Nodes: [2]

说明:

A C program that uses the Erl_Interface functions for setting up a connection to, and communicating with, a distributed Erlang node is called a C node, or a hidden node.
从Erlang的视觉看,C node就像是一个erlang进程: {RegName, Node} ! Msg

NIFs

Native Implemented Functions
相比使用port drivers调用c node,NIFs是一种更有效、更简单的方式
但也是最不安全的方式,因为NIF的崩溃会引起emulator挂掉
适用于同步函数,用于做一些相对简单且没有副作用的计算

Erlang Program:

1.The NIF library must be explicitly loaded by Erlang code in the same module.
2.All NIFs of a module must have an Erlang implementation as well.

使用:

erlang:load_nif/2

使用:

unix> gcc -o complex6_nif.so -fpic -shared complex.c complex6_nif.c
windows> cl -LD -MD -Fe complex6_nif.dll complex.c complex6_nif.c
> erl
1> c(complex6).
{ok,complex6}
3> complex6:foo(3).
4
4> complex6:bar(5).
10
5> complex6:foo("not an integer").
** exception error: bad argument
     in function  complex6:foo/1
        called as comlpex6:foo("not an integer")

主页

索引

模块索引

搜索页面