给erlang增加自定义模块 ---- C Port Driver

来源:互联网 发布:众泰知豆和吉利知豆 编辑:程序博客网 时间:2024/05/01 21:54

转载: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

0 0
原创粉丝点击