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:¶
文件: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挂掉)
说明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")