Native Implemented Functions(NIF)可以用C来实现程序一些功能的扩展,一般用来实现一些用Erlang无法实现或者实现效率低的功能。
C语言编译生成的动态库(*.so)在Erlang调用C模块时动态加载到Erlang的进程空间中,调用NIF不用上下文的切换开销,但是安全性不是很高,因为NIF的crash会导致整个Erlang进程crash。
NIF的实现先按官方文档上给的例子,初次实现一下NIF的使用:
1.创建niftest.c文件#include "erl_nif.h" static ERL_NIF_TERM hello(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { return enif_make_string(env, "Hello world!", ERL_NIF_LATIN1); } static ErlNifFunc nif_funcs[] = { {"hello", 0, hello} }; ERL_NIF_INIT(niftest,nif_funcs,NULL,NULL,NULL,NULL)
初始化NIF库:
通过初始化宏将C实现和对应的Erlang模块绑定起来;
ERL_NIF_INIT(niftest,nif_funcs,NULL,NULL,NULL,NULL)
第一个参数 : 对应erlang模块的名称
第二个参数 : 是该库中所有可供外部调用的NIF的函数描述符的静态数组
函数描述符用数据结构ErlNifFunc来表示:
typedef struct { const char* name; unsigned arity; ERL_NIF_TERM (*fptr)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); }ErlNifFunc;
name表示在Erlang中调用的函数名; arity表示函数的参数个数;
fptr是一个指向函数的指针,它是这个函数对应的C语言实现
在C语言中实现的NIF函数的定义方式:
static ERL_NIF_TERM FuncName(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
第一个参数总是ErlNifEnv,代表着函数调用的上下文环境, 可以通过它得到对应的NIF模块的某些特定数据;
参数列表argv是由Erlang传过来的参数,参数个数是argc;
如果在函数中需要使用传过来的参数,就要用enif_get_*函数将argv解析成对应的数据类型,如int, float等;
输出(函数返回)的数据,要将C的数据类型转换成ERL_NIF_TERM,C返回给Erlang的数据的转换过程是通过enif_make_*函数实现;
-module(niftest). -export([init/0, hello/0]). init() -> erlang:load_nif("./niftest", 0). hello() -> "NIF library not loaded".
通过load_nif函数加载niftest.so
3.编译niftest.c文件niftest.c将被编译为共享库niftest.so,以供在需要调用的时候加载
在linux平台下:
gcc -fPIC -shared -o niftest.so niftest.c -I $ERL_ROOT/usr/include/
gcc编译生成共享库niftest.so:
-fPIC : 告诉编译器产生与位置无关代码 (Position-Independent Code)。这样一来,产生的代码中就没有绝对地址了,全部使用相对地址,所以代码可以被加载到内存的任意位置,都可以正确的执行;
-shared : 表明产生共享库;
-o : 指定生成可执行文件的名称
-I : 指定头文件目录
$ERL_ROOT : 为erlang-otp的安装路径
调用结果:
可以看到在加载nif后就可以调用niftest.c中的函数了
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)