转载:http://kasicass.blog.163.com/blog/static/39561920107264842832/
从官方抄过来的例子,代码流,并讲解需要注意的地方。
http://www.erlang.org/doc/tutorial/c_portdriver.html
erlang 把自定义模块叫做 driver。
自定义模块与 erlang process 在同一进程:(速度快)
erl proc <----> port <----> YourMod
不同进程:(安全)
erl proc <-----> port <--- pipe ---> YourMod
--------------------------------------------------------------------
%% 实现一个模块,把 Port 的访问封装下
-module(complex5).
-export([start/1, stop/0, init/1]).
-export([foo/1, bar/1]).
start(SharedLib) ->
case erl_ddll:load_driver(".", SharedLib) of
ok -> ok;
{error, already_loaded} -> ok;
{error, Desc} ->
io:format("~p~n", [Desc]),
exit({error, could_not_load_driver})
end,
spawn(?MODULE, init, [SharedLib]).
init(SharedLib) ->
register(complex, self()),
Port = open_port({spawn, SharedLib}, []),
loop(Port).
stop() ->
complex ! stop.
loop(Port) ->
receive
{call, Caller, Msg} ->
Port ! {self(), {command, encode(Msg)}},
receive
{Port, {data, Data}} ->
Caller ! {complex, decode(Data)}
end,
loop(Port)
end.
encode({foo, X}) -> [1, X];
encode({bar, Y}) -> [2, Y].
decode([Int]) -> Int.
foo(X) ->
call_port({foo, X}).
bar(Y) ->
call_port({bar, Y}).
call_port(Msg) ->
complex ! {call, self(), Msg},
receive
{complex, Result} ->
Result
end.
--------------------------------------------------------------------
#include <stdio.h>
#include <erl_driver.h>
typedef struct {
ErlDrvPort port;
} example_data;
static ErlDrvData
example_drv_start(ErlDrvPort port, char *buff)
{
example_data *d = (example_data *) driver_alloc(sizeof(example_data));
d->port = port;
return (ErlDrvData) d;
}
static void
example_drv_stop(ErlDrvData handle)
{
driver_free((char*)handle);
}
static void
example_drv_output(ErlDrvData handle, char *buff, int bufflen)
{
example_data *d = (example_data *)handle;
char fn = buff[0], arg = buff[1], res;
if ( fn == 1 )
res = arg + 1;
else if ( fn == 2 )
res = arg * 2;
driver_output(d->port, &res, 1);
}
ErlDrvEntry example_driver_entry = {
NULL,
example_drv_start,
example_drv_stop,
example_drv_output,
NULL,
NULL,
"exampledrv",
NULL,
NULL,
NULL,
NULL
};
DRIVER_INIT(exampledrv) // 注意:这里的名字 exampledrv,要和上面 example_driver_entry 中的 "exampledrv" 名字一样
{
return &example_driver_entry;
}
--------------------------------------------------------------------
gcc -o exampledrv.so -I/usr/local/lib/erlang/usr/include/ -fpic -shared port_driver.c
--------------------------------------------------------------------
1> c(complex5).
{ok,complex5}
2> complex5:start("exampledrv").
<0.38.0>
3> complex5:foo(10).
11
4> complex5:bar(20).
40
--------------------------------------------------------------------
我这里演示的是
同一进程 的情况。
需要注意的是,erl_ddll:load_driver() 的模块名,是不包含 .so 后缀的,但实际的动态库文件需要 .so 后缀,否则返回 {error, {open_error, -11}}。
而且,模块名 exampledrv 要与动态库文件名一致,否则返回:bad_driver_name