erlang NIF部分接口实现(一)加载过程及编写框架

本文介绍了erlang的NIF(Native Implemented Functions)功能,它允许使用C/C++扩展erlang虚拟机。文章详细阐述了NIF的加载过程,包括erlang模块与NIF库的关联、函数调用的替换以及NIF库的初始化。同时,讨论了NIF相对于其他扩展方式的优点,如性能提升和简单编程接口。文章通过示例代码展示了NIF的编写框架,并解析了NIF加载时的内部机制,包括dlopen加载库、nif_init函数、函数替换等步骤,以及NIF执行时的上下文环境和参数转换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在项目中频繁用到erlang的NIF接口,以扩展erlang虚拟机的功能,同时又能提供较高的性能。

NIF(native implemented functions)从R14B开始支持,其功能在于,能够使得erlang module的功能通过c/c++实现。

erlang虚拟机有很多与外部进行功能交互的方式,如通过spawn_executable类型的port调用其它程序,port_driver,nif等,它们各有适应的场合:

spawn_executable类型的port会产生一个外部进程执行命令,不会干扰到erlang虚拟机本身,但是性能较低;

port_driver遵循erlang虚拟机的port机制,功能强大,但需要对虚拟机有较多的了解,编程门槛较高,内嵌入虚拟机,会影响到虚拟机执行,执行结果异步地通过消息队列返回,一些同步的环境下不适合;

nif功能强大但编程接口相对简单,不需要对虚拟机了解太多即可编写,调用一个nif实现的erlang接口就如同调用一个c函数一般,同时也有异步向进程投递消息的能力,极大的提升了erlang虚拟机的扩展能力,缺点也是需要内嵌入虚拟机,会影响到虚拟机执行。

如何利用NIF编写接口,初学者可以看看《erlang otp in action》上的例子,进阶者可以看看一些开源项目,如riak依赖的bitcask、eleveldb、ebloom等等,都是非常好的范例。

本次将分析NIF的部分重要接口的实现,其中涉及到erlang虚拟机的部分仅介绍基本原理而不会深入分析。

照抄官方文档上给出的例子:

NIF实现:

 

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)

ERL_NIF_INIT的第一个参数是该NIF的模块名,第二个参数是该模块包含的所有可供外部调用的函数定义数组,本例中,niftest即为NIF的模块名,而nif_funcs即为函数定义数组,nif_funcs包含了一个函数定义hello,其参数个数为0,具体实现为c函数static ERL_NIF_TERM hello(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])。

NIF的模块名可以将其与同名的erlang module对应起来。

对于linux平台,按照官方文档给出的编译方法,

gcc -fPIC -shared -o niftest.so niftest.c -I $ERL_ROOT/usr/include/

niftest.c将被编译为共享库niftest.so,以供虚拟机在需要的时候加载。

相应的erlang模块实现:

niftest.erl

-module(niftest).

-export([init/0, hello/0]).

init() ->

      erlang:load_nif("./niftest", 0).

hello() ->

      "NIF library not loaded".

 

niftest.erl定义了一个erlang模块niftest,包含一个init函数和一个hello函数,init函数用于初始化模块,通过load_nif函数加载niftest.so,这个定义与NIF对模块和函数的定义是一致的。

官方文档介绍时,明确的给出了调用结果:

 

1> c(niftest).

{ok,niftest}

2> niftest:hello().

"NIF library not loaded"

3> niftest:init().

ok

4> niftest:hello().

"Hello world!"

这是为什么呢?

首先来看加载时做了什么。

erlang:load_nif/2是一个bif,也即erlang的内建函数,要求必须由NIF对应的erlang module的某个函数直接调用。

erl_nif.c

BIF_RETTYPE load_nif_2(BIF_ALIST_2)

/*该函数的参数BIF_ALIST_2是一个数组,包含两个元素,对应了erlang:load_nif/2两个参数,第一个为共享库文件名,第二个为加载时用户传入的一些私有参数。*/

{

 

    len = list_length(BIF_ARG_1);

    if (len < 0) {

BIF_ERROR(BIF_P, BADARG);

    }

    lib_name = (char *) erts_alloc(ERTS_ALC_T_TMP, len + 1);

 

    if (intlist_to_buf(BIF_ARG_1, lib_name, len) != len) {

erts_free(ERTS_ALC_T_TMP, lib_name);

BIF_ERROR(BIF_P, BADARG);

    }

    lib_n

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值