5. NIF
There is an article from the blog Mr. yufeng. Thank him.
我们来用nif写个最简单的hello, 来展现nif的威力和简单性。
不啰嗦,直接上代码:
root@nd-desktop:~/niftest# cat niftest.c
/* niftest.c */
#include "erl_nif.h"
static ERL_NIF_TERM hello(ErlNifEnv* env)
{
return enif_make_string(env, "Hello world!");
}
static ErlNifFunc nif_funcs[] =
{
{"hello", 0, hello}
};
ERL_NIF_INIT(niftest,nif_funcs,NULL,NULL,NULL,NULL)
root@nd-desktop:~/niftest# cat niftest.erl
-module(niftest).
%-on_load(init/0).
-export([init/0, hello/0]).
init() ->
ok=erlang:load_nif("./niftest", 0), true.
hello() ->
"NIF library not loaded".
编译:
root@nd-desktop:~/niftest# gcc -fPIC -shared -o niftest.so niftest.c -I /usr/local/lib/erlang/usr/include #你的erl_nif.h路径
运行:
root@nd-desktop:~/niftest# erl
Erlang R13B03 (erts-5.7.4) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.7.4 (abort with ^G)
1> c(niftest).
{ok,niftest}
2> niftest:hello().
"NIF library not loaded"
3> niftest:init().
ok
4> niftest:hello().
"Hello world!"
5>
现在重新修改下 niftest.erl 把on_load的注释去掉
利用模块的自动加载机制来自动初始化。
root@nd-desktop:~/niftest# erl
Erlang R13B03 (erts-5.7.4) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.7.4 (abort with ^G)
1> c(niftest).
{ok,niftest}
2> niftest:hello().
"Hello world!"
3>
Note: Nbif参与erlang的公平调度, 你调用了nif函数,VM不保证马上调用你的函数实现,而是要到VM认为合适的时候才调用。
综述: nbif很简单,而且高效。
NIF的技术原文摘录如下:
erl_nif
Warning
The NIF concept is introduced in R13B03 as an EXPERIMENTAL feature. The interfaces may be changed in any way in coming releases. The API introduced in this release is very sparse and contains only the most basic functions to read and write Erlang terms.
A NIF library contains native implementation of some functions of an erlang module. The native implemented functions (NIFs) are called like any other functions without any difference to the caller. Each NIF must also have an implementation in Erlang that will be invoked if the function is called before the NIF library has been successfully loaded. A typical such stub implementation is to throw an exception. But it can also be used as a fallback implementation if the NIF library is not implemented for some architecture.
A better solution for a real module is to take advantage of the new attribute on_load to automatically load the NIF library when the module is loaded.
A loaded NIF library is tied to the Erlang module code version that loaded it. If the module is upgraded with a new version, the new code will have to load its own NIF library (or maybe choose not to). The new code version can however choose to load the exact same NIF library as the old code if it wants to. Sharing the same dynamic library will mean that static data defined by the library will be shared as well. To avoid unintentionally shared static data, each Erlang module code can keep its own private data. This global private data can be set when the NIF library is loaded and then retrieved by calling enif_get_data().
There is currently no way to explicitly unload a NIF library. A library will be automatically unloaded when the module code that it belongs to is purged by the code server. A NIF library will can also be unloaded by replacing it with another version of the library by a second call to erlang:load_nif/2 from the same module code.
上述几种方法中前4种是来自erlang的文档。最后一种是从Mr.yufeng的博文中抄来的。这样汇总好找。也好有个比较。
几种方法我已经在R13B03版本上测试过。奇怪的是Erl_interface接口,编译时报线程锁的错误。也就是说第二个,第四个是没有试通。NIF是可以的。 应用时选择对你最有效的一种。每种方式都有优缺点。
注意NIF文档中的说明,NIF是Erlang中函数的一种本地化,所以调用起来对于erlang程序没有分别速度也很快,但载入一个NIF库与erlang模板代码的版本有关。高版本的可能会载入原来低版本中用的库,这个要看情况。这样库中定义的static变量中的数据可能无意中共享了。目前解决之道是采用私有化的方法。我个人理解也许可以放到TLS中。
另外,目前也没有显示解载库的方法;只能由code server判断自动解载。也可以载入库的另一个版本来解载当前版本。
最后,上面红字中已经提醒了。NIF目前只是试验性,使用后有什么问题后果自负。