系列:
Erlang/Elixir: 外部通訊之-NIF
Erlang/Elixir: 外部通訊之-端口驅動
Erlang/Elixir: 外部通訊之-C節點linux
本文闡述瞭如何使用rebar3
來構建一個簡單的NIF(內建函數)git
讓咱們從建立一個新的名爲test_nif
的lib
, 使用test_nif
項目中的cmake
模板開始shell
$ rebar3 new lib test_nif ===> Writing test_nif/src/test_nif.erl ===> Writing test_nif/src/test_nif.app.src ===> Writing test_nif/rebar.config ===> Writing test_nif/.gitignore ===> Writing test_nif/LICENSE ===> Writing test_nif/README.md $ cd test_nif $ rebar3 new cmake ===> Writing c_src/Makefile
設置鉤子segmentfault
{erl_opts, [debug_info]}. {deps, []}. {pre_hooks,[ {"(linux|darwin|solaris)", compile, "make -C c_src"}, {"(freebsd)", compile, "gmake -C c_src"} ]}. {post_hooks,[ {"(linux|darwin|solaris)", clean, "make -C c_src clean"}, {"(freebsd)", clean, "gmake -C c_src clean"} ]}.
Makefile
是由rebar3 new cmake
命令建立的.app
下面是一個NIF, 包含一個repeat
函數, 接受一個pid
和一個erlang term做爲參數, 向該pid
發送消息:async
c_src/test_nif.c函數
#include "erl_nif.h" ERL_NIF_TERM mk_atom(ErlNifEnv* env, const char* atom) { ERL_NIF_TERM ret; if(!enif_make_existing_atom(env, atom, &ret, ERL_NIF_LATIN1)) { return enif_make_atom(env, atom); } return ret; } ERL_NIF_TERM mk_error(ErlNifEnv* env, const char* mesg) { return enif_make_tuple2(env, mk_atom(env, "error"), mk_atom(env, mesg)); } static ERL_NIF_TERM repeat(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ErlNifEnv* msg_env; ErlNifPid pid; ERL_NIF_TERM copy; if(argc != 2) { return enif_make_badarg(env); } if(!enif_is_pid(env, argv[0])) { return mk_error(env, "not_a_pid"); } if(!enif_get_local_pid(env, argv[0], &pid)) { return mk_error(env, "not_a_local_pid"); } msg_env = enif_alloc_env(); if(msg_env == NULL) { return mk_error(env, "environ_alloc_error"); } copy = enif_make_copy(msg_env, argv[1]); if(!enif_send(env, &pid, msg_env, copy)) { enif_free(msg_env); return mk_error(env, "error_sending_term"); } enif_free_env(msg_env); return mk_atom(env, "ok"); } static ErlNifFunc nif_funcs[] = { {"repeat", 2, repeat} }; ERL_NIF_INIT(test_nif, nif_funcs, NULL, NULL, NULL, NULL);
測試代碼 src/test_nif.erl
post
-module(test_nif). -export([repeat/2]). -on_load(init/0). -define(APPNAME, test_nif). -define(LIBNAME, test_nif). repeat(_, _) -> not_loaded(?LINE). init() -> SoName = case code:priv_dir(?APPNAME) of {error, bad_name} -> case filelib:is_dir(filename:join(["..", priv])) of true -> filename:join(["..", priv, ?LIBNAME]); _ -> filename:join([priv, ?LIBNAME]) end; Dir -> filename:join(Dir, ?LIBNAME) end, erlang:load_nif(SoName, 0). not_loaded(Line) -> exit({not_loaded, [{module, ?MODULE}, {line, Line}]}).
運行 rebar3 shell
測試
$ rebar3 shell ===> Verifying dependencies... ===> Compiling test_nif Erlang/OTP 17 [erts-6.3] [source] [64-bit] [smp:4:4] [async-threads:0] [kernel-poll:false] Eshell V6.3 (abort with ^G) 1> test_nif:repeat(self(), hello). ok 2> receive X -> X end. hello