非常教程

Erlang 20参考手册

ERTS/erl_nif

erl_nif

C库

erl_nif

库摘要

用于ErlangNIF库的API函数。

描述

NIF库包含本地实现的Erlang模块的一些功能。本地实现的函数(NIF)与其他函数一样被调用,与调用者没有任何区别。如果在成功加载NIF库之前调用该函数,则每个NIF都必须在Erlang中实现该实现。典型的这种存根实现是抛出异常。但是如果NIF库没有为某些架构实现,它也可以用作后备实现。

警告

使用此功能时要格外小心。

本机函数作为VM本机代码的直接扩展执行。执行不是在安全的环境中进行的。VM不可能提供与执行Erlang代码相同的服务,例如抢占式调度或内存保护。如果本机函数的行为不太好,整个VM就会出现错误。

  • 崩溃的本机函数将使整个VM崩溃。
  • 错误实现的本机函数可能导致VM内部状态不一致,这可能导致VM崩溃,或者在调用本机函数之后的任意一点上VM的各种错误行为。
  • 返回之前执行lengthy work的本机函数会降低虚拟机的响应速度,并可能导致混杂的奇怪行为。这种奇怪的行为包括但不限于极端的内存使用,以及调度程序之间的负载平衡不佳。Erlang/OTP版本之间因长时间工作而出现的奇怪行为也会有所不同。

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)

Erlang模块可以如下所示:

-module(niftest).

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

init() ->
      erlang:load_nif("./niftest", 0).

hello() ->
      "NIF library not loaded".

编译和测试可以如下所示(在Linux上):

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

1> c(niftest).
{ok,niftest}
2> niftest:hello().
"NIF library not loaded"
3> niftest:init().
ok
4> niftest:hello().
"Hello world!"

更好的解决方案是利用on_load指令(参见 Erlang参考手册中的加载模块时运行函数),在模块加载时自动加载NIF库。

注意

NIF不一定要导出,它可以是模块本地的。但是,未使用的本地存根函数将被编译器优化,导致加载NIF库失败。

一旦加载,NIF库就是持久的。它将不会被卸载,直到它所属的模块代码版本被清除。

功能

NIF代码和Erlang运行时系统之间的所有交互都通过调用NIF API函数来执行。函数存在以下功能:

阅读和编写Erlang条款

任何Erlang条款都可以作为函数参数传递给NIF,并作为函数返回值返回。这些术语属于C型 ERL_NIF_TERM,只能使用API​​函数读取或写入。读取术语内容的大多数函数都以enif_get_为前缀,如果术语是预期类型(或不是),通常会返回 true(或false)。编写术语的函数都带有前缀enif_make_, 并通常返回创建的ERL_NIF_TERM。还有一些函数用于查询术语,如enif_is_atom,enif_is_identical和enif_compare。

类型的所有方面ERL_NIF_TERM属于类型的环境ErlNifEnv。术语的生命周期由其环境对象的生命周期来控制。所有读取或写入术语的API函数都有该术语所属的环境作为第一个函数参数。

二进制

二进制类型的术语在结构类型ErlNifBinary的帮助下访问 ,它包含一个指向原始二进制数据的指针(数据)和数据的长度(大小)(以字节为单位)。这两个数据和 大小是只读的,并且只能使用到API函数的调用来编写。然而,ErlNifBinary的实例总是由用户分配(通常作为局部变量)。

原始数据指向的数据仅仅是可变后调用enif_alloc_binaryenif_realloc_binary。对二进制文件进行操作的所有其他函数将数据保留为只读。一个可变的二进制文件最终必须通过enif_release_binary释放, 或者通过 enif_make_binary将其转换为Erlang术语进行 只读操作。但是,它不一定发生在同一个NIF呼叫中。只读的二进制文件不必被释放。

enif_make_new_binary可以用作在同一个NIF调用中分配和返回二进制文件的快捷方式。

二进制文件是整个字节的序列。具有任意位长的位串还不支持。

资源对象

资源对象的使用是一种从NIF返回指向本地数据结构的指针的安全方法。资源对象只是一个由enif_alloc_resource分配的内存块 。这个内存块的句柄(“安全指针”)可以通过使用enif_make_resource返回到Erlang 。enif_make_resource返回的术语本质上是不透明的。它可以在进程之间存储和传递,但唯一真正的最终用途是将其作为参数传递给NIF。然后NIF可以调用enif_get_resource并返回一个指向内存块的指针,该指针保证仍然有效。资源对象不会被释放,直到VM的最后一个句柄被垃圾收集,并且资源与enif_release_resource一起 释放 (不一定按顺序)。

所有资源对象都是作为某种资源类型的实例创建的。这使得来自不同模块的资源能够被区分。资源类型通过在加载库时调用enif_open_resource_type来创建 。随后可以分配该资源类型的对象,并且 enif_get_resource将验证该资源是否为预期类型。资源类型可以具有用户提供的析构函数,当释放该类型的资源时(通过垃圾收集器或 enif_release_resource)。资源类型由提供的名称字符串和实现模块的名称唯一标识。

以下是如何创建和返回资源对象的模板示例。

ERL_NIF_TERM term;
MyStruct* obj = enif_alloc_resource(my_resource_type, sizeof(MyStruct));

/* initialize struct ... */

term = enif_make_resource(env, obj);

if (keep_a_reference_of_our_own) {
    /* store 'obj' in static variable, private data or other resource object */
}
else {
    enif_release_resource(obj);
    /* resource now only owned by "Erlang" */
}
return term;

请注意,一旦enif_make_resource创建了返回Erlang的术语,代码可以选择保留自己的指向已分配结构的本地指针并稍后释放它,或者立即释放它,并且仅在垃圾收集器最终释放资源对象时才会最终释放资源对象它收集该术语。

资源对象的另一个用途是使用用户定义的内存管理创建二元项。enif_make_resource_binary 创建一个连接到资源对象的二元项。当二进制文件被垃圾收集时,资源的析构函数被调用,此时二进制数据可以被释放。这个例子可以是一个二进制项,由mmap'ed文件中的数据组成。然后析构函数可以做munmap来释放内存区域。

资源类型支持运行时升级,方法是允许加载的NIF库接管已存在的资源类型,并通过“继承”该类型的所有现有对象。此后,新库的析构函数将被调用以继承对象,并且旧析构函数的库可以安全地卸载。已升级模块的现有资源对象必须删除或由新的NIF库接管。只要库中存在具有析构函数的资源对象,就会推迟卸载库。

模块升级和静态数据

加载的NIF库绑定到加载它的Erlang模块实例。如果模块已升级,则新模块实例需要加载它自己的NIF库(或者可能选择不)。但是,新模块实例可以选择加载与旧代码完全相同的NIF库(如果需要)。共享动态库意味着由库定义的静态数据也是共享的。为避免模块实例之间无意间共享静态数据,每个Erlang模块版本都可以保留自己的私有数据。这个私有数据可以在加载NIF库时设置,并且稍后通过调用enif_priv_data来获取 。

线程和并发

只要NIF作为纯函数并只读取提供的参数,它就是线程安全的,不需要任何显式同步。当通过静态变量或enif_priv_data写入共享状态时,您需要提供自己的显式同步。这包括在线程之间共享的与进程无关的环境中的术语。如果将资源对象视为可变对象,则资源对象也需要同步。

即使对于共享状态数据,库初始化回调加载和 升级也是线程安全的。

版本管理

当构建NIF库时,有关NIF API版本的信息被编译到库中。加载NIF库时,运行时系统会验证该库是否具有兼容版本。 erl_nif.h定义了以下内容:

ERL_NIF_MAJOR_VERSION

在对Erlang运行时系统进行NIF库不兼容更改时增加。通常在ERL_NIF_MAJOR_VERSION更改时重新编译NIF库就足够了,但在极少数情况下,它可能意味着NIF库必须稍微修改。如果是这样,这当然会被记录下来。

ERL_NIF_MINOR_VERSION

增加新功能时增加。运行时系统使用次要版本来确定要使用的功能。

如果主版本不同,或者主版本相同,并且NIF库使用的次版本大于运行系统使用的次版本,则运行系统通常会拒绝加载NIF库。然而,在两个主要版本的过渡期间,主要版本出现颠簸后,允许使用低版本的旧NIF库。但是,如果使用不推荐使用的功能,则旧的NIF库可能会失败。

时间测量

支持NIF库中的时间测量:

  • ErlNifTime
  • ErlNifTimeUnit
  • enif_monotonic_time()
  • enif_time_offset()
  • enif_convert_time_unit()

I/O队列

Erlang nif库包含一些函数,可以方便地处理unix系统调用所使用的I/O向量。writevI/O队列不是线程安全的,因此必须使用其他的同步机制。

  • SysIOVec
  • ErlNifIOVec
  • enif_ioq_create()
  • enif_ioq_destroy()
  • enif_ioq_enq_binary()
  • enif_ioq_enqv()
  • enif_ioq_deq()
  • enif_ioq_peek()
  • enif_inspect_iovec()
  • enif_free_iovec()

写入文件描述符时的典型用法如下所示:

int writeiovec(ErlNifEnv *env, ERL_NIF_TERM term, ERL_NIF_TERM *tail,
               ErlNifIOQueue *q, int fd) {

    ErlNifIOVec vec, *iovec = &vec;
    SysIOVec *sysiovec;
    int saved_errno;
    int iovcnt, n;

    if (!enif_inspect_iovec(env, 64, term, tail, &iovec))
        return -2;

    if (enif_ioq_size(q) > 0) {
        /* If the I/O queue contains data we enqueue the iovec and
           then peek the data to write out of the queue. */
        if (!enif_ioq_enqv(q, iovec, 0))
            return -3;

        sysiovec = enif_ioq_peek(q, &iovcnt);
    } else {
        /* If the I/O queue is empty we skip the trip through it. */
        iovcnt = iovec->iovcnt;
        sysiovec = iovec->iov;
    }

    /* Attempt to write the data */
    n = writev(fd, sysiovec, iovcnt);
    saved_errno = errno;

    if (enif_ioq_size(q) == 0) {
        /* If the I/O queue was initially empty we enqueue any
           remaining data into the queue for writing later. */
        if (n >= 0 && !enif_ioq_enqv(q, iovec, n))
            return -3;
    } else {
        /* Dequeue any data that was written from the queue. */
        if (n > 0 && !enif_ioq_deq(q, n, NULL))
            return -4;
    }

    /* return n, which is either number of bytes written or -1 if
       some error happened */
    errno = saved_errno;
    return n;
}

长期运行的NIF

正如warning本手册页开头部分所述,本机函数返回速度相当快是非常重要的。很难给出一个原生函数允许工作的确切的最大时间量,但通常一个行为良好的本地函数会在1毫秒内返回给调用者。这可以通过使用不同的方法来实现。如果您完全控制要在本机函数中执行的代码,最好的方法是将工作划分为多个工作块并多次调用本机函数。但是,这并不总是可行的,例如在调用第三方库时。

enif_consume_timeslice()函数可用于通知运行时系统有关NIF呼叫的长度。它通常总是被使用,除非NIF执行得非常快。

如果NIF调用太长,必须通过以下方式之一来处理,以避免响应性降低,调度程序负载平衡问题以及其他奇怪行为:

产生NIF

如果可以拆分长期运行的NIF的功能,从而可以通过一系列较短的NIF调用来完成其工作,则应用程序有两个选项:

  • 从Erlang级别进行一系列NIF调用。
  • 调用一个NIF,该NIF首先执行一个工作块,然后调用enif_schedule_nif函数来调度另一个NIF调用以执行下一个块。然后,以这种方式调度的最后调用可以返回总体结果。

以这种方式分解一个长期运行的函数,使VM能够在对NIF的调用之间重新获得控制权。

这种方法总是比下文所述的其他备选办法更可取。这既从性能角度,也从系统特性角度。

螺纹NIF

这是通过将工作分派到由NIF库管理的另一个线程、从NIF返回并等待结果来完成的。线程可以使用以下方法将结果发送回Erlang进程enif_send下面提供了有关线程原语的信息。

Dirty NIF

只有当仿真程序配置了脏调度程序支持时,才能获得脏NIF支持。从ERTS版本9.0开始,在运行时系统上默认启用了脏调度程序支持,并提供了smp支持。没有SMP支持的Erlang运行时可以执行支持脏调度器,即使在显式启用脏调度程序支持时也是如此。若要在运行时检查是否存在脏调度程序线程,代码可以使用enif_system_info()API函数

不能拆分且不能在毫秒或更短时间内执行的NIF称为“脏NIF”,因为它执行Erlang运行时系统的普通调度程序无法干净处理的工作。使用这些函数的应用程序必须向运行时指示函数是脏的,以便能够进行特殊处理。这是通过在一组称为脏调度器的单独调度器上执行脏作业来处理的。在脏调度程序上执行的脏NIF不具有与普通NIF相同的持续时间限制。

把肮脏的工作分类正确是很重要的。I/O绑定作业应分类为I/O作业,CPU绑定作业应归类为I/O作业。如果将CPU绑定作业归类为I/O绑定作业,脏I/O调度器可能会使普通调度程序挨饿。受I/O约束的作业需要阻止等待I/O,和/或花费有限的时间移动数据。

要安排一个脏的NIF执行,应用程序有两个选项:

  • 在其ErlNifFunc条目中为脏NIF设置适当的标志值。
  • 调用enif_schedule_nif时,向它传递一个指向要执行的NIF的指针,并用参数指示flags它是否期望该操作受CPU限制或I/O限制。

在I/O绑定和CPU绑定之间交替的作业可以重新分类并重新调度,enif_schedule_nif以便它始终在正确类型的脏调度器上执行。有关更多信息,请参阅erl(1)命令行参数的文档+SDcpu+SDio

当一个进程执行一个肮脏的NIF时,一些与它通信的操作可能需要很长时间才能完成。执行脏NIF的进程的暂停或垃圾收集在脏NIF返回之前无法完成。因此,等待这些操作完成的其他进程可能需要等待很长时间。阻止多计划,即呼叫erlang:system_flag(multi_scheduling, block),也可能需要很长时间才能完成。这是因为在块操作完成之前,所有脏调度器上的所有正在进行的脏操作都必须完成。

但是,许多与执行脏NIF的进程通信的操作可以在执行脏NIF时完成。例如,通过erlang:process_info、设置其组长、注册/注销其名称等。

执行脏NIF的进程的终止只能在执行脏NIF时完成到某一点。所有的Erlang资源,例如其注册名称和ETS表格,都已发布。所有链接和监视器都被触发。然而,NIF的执行并未停止。NIF可以安全地继续执行,分配堆内存等,但最好尽快停止执行。NIF可以检查当前进程是否正在使用enif_is_current_process_alive。通信用enif_sendenif_port_command也跌落时的发送过程是不是还活着。某些内部资源(如进程堆和进程控制块)的取消分配被延迟,直到肮脏的NIF完成。

初始化

ERL_NIF_INIT(MODULE, ErlNifFunc funcs[], load, NULL, upgrade, unload)

这是初始化NIF库的神奇宏。它将在全局文件范围内进行评估。

MODULEErlang模块的名称作为没有字符串引号的标识符。它被宏所束缚。

funcs是该库中所有已实现的NIF的函数描述符的静态数组。

loadupgrade并且unload是功能的指针。其中一个load或被upgrade称为初始化图书馆。unload被称为释放图书馆。所有内容在下面分别介绍。

第四个参数NULL被忽略。它早期用于reload自OTP 20以来不再支持的deprecated 回调。

如果编译通过静态包含的NIF --enable-static-nifs,则必须STATIC_ERLANG_NIFERL_NIF_INIT声明之前进行定义。

`int(load)( ErlNifEnv env,void priv_data,ERL_NIF_TERM load_info)`**

load在加载NIF库时调用,并且此模块不存在以前加载的库。

*priv_data可以设置为指向一些私人数据,如果库需要保持NIF调用之间的状态。enif_priv_data返回这个指针。*priv_data被初始化为什么NULL时候load被调用。

load_info是第二个参数erlang:load_nif/2

如果load返回非0load可以是,NULL如果不需要初始化。

`int(升级)( ErlNifEnv env,void priv_data, void old_priv_data,ERL_NIF_TERM load_info)`

upgrade在加载NIF库时调用,并且该模块的旧代码包含已加载的NIF库。

作为load,除了*old_priv_data已经包含最后一次调用loadupgrade为旧模块代码设置的值。*priv_data被初始化为什么NULL时候upgrade被调用。它被允许写入两者*priv_data*old_priv_data.

该库无法加载,如果upgrade返回其他任何东西0或如果upgradeNULL

void (*unload)(ErlNifEnv* env, void* priv_data)

unload当清除NIF库所属的模块代码时调用。同一模块的新代码可能存在,也可能不存在。

数据类型

ERL_NIF_TERM

类型变量ERL_NIF_TERM可以指任何Erlang术语。这是一种不透明的类型,它的值只能用作API函数的参数或者用作NIF的返回值。所有ERL_NIF_TERM属于一个环境(ErlNifEnv)。一个术语不能单独破坏,它的有效性直到其环境被破坏。

ErlNifEnv

ErlNifEnv表示可以承载Erlang术语的环境。只要环境有效,环境中的所有术语都是有效的。ErlNifEnv是不透明类型;指向它的指针只能传递给API函数。有两种环境:

进程约束环境

作为第一个参数传递给所有NIF。传递给NIF的所有函数参数都属于该环境。NIF的返回值也必须是属于相同环境的术语。

一个进程绑定环境包含关于调用Erlang进程的瞬态信息。只有在NIF返回之前,环境才作为参数提供的线程有效。因此,在NIF调用之间存储指向进程绑定环境的指针是没有用的,也是危险的。

过程无关环境

通过调用创建enif_alloc_env。这个环境可以用来在NIF呼叫之间存储条款并且用来发送条款enif_send。包含所有术语的独立于流程的环境在您明确使用enif_free_env或使其失效之前一直有效enif_send

列表/元组/映射中包含的所有术语必须与列表/元组/映射本身属于相同的环境。术语可以在环境之间复制enif_make_copy

ErlNifFunc

typedef struct {
    const char* name;
    unsigned arity;
    ERL_NIF_TERM (*fptr)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
    unsigned flags;
} ErlNifFunc;

描述NIF的名称、重要性和实现。

fptr

指向实现NIF的函数的指针。

argv

包含传递给NIF的函数参数。

argc

数组长度,即函数的参数。argv[N-1]因此表示NIF的第N个参数。请注意,该参数argc允许相同的C函数实现具有不同arity(但可能具有相同名称)的几个Erlang函数。

flags

0一个常规的NIF(因此它的值可以被静态初始化的ErlNifFunc实例省略)。

flags可以用来指示nif是dirty NIF这将在脏调度程序线程上执行。

如果肮脏的NIF预计受CPU限制,则其flags字段将设置为ERL_NIF_DIRTY_JOB_CPU_BOUNDERL_NIF_DIRTY_JOB_IO_BOUND

如果其中一个ERL_NIF_DIRTY_JOB_*_BOUND设置标志,运行时系统不支持脏调度程序,运行时系统拒绝加载NIF库。

ErlNifBinary

typedef struct {
    unsigned size;
    unsigned char* data;
} ErlNifBinary;

ErlNifBinary包含有关检查的二进制项的瞬态信息。data的缓冲区的指针。size具有二进制的原始内容的字节。

请注意,这ErlNifBinary是一种半透明类型,您只能阅读字段sizedata

ErlNifBinaryToTerm

枚举可以指定的选项enif_binary_to_term。对于默认行为,请使用值0

从不可信来源接收数据时,请使用选项ERL_NIF_BIN2TERM_SAFE

ErlNifMonitor

这是标识监视器的不透明数据类型。

nif编写器将提供内存,用于在调用时存储监视器。enif_monitor_process运行时系统不存储数据的地址,因此ErlNifMonitor可以作为任何其他数据使用,它可以被复制,在内存中移动,遗忘等等。为了比较两台监视器,enif_compare_monitors一定要用。

ErlNifPid

进程标识符(pid)。与pid术语(实例ERL_NIF_TERM)相比,ErlNifPids是独立的,不受任何限制environmentErlNifPid是一种不透明的类型。

ErlNifPort

一个端口标识符。与端口ID术语(实例ERL_NIF_TERM)相比,ErlNifPorts是独立的,不受任何限制environmentErlNifPort是一种不透明的类型。

ErlNifResourceType

每一个实例ErlNifResourceType表示可以垃圾回收的内存托管资源对象的类。每个资源类型都有一个惟一的名称和一个析构函数,当释放其类型的对象时调用该函数。

ErlNifResourceTypeInit

typedef struct {
    ErlNifResourceDtor* dtor;
    ErlNifResourceStop* stop;
    ErlNifResourceDown* down;
} ErlNifResourceTypeInit;

初始化结构读取enif_open_resource_type_x

ErlNifResourceDtor

typedef void ErlNifResourceDtor(ErlNifEnv* env, void* obj);

资源析构函数的函数原型。

obj参数是指向资源的指针。对析构函数中的资源的唯一允许使用是最后一次访问其用户数据。析构函数保证是资源解除分配之前的最后一个回调。

ErlNifResourceDown

typedef void ErlNifResourceDown(ErlNifEnv* env, void* obj, const ErlNifPid* pid, const ErlNifMonitor* mon);

资源向下函数的函数原型,代表enif_monitor_process...obj是资源,pid是正在退出的受监视进程的标识,并且mon是监视器的身份。

ErlNifResourceStop

typedef void ErlNifResourceStop(ErlNifEnv* env, void* obj, ErlNifEvent event, int is_direct_call);

代表一个资源停止函数的函数原型enif_selectobj是资源,event是操作系统事件,is_direct_call如果调用是直接调用的,则为true;如果enif_select调度是调度调用(可能来自另一个线程),则为false。

ErlNifCharEncoding

typedef enum {
    ERL_NIF_LATIN1
}ErlNifCharEncoding;

字符串和原子中使用的字符编码。唯一支持的编码是ERL_NIF_LATIN1ISO Latin-1(8位ASCII)。

ErlNifSysInfo

用于enif_system_info返回有关运行时系统的信息。包含与。相同的内容ErlDrvSysInfo

ErlNifSInt64

本机带符号64位整数类型.

ErlNifUInt64

本机64位整数类型.

ErlNifTime

表示时间的带符号64位整数类型.

ErlNifTimeUnit

NIF API支持的时间单位的枚举:

ERL_NIF_SEC秒数ERL_NIF_MSEC毫秒ERL_NIF_USEC微秒ERL_NIF_NSEC纳秒ErlNifUniqueInteger

列举可以从中请求的属性enif_unique_integer。对于默认属性,请使用值0

ERL_NIF_UNIQUE_POSITIVE

只返回正整数。

ERL_NIF_UNIQUE_MONOTONIC

只返回strictly monotonically increasing与创建时间相对应的整数。

ErlNifHash

枚举可以使用生成的支持的哈希类型enif_hash

ERL_NIF_INTERNAL_HASH

非便携散列函数,它只保证在一个ErlangVM实例中为同一个术语提供相同的哈希。

它需要32位盐值并在其内生成哈希值0..2^32-1

ERL_NIF_PHASH2

可移植的哈希函数,它为相同的Erlang术语提供相同的散列,而不考虑机器架构和ERTS版本。

它忽略盐值并在其内部生成哈希值0..2^27-1

ERL_NIF_INTERNAL_HASH.它对应的慢erlang:phash2/1

SysIOVec

系统I / O矢量,writev在Unix和WSASendWin32上使用。它用在ErlNifIOVec和之中enif_ioq_peek

ErlNifIOVec

typedef struct {
  int iovcnt;
  size_t size;
  SysIOVec* iov;
} ErlNifIOVec;

包含iovcnt SysIOVec指向数据的I / O向量。它被enif_inspect_iovec和使用enif_ioq_enqv

ErlNifIOQueueOpts选项来配置一个ErlNifIOQueueERL_NIF_IOQ_NORMAL

创建正常的I/O队列

出口

void *enif_alloc(size_t size)

分配内存size字节。

回报NULL如果分配失败。

int enif_alloc_binary(size_t size, ErlNifBinary* bin)

分配一个大小为size字节的新二进制数。初始化指向的结构bin以引用分配的二进制文件。该二进制文件必须通过enif_release_binary或由所有权转移到Erlang术语enif_make_binaryErlNifBinaryNIF呼叫之间可以保留一个已分配(和拥有)。

回报true关于成功,或false如果分配失败。

ErlNifEnv *enif_alloc_env()

分配一个新的独立于流程的环境。该环境可用于保存不受任何进程约束的术语。这些条款可以稍后复制到流程环境中,enif_make_copy或作为消息发送到流程enif_send

返回指向新环境的指针。

void *enif_alloc_resource(ErlNifResourceType* type, unsigned size)

分配类型的内存托管资源对象。type和大小size字节。

size_t enif_binary_to_term(ErlNifEnv *env, const unsigned char* data, size_t size, ERL_NIF_TERM *term, ErlNifBinaryToTerm opts)

创建一个解释二进制数据的结果,该二进制数据data必须根据Erlang外部术语格式进行编码。不超过size读取字节data。参数opts对应于第二个参数erlang:binary_to_term/2并且必须是0或者ERL_NIF_BIN2TERM_SAFE

在成功时,将结果项存储在*term并返回读取的字节数。回报0如果解码失败或如果opts是无效的。

另见ErlNifBinaryToTermerlang:binary_to_term/2enif_term_to_binary

void enif_clear_env(ErlNifEnv* env)

释放环境中的所有术语并清除它以供重用。环境必须已经分配enif_alloc_env

int enif_compare(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs)

返回一个整数< 0if lhs< rhs0if lhs= rhs和> 0if lhs> rhs。对应于运营商二郎==/==<<>=,和>(但 =:==/=)。

int enif_compare_monitors(const ErlNifMonitor *monitor1, const ErlNifMonitor *monitor2)

比较两ErlNifMonitor无论出于什么原因,也可以用来暗示显示器上的某种人为秩序.

返回0if monitor1monitor2equal,< 0if monitor1< monitor2,and> 0if monitor1> monitor2

void enif_cond_broadcast(ErlNifCond *cnd)

和...一样erl_drv_cond_broadcast

ErlNifCond *enif_cond_create(char *name)

和...一样erl_drv_cond_create

void enif_cond_destroy(ErlNifCond *cnd)

和...一样erl_drv_cond_destroy

void enif_cond_signal(ErlNifCond *cnd)

和...一样erl_drv_cond_signal

void enif_cond_wait(ErlNifCond *cnd, ErlNifMutex *mtx)

和...一样erl_drv_cond_wait

int enif_consume_timeslice(ErlNifEnv *env, int percent)

向运行时系统提供一个提示,说明当前NIF调用自上次提示以来消耗了多少CPU时间,如果未指定之前的提示,则自NIF启动以来使用多少CPU时间。时间被指定为进程被允许执行Erlang代码的时间的百分比,直到它可以被挂起以便为其他可运行的进程提供时间。调度时间不是一个精确的实体,但通常可以近似为1毫秒。

请注意,要由运行时系统来确定是否以及如何使用这些信息。某些平台上的实现可以使用其他方法来确定消耗的CPU时间。冗长的NIF应该不考虑这个频繁的呼叫。enif_consume_timeslice以确定是否允许继续执行。

论辩percent必须是介于1和100之间的整数。此函数只能从nif调用线程和参数调用。env必须是调用进程的环境。

回报1如果时间已耗尽,则为0.如果1返回时,NIF将尽快返回,以便进程产生结果。

提供此功能是为了更好地支持协同调度,提高系统响应能力,并且更容易防止由于NIF垄断调度程序线程而导致的VM的错误行为。它可以用来划分length work进入多个重复的NIF调用,无需创建线程。

另见warning此手册页开头的文本。

ErlNifTime enif_convert_time_unit(ErlNifTime val, ErlNifTimeUnit from, ErlNifTimeUnit to)

转换val时间单位值from对应的时间单位值。to.结果使用地板函数四舍五入。

val值来转换时间单位。from时间单位val...to返回值的时间单位。

回报ERL_NIF_TIME_ERROR如果使用无效的时间单位参数调用。

另见ErlNifTimeErlNifTimeUnit

ERL_NIF_TERM enif_cpu_time(ErlNifEnv *)

以与格式相同的方式返回CPU时间erlang:timestamp()。CPU时间是当前逻辑CPU从过去的任意点开始执行的时间。如果操作系统不支持读取此值,则enif_cpu_time调用enif_make_badarg

int enif_demonitor_process(ErlNifEnv* env, void* obj, const ErlNifMonitor* mon)

取消先前创建的监视器。enif_monitor_process.论点obj是指向保存监视器和*mon识别监视器。

回报0如果监视器被成功识别并移除。如果无法识别监视器,则返回一个非零值,这意味着它是

  • 从未为此资源创建过
  • 已取消
  • 已触发
  • 将由并发线程触发。

当使用支持SMP的仿真器时,此函数才是线程安全的.。它只能用于来自NIF调用线程的非SMP仿真器.

int enif_equal_tids(ErlNifTid tid1, ErlNifTid tid2)

和...一样erl_drv_equal_tids

void enif_free(void* ptr)

释放分配的内存enif_alloc

void enif_free_env(ErlNifEnv* env)

释放分配给enif_alloc_env.所有在环境中创建的术语也都被释放了。

void enif_free_iovec(ErlNifIOvec* iov)

释放返回的io矢量enif_inspect_iovec。这只有在NULL传递给环境时才需要enif_inspect_iovec

ErlNifIOVec *iovec = NULL;
size_t max_elements = 128;
ERL_NIF_TERM tail;
if (!enif_inspect_iovec(NULL, max_elements, term, &tail, iovec))
  return 0;

// Do things with the iovec

/* Free the iovector, possibly in another thread or nif function call */
enif_free_iovec(iovec);

int enif_get_atom(ErlNifEnv* env, ERL_NIF_TERM term, char* buf, unsigned size, ErlNifCharEncoding encode)

NULL在由bufsize 指向的缓冲区中写入一个终止字符串size,该字符串由term编码的原子的字符串表示组成encode

返回写入的字节数(包括终止NULL字符),或者0如果term不是最大长度为的原子size-1

int enif_get_atom_length(ErlNifEnv* env, ERL_NIF_TERM term, unsigned* len, ErlNifCharEncoding encode)

设置*len为编码NULL原子的长度(不包括终止字符的字节数)。termencode

回报true关于成功,或false如果term不是原子。

int enif_get_double(ErlNifEnv* env, ERL_NIF_TERM term, double* dp)

设置*dp为的浮点值term

回报true关于成功,或false如果term不是浮子。

int enif_get_int(ErlNifEnv* env, ERL_NIF_TERM term, int* ip)

设置*ip为的整数值term

true成功返回,或者false如果term不是整数或超出类型的范围int

int enif_get_int64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifSInt64* ip)

设置*ip为的整数值term

回报true关于成功,或false如果term不是整数,或者不在有符号64位整数的界限之外.

int enif_get_local_pid(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPid* pid)

如果term是节点本地进程的PID,此函数初始化PID变量。*pid从那里回来true.否则返回false不进行任何检查,以确定该进程是否处于活动状态。

int enif_get_local_port(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPort* port_id)

如果term标识节点本地端口,此函数将初始化端口变量。*port_id从那里回来true.否则返回false.没有进行检查以确定端口是否还活着。

int enif_get_list_cell(ErlNifEnv* env, ERL_NIF_TERM list, ERL_NIF_TERM* head, ERL_NIF_TERM* tail)

*head*tail从列表list

回报true关于成功,或false如果它不是列表或列表是空的。

int enif_get_list_length(ErlNifEnv* env, ERL_NIF_TERM term, unsigned* len)

设置*len为列表的长度term

回报true关于成功,或false如果term不是一个合适的名单。

int enif_get_long(ErlNifEnv* env, ERL_NIF_TERM term, long int* ip)

设置*ip为长整型值term

true成功返回,或者false如果term不是整数或超出类型的范围long int

int enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, size_t *size)

*size映射中的键值对数。term...

回报true关于成功,或false如果term不是地图。

int enif_get_map_value(ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)

设置*value为与key地图关联的值map

true成功返回,或者false如果map不是地图或map不包含地图key

int enif_get_resource(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifResourceType* type, void** objp)

设置*objp为指向引用的资源对象term

true成功时返回,或者false如果term不是类型的资源对象的句柄type

int enif_get_string(ErlNifEnv* env, ERL_NIF_TERM list, char* buf, unsigned size, ErlNifCharEncoding encode)

NULL在由bufsize 指向的缓冲区中写入一个终止字符串,该字符串由字符串中size的字符组成list。这些字符是使用编码写入的encode

返回下列内容之一:

  • 写入的字节数(包括终止NULL字符)
  • -size如果字符串由于缓冲区空间而被截断
  • 0如果list不是可以用< encodesize> 编码的字符串1

写入的字符串总是NULL终止的,除非缓冲区size是< 1

int enif_get_tuple(ErlNifEnv* env, ERL_NIF_TERM term, int* arity, const ERL_NIF_TERM** array)

如果term是一个元组,此函数设置*array指向包含元组元素的数组,并设置*arity元素的数量。注意,数组是只读的,并且(*array)[N-1]是元组的第N个元素。*array如果元组的重要性为零,则为未定义。

回报true关于成功,或false如果term不是元组。

int enif_get_uint(ErlNifEnv* env, ERL_NIF_TERM term, unsigned int* ip)

设置*ip为无符号整数值term

true成功返回,或者false如果term不是无符号整数或超出类型的范围unsigned int

int enif_get_uint64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifUInt64* ip)

设置*ip为无符号整数值term

回报true关于成功,或false如果term不是无符号整数,也不是无符号64位整数的边界.

int enif_get_ulong(ErlNifEnv* env, ERL_NIF_TERM term, unsigned long* ip)

设置*ip为无符号长整型值term

true成功返回,或者false如果term不是无符号整数或超出类型的范围unsigned long

int enif_getenv(const char* key, char* value, size_t *value_size)

和...一样erl_drv_getenv

int enif_has_pending_exception(ErlNifEnv* env, ERL_NIF_TERM* reason)

true如果未决异常与环境相关联,则返回env。如果reasonNULL指针,则忽略它。否则,如果与env存在关联的未决异常设置*reason为异常项的值。例如,如果enif_make_badarg被调用来设置待处理的badarg异常,则稍后调用enif_has_pending_exception(env, &reason)设置*reason为原子badarg,然后返回true

另见enif_make_badargenif_raise_exception

ErlNifUInt64 enif_hash(ErlNifHash type, ERL_NIF_TERM term, ErlNifUInt64 salt)

term根据指定的哈希ErlNifHash type

被采纳盐的范围(如果有的话)和返回值取决于散列类型。

int enif_inspect_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, ErlNifBinary* bin)

bin用关于二元项的信息初始化指向的结构bin_term

回报true关于成功,或false如果bin_term不是二进制。

int enif_inspect_iolist_as_binary(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifBinary* bin)

初始化bin具有连续缓冲区,其字节内容与iolist.和inspect_binary的数据bin是暂时的,不需要释放。

回报true关于成功,或false如果iolist不是个少年主义者。

int enif_inspect_iovec(ErlNifEnv* env, size_t max_elements, ERL_NIF_TERM iovec_term, ERL_NIF_TERM* tail, ErlNifIOVec** iovec)

填充iovec中提供的二进制文件列表。iovec_term调用中处理的元素数量仅限于max_elements,和tail设置为列表的其余部分。请注意,输出可能比max_elements在一些平台上。

要从任意的iolist创建二进制文件列表,请使用erlang:iolist_to_iovec/1

调用此函数时,iovec应该包含指向NULL或者一个ErlNifIOVec结构,如果可能的话,应该使用它。G.

/* Don't use a pre-allocated structure */
ErlNifIOVec *iovec = NULL;
enif_inspect_iovec(env, max_elements, term, &tail, &iovec);

/* Use a stack-allocated vector as an optimization for vectors with few elements */
ErlNifIOVec vec, *iovec = &vec;
enif_inspect_iovec(env, max_elements, term, &tail, &iovec);

iovec被调用的nif函数返回之前,该内容是有效的。如果iovecnif调用返回后应该是有效的,则可以在一个NULL环境中调用该函数。如果没有给定环境iovec,则向量中拥有数据,并且必须使用明确释放的数据enif_free_iovec

回报true关于成功,或false如果iovec_term不是一片爱。

ErlNifIOQueue *enif_ioq_create(ErlNifIOQueueOpts opts)

创建一个可用于存储数据的新I / O队列。opts必须设置为ERL_NIF_IOQ_NORMAL

void enif_ioq_destroy(ErlNifIOQueue *q)

销毁I / O队列并释放所有内容

int enif_ioq_deq(ErlNifIOQueue *q, size_t count, size_t *size)

去队列count来自I/O队列的字节。如果size不是NULL,队列的新大小被放置在那里。

回报true关于成功,或false如果I/O不包含count字节。如果失败,队列将保持不变。

int enif_ioq_enq_binary(ErlNifIOQueue *q, ErlNifBinary *bin, size_t skip)

binq跳过第一个skip字节。

回报true关于成功,或false如果skip的大小大于bin二进制数据的任何所有权都被转移到队列中,并且bin将被认为是只读的,其余的NIF调用,然后作为释放。

int enif_ioq_enqv(ErlNifIOQueue *q, ErlNifIOVec *iovec, size_t skip)

iovecq跳过第一个skip字节。

返回true成功,或者false如果skip比规模更大iovec

SysIOVec *enif_ioq_peek(ErlNifIOQueue *q, int *iovlen)

将I/O队列作为指向SysIOVec中的元素数。iovlen.这是将数据从队列中取出的唯一方法。

这个函数不会从队列中删除任何内容,必须使用该函数完成enif_ioq_deq

返回的数组适用于Unix系统调用writev

size_t enif_ioq_size(ErlNifIOQueue *q)

获取大小q

int enif_is_atom(ErlNifEnv* env, ERL_NIF_TERM term)

回报true如果term是一个原子。

int enif_is_binary(ErlNifEnv* env, ERL_NIF_TERM term)

回报true如果term是二进制。

int enif_is_current_process_alive(ErlNifEnv* env)

返回true当前正在执行的进程当前是否存在,否则返回false

此函数只能从NIF调用线程中使用,并且可以使用与当前正在执行的进程相对应的环境。

int enif_is_empty_list(ErlNifEnv* env, ERL_NIF_TERM term)

回报true如果term是一个空列表。

int enif_is_exception(ErlNifEnv* env, ERL_NIF_TERM term)

返回true如果term是个例外。

int enif_is_fun(ErlNifEnv* env, ERL_NIF_TERM term)

回报true如果term很有趣。

int enif_is_identical(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs)

true如果两个词相同则返回。对应于Erlang运算符=:==/=

int enif_is_list(ErlNifEnv* env, ERL_NIF_TERM term)

回报true如果term是一份名单。

int enif_is_map(ErlNifEnv* env, ERL_NIF_TERM term)

true如果term是地图则返回,否则返回false

int enif_is_number(ErlNifEnv* env, ERL_NIF_TERM term)

回报true如果term是个数字。

int enif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term)

回报true如果term是PID。

int enif_is_port(ErlNifEnv* env, ERL_NIF_TERM term)

回报true如果term是港口。

int enif_is_port_alive(ErlNifEnv* env, ErlNifPort *port_id)

回报true如果port_id还活着。

当使用支持SMP的仿真器时,此函数才是线程安全的.。它只能用于来自NIF调用线程的非SMP仿真器.

int enif_is_process_alive(ErlNifEnv* env, ErlNifPid *pid)

回报true如果pid还活着。

当使用支持SMP的仿真器时,此函数才是线程安全的.。它只能用于来自NIF调用线程的非SMP仿真器.

int enif_is_ref(ErlNifEnv* env, ERL_NIF_TERM term)

回报true如果term是一个参考。

int enif_is_tuple(ErlNifEnv* env, ERL_NIF_TERM term)

回报true如果term是个元组。

int enif_keep_resource(void* obj)

添加对资源对象的引用。objenif_alloc_resource.每一次呼叫enif_keep_resource对象必须通过调用enif_release_resource在破坏对象之前。

ERL_NIF_TERM enif_make_atom(ErlNifEnv* env, const char* name)

使用ISO Latin-1编码从NULL终止的C字符串创建原子术语name。如果长度name超过原子允许的最大长度(255个字符),则enif_make_atom调用enif_make_badarg

ERL_NIF_TERM enif_make_atom_len(ErlNifEnv* env, const char* name, size_t len)

name长度为字符串中创建一个原子项lenNULL字符被视为任何其他字符。如果len超过原子允许的最大长度(255个字符),则enif_make_atom调用enif_make_badarg

ERL_NIF_TERM enif_make_badarg(ErlNifEnv* env)

使...badarg从NIF返回的异常,并将其与环境关联。env.一旦NIF或它调用的任何函数调用enif_make_badarg,运行时确保badarg当NIF返回时引发异常,即使NIF试图返回一个非异常项。

返回值enif_make_badarg可以仅用作NIF的返回值(直接或间接)或传递给enif_is_exceptionNIF,但不能传递给其他NIF API函数。

另见enif_has_pending_exceptionenif_raise_exception

在ERTS 7.0(Erlang / OTP 18)enif_make_badarg之前,必须从NIF 返回返回值。如果enif_make_badarg已调用NIF的返回值,则此要求现在被取消。

ERL_NIF_TERM enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin)

生成一个二进制术语bin二进制数据的任何所有权转移到创建的术语中,bin将被认为是只读的,其余的NIF调用,然后作为释放。

ERL_NIF_TERM enif_make_copy(ErlNifEnv* dst_env, ERL_NIF_TERM src_term)

复制一份术语src_term复制是在环境中创建的。dst_env源项可以位于任何环境中。

ERL_NIF_TERM enif_make_double(ErlNifEnv* env, double d)

从a创建浮点术语double。如果参数double不是有限的,或者是NaN,则enif_make_double调用enif_make_badarg

int enif_make_existing_atom(ErlNifEnv* env, const char* name, ERL_NIF_TERM* atom, ErlNifCharEncoding encode)

尝试使用编码创建已存在的来自NULL终止C字符串的原子的术语。nameencode

如果原子已经存在,则该函数将该项存储*atom并返回true,否则falsefalse如果长度name超过原子允许的最大长度(255个字符),也会返回。

int enif_make_existing_atom_len(ErlNifEnv* env, const char* name, size_t len, ERL_NIF_TERM* atom, ErlNifCharEncoding encoding)

尝试从name长度len和编码的字符串中创建已有原子的术语encodeNULL字符被视为任何其他字符。

如果原子已经存在,则该函数将该项存储*atom并返回true,否则falsefalse如果len超过原子允许的最大长度(255个字符),也会返回。

ERL_NIF_TERM enif_make_int(ErlNifEnv* env, int i)

创建一个整数项。

ERL_NIF_TERM enif_make_int64(ErlNifEnv* env, ErlNifSInt64 i)

从带符号的64位整数创建整数项.

ERL_NIF_TERM enif_make_list(ErlNifEnv* env, unsigned cnt, ...)

创建一个普通的长度列表项cnt。期望类型cnt的参数(之后cnt)的数量ERL_NIF_TERM作为列表的元素。

返回空列表,如果cnt是0。

ERL_NIF_TERM enif_make_list1(ErlNifEnv* env, ERL_NIF_TERM e1)ERL_NIF_TERM enif_make_list2(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2)ERL_NIF_TERM enif_make_list3(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2, ERL_NIF_TERM e3)ERL_NIF_TERM enif_make_list4(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e4)ERL_NIF_TERM enif_make_list5(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e5)ERL_NIF_TERM enif_make_list6(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e6)ERL_NIF_TERM enif_make_list7(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7)ERL_NIF_TERM enif_make_list8(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8)ERL_NIF_TERM enif_make_list9(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9)

创建一个具有由函数名称指示的长度的普通列表项。enif_make_list如果参数的数量不匹配,则优先使用这些函数(宏)来覆盖可变参数以获得编译时错误。

ERL_NIF_TERM enif_make_list_cell(ErlNifEnv* env, ERL_NIF_TERM head, ERL_NIF_TERM tail)

创建一个列表单元格[head | tail]

ERL_NIF_TERM enif_make_list_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], unsigned cnt)

创建一个包含arr长度数组元素的普通列表cnt

返回空列表,如果cnt是0。

ERL_NIF_TERM enif_make_long(ErlNifEnv* env, long int i)

从a创建一个整数项long int

int enif_make_map_put(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)

让副本地图map_in和插入keyvalue。如果key已经存在map_in,旧的关联值将被替换为value

如果成功,此函数将设置*map_out到新地图并返回true.返回false如果map_in不是地图。

map_in术语必须属于环境env

int enif_make_map_remove(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)

如果map map_in包含key,则此函数会复制map_inin *map_out,并删除key相关的值。如果map map_in不包含key*map_out则设置为map_in

回报true关于成功,或false如果map_in不是地图。

map_in术语必须属于环境env

int enif_make_map_update(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM new_value, ERL_NIF_TERM* map_out)

使得地图的副本map_in和替换旧的关联值keynew_value

如果成功,此函数将设置*map_out为新地图并返回truefalse如果map_in不是地图或不包含地图,则返回key

map_in术语必须属于环境env

unsigned char *enif_make_new_binary(ErlNifEnv* env, size_t size, ERL_NIF_TERM* termp)

分配大小的二进制文件size字节并创建一个拥有项。在调用NIF返回之前,二进制数据是可变的。这是一种快速创建新二进制文件的方法,而不必使用ErlNifBinary它的缺点是不能在NIF调用之间保留二进制文件,也不能重新分配二进制文件。

返回指向原始二进制数据和设置的指针。*termp二元术语。

ERL_NIF_TERM enif_make_new_map(ErlNifEnv* env)

形成一个空的地图术语。

ERL_NIF_TERM enif_make_pid(ErlNifEnv* env, const ErlNifPid* pid)

从中获得一个pid字词*pid

ERL_NIF_TERM enif_make_ref(ErlNifEnv* env)

创建一个参考erlang:make_ref/0

ERL_NIF_TERM enif_make_resource(ErlNifEnv* env, void* obj)

为由内存管理的资源对象创建一个不透明句柄enif_alloc_resource。没有完成所有权转移,因为资源对象仍然需要发布enif_release_resource。但是,请注意,调用enif_release_resource可以在获取期限后立即发生enif_make_resource,在这种情况下,资源对象在垃圾收集期间被释放。有关更多详细信息,请参阅example of creating and returning a resource object用户指南中的。

由于ERTS 9.0(OTP-20.0),资源项在比较和序列化term_to_binary或在节点之间传递时具有定义的行为。

  • 两个资源项将比较相等,如果它们传递给它们时会产生相同的资源对象指针enif_get_resource
  • 资源项可以用term_to_binary之后,如果资源对象仍然是活动的,则重新创建binary_to_term叫做。阿陈腐资源期限将从binary_to_term如果资源对象已被解除分配。enif_get_resource将返回无效资源项的false。

在将消息中的资源项传递给远程节点并再次返回时,也适用相同的序列化原则。资源项将在所有节点上失效,但资源对象仍然存在于内存中的节点除外。

在ERTS 9.0(OTP-20.0)之前,所有资源术语确实相互比较相等,并清空二进制文件(<<>>)。如果序列化,它们将被重新创建为纯二进制文件。

ERL_NIF_TERM enif_make_resource_binary(ErlNifEnv* env, void* obj, const void* data, size_t size)

创建由资源对象管理的内存的二进制术语。objenif_alloc_resource返回的二进制项包含size指向字节data在调用资源的析构函数之前,必须保持此原始二进制数据的可读性并保持不变。二进制数据可以存储在资源对象的外部,在这种情况下,析构函数负责释放数据。

多个二进制术语可以由同一个资源对象管理。直到最后一个二进制文件被垃圾收集后,才会调用析构函数。这对于返回更大的二进制缓冲区的不同部分非常有用。

与此同时enif_make_resource,没有完成所有权转让。该资源仍然需要与释放enif_release_resource

int enif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM list_in, ERL_NIF_TERM *list_out)

*list_out到列表的反向列表。list_in和回报true,或返回false如果list_in不是名单。

此函数仅用于短列表,因为该列表的副本是在NIF返回后才释放的。

list_in术语必须属于环境env

ERL_NIF_TERM enif_make_string(ErlNifEnv* env, const char* string, ErlNifCharEncoding encoding)

创建一个包含NULL-终止字符串string带编码encoding。

ERL_NIF_TERM enif_make_string_len(ErlNifEnv* env, const char* string, size_t len, ErlNifCharEncoding encoding)

创建一个包含字符串string长度len和编码的字符的列表encodingNULL字符被视为任何其他字符。

ERL_NIF_TERM enif_make_sub_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, size_t pos, size_t size)

生成二进制的子二进制bin_term,从零开始pos有一个长度size字节。bin_term必须是二进制或位字符串。pos+size必须小于或等于bin_term

ERL_NIF_TERM enif_make_tuple(ErlNifEnv* env, unsigned cnt, ...)

创建元组的元组术语cnt。期望类型cnt的参数数量(之后cntERL_NIF_TERM作为元组的元素。

ERL_NIF_TERM enif_make_tuple1(ErlNifEnv* env, ERL_NIF_TERM e1)ERL_NIF_TERM enif_make_tuple2(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2)ERL_NIF_TERM enif_make_tuple3(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2, ERL_NIF_TERM e3)ERL_NIF_TERM enif_make_tuple4(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e4)ERL_NIF_TERM enif_make_tuple5(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e5)ERL_NIF_TERM enif_make_tuple6(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e6)ERL_NIF_TERM enif_make_tuple7(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7)ERL_NIF_TERM enif_make_tuple8(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8)ERL_NIF_TERM enif_make_tuple9(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9)

创建一个元组项,长度由函数名称指示。enif_make_tuple如果参数的数量不匹配,则优先使用这些函数(宏)来覆盖可变参数以获得编译时错误。

ERL_NIF_TERM enif_make_tuple_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], unsigned cnt)

创建一个包含arr长度数组元素的元组cnt

ERL_NIF_TERM enif_make_uint(ErlNifEnv* env, unsigned int i)

unsigned int。创建一个整数项。

ERL_NIF_TERM enif_make_uint64(ErlNifEnv* env, ErlNifUInt64 i)

从无符号64位整数创建整数项.

ERL_NIF_TERM enif_make_ulong(ErlNifEnv* env, unsigned long i)

unsigned long int。创建一个整数项。

ERL_NIF_TERM enif_make_unique_integer(ErlNifEnv *env, ErlNifUniqueInteger properties)

返回具有与指定的相同属性的唯一整数erlang:unique_integer/1

env创建整数的环境。

ERL_NIF_UNIQUE_POSITIVEERL_NIF_UNIQUE_MONOTONIC可以作为第二个参数传递,以更改返回的整数的属性。它们可以通过OR:将这两个值组合在一起。

另见ErlNifUniqueInteger

int enif_map_iterator_create(ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry)

map通过初始化指向的结构来创建映射的迭代器iter。参数entry决定迭代器的开始位置:ERL_NIF_MAP_ITERATOR_FIRSTERL_NIF_MAP_ITERATOR_LAST

回报true如果是成功的话,还是假的map不是地图。

映射迭代器仅在环境的生存期内有用。env认为map属于。必须通过调用enif_map_iterator_destroy*

ERL_NIF_TERM key, value;
ErlNifMapIterator iter;
enif_map_iterator_create(env, my_map, &iter, ERL_NIF_MAP_ITERATOR_FIRST);

while (enif_map_iterator_get_pair(env, &iter, &key, &value)) {
    do_something(key,value);
    enif_map_iterator_next(env, &iter);
}
enif_map_iterator_destroy(env, &iter);

映射的键值对没有定义的迭代顺序。唯一的保证是在映射所属的环境的生存期内保留单个map实例的迭代顺序。

void enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter)

破坏由...创建的地图迭代器enif_map_iterator_create

int enif_map_iterator_get_pair(ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value)

获取当前映射迭代器位置的键和值项。

如果成功,将*key*value和回报true。返回false如果迭代器位于头(第一个条目之前)或尾部(超出最后一项)。

int enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter)

回报true中频映射迭代器iter在第一个条目之前定位。

int enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter)

回报true中频映射迭代器iter在最后一项之后定位。

int enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter)

增量映射迭代器指向下一个键值条目。

返回true如果迭代现在定位在一个有效的键值项,或者false如果迭代器位于尾部(超出最后一项)。

int enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter)

递减映射迭代器指向前一个键值项。

返回true如果迭代现在定位在一个有效的键值项,或者false如果迭代器定位在头部(第一项之前)。

int enif_monitor_process(ErlNifEnv* env, void* obj, const ErlNifPid* target_pid, ErlNifMonitor* mon)

从资源开始监视进程。监视进程时,流程退出将导致对提供的down与资源类型关联的回调。

论辩obj是指向用于保存监视器和*target_pid标识要监视的本地进程。

如果mon不是NULL,成功的调用将监视器的标识存储在ErlNifMonitor结构指向mon此标识符用于引用监视器,以便以后使用enif_demonitor_process或与之相比enif_compare_monitors当监视器触发或资源被解除分配时,将自动删除它。

回报0在成功时,<0(如果没有)down如果进程不再活动,则提供回调,并且>0。

当使用支持SMP的仿真器时,此函数才是线程安全的.。它只能用于来自NIF调用线程的非SMP仿真器.

ErlNifTime enif_monotonic_time(ErlNifTimeUnit time_unit)

返回当前Erlang monotonic time注意,负值并不少见。

time_unit返回值的时间单位。

回报ERL_NIF_TIME_ERROR如果使用无效的时间单元参数调用,或者从不是调度程序线程的线程调用。

另见ErlNifTimeErlNifTimeUnit

ErlNifMutex *enif_mutex_create(char *name)

和...一样erl_drv_mutex_create

void enif_mutex_destroy(ErlNifMutex *mtx)

erl_drv_mutex_destroy一样。

void enif_mutex_lock(ErlNifMutex *mtx)

和...一样erl_drv_mutex_lock

int enif_mutex_trylock(ErlNifMutex *mtx)

和...一样erl_drv_mutex_trylock

void enif_mutex_unlock(ErlNifMutex *mtx)

erl_drv_mutex_unlock一样。

ERL_NIF_TERM enif_now_time(ErlNifEnv *env)

返回erlang:now()时间戳。

这一功能已被废弃。

ErlNifResourceType *enif_open_resource_type(ErlNifEnv* env, const char* module_str, const char* name, ErlNifResourceDtor* dtor, ErlNifResourceFlags flags, ErlNifResourceFlags* tried)

创建或接管由字符串标识的资源类型。name并给出它所指向的析构函数。dtor.论点flags可以具有以下值:

ERL_NIF_RT_CREATE创建不存在的新资源类型。ERL_NIF_RT_TAKEOVER打开现有资源类型并接管其所有实例的所有权。提供的析构函数dtor为调用NIF库尚未创建的现有实例和新实例调用。

两个标志值可以与按位OR组合。资源类型名称对调用模块而言是本地的。论证module_str尚未被使用,必须是NULLdtor可以是NULL如果不需要析构函数的话。

成功时,该函数返回一个指向资源类型的指针,并将*tried其设置为ERL_NIF_RT_CREATEERL_NIF_RT_TAKEOVER指示完成了什么。失败时,返回NULL并设置*triedflags。它可以设置triedNULL

请注意,enif_open_resource_type只允许在两个回调调用loadupgrade

另见enif_open_resource_type_x

ErlNifResourceType *enif_open_resource_type_x(ErlNifEnv* env, const char* name, const ErlNifResourceTypeInit* init, ErlNifResourceFlags flags, ErlNifResourceFlags* tried)

enif_open_resource_type除了接受与enif_selectand 一起使用的资源类型的附加回调函数外enif_monitor_process

论辩init是指向ErlNifResourceTypeInit结构,该结构包含析构函数、资源类型的向下回调和停止回调的函数指针。

int enif_port_command(ErlNifEnv* env, const ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg)

作为erlang:port_command/2,只是它总是完全异步的。

env调用过程的环境。一定不是NULL*to_port接收端口的端口号。端口ID是指本地节点上的端口。msg_env消息术语的环境。可以是与enif_alloc_envor 分配的与流程无关的环境NULLmsg要发送的消息条款。在有效载荷上也适用相同的限制erlang:port_command/2

使用msg_envNULL是一种优化,这组一起呼吁enif_alloc_envenif_make_copyenif_port_command,和enif_free_env成为一个呼叫。这种优化只有在大多数条款要从env中复制时才有用msg_env

回报true如果命令已成功发送。回报false如果命令失败,例如:

  • *to_port不引用本地端口。
  • 当前正在执行的进程%28,即发送方%29未激活。
  • msg是无效的。

另见enif_get_local_port

void *enif_priv_data(ErlNifEnv* env)

返回指向由loador 设置的私有数据的指针upgrade

ERL_NIF_TERM enif_raise_exception(ErlNifEnv* env, ERL_NIF_TERM reason)

使用术语创建错误异常。reason从nif返回,并将其与环境关联。env.一旦NIF或它调用的任何函数调用enif_raise_exception,运行时确保它创建的异常在NIF返回时引发,即使NIF试图返回一个非异常项。

的返回值。enif_raise_exception只能用作直接或间接调用它的NIF的返回值,或传递给enif_is_exception,但不适用于任何其他NIF API函数。

另见enif_has_pending_exceptionenif_make_badarg

int enif_realloc_binary(ErlNifBinary* bin, size_t size)

更改二进制文件的大小bin。源二进制文件可以是只读的,在这种情况下,它不会被触发,并且可变副本被分配并分配给它*bin

回报true关于成功,或false如果内存分配失败。

void enif_release_binary(ErlNifBinary* bin)

释放一个二进制文件enif_alloc_binary

void enif_release_resource(void* obj)

删除对从中obj获取的资源对象的引用enif_alloc_resource。当最后一个引用被删除时,资源对象被破坏。每个呼叫enif_release_resource必须对应于先前对enif_alloc_resource或的呼叫enif_keep_resource。所做的引用enif_make_resource只能由垃圾收集器删除。

ErlNifRWLock *enif_rwlock_create(char *name)

和...一样erl_drv_rwlock_create

void enif_rwlock_destroy(ErlNifRWLock *rwlck)

erl_drv_rwlock_destroy一样。

void enif_rwlock_rlock(ErlNifRWLock *rwlck)

erl_drv_rwlock_rlock一样。

void enif_rwlock_runlock(ErlNifRWLock *rwlck)

erl_drv_rwlock_runlock一样。

void enif_rwlock_rwlock(ErlNifRWLock *rwlck)

erl_drv_rwlock_rwlock一样。

void enif_rwlock_rwunlock(ErlNifRWLock *rwlck)

erl_drv_rwlock_rwlock一样。

int enif_rwlock_tryrlock(ErlNifRWLock *rwlck)

erl_drv_rwlock_tryrlock一样。

int enif_rwlock_tryrwlock(ErlNifRWLock *rwlck)

和erl_drv_rwlock_tryrlock一样。

ERL_NIF_TERM enif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags, ERL_NIF_TERM (*fp)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]), int argc, const ERL_NIF_TERM argv[])

附表NIFfp执行。此函数允许应用程序将长期运行的工作分解为多个常规的nif调用或调度dirty NIF若要在脏调度程序线程上执行,请执行以下操作。

fun_name

为计划执行的NIF提供名称。如果不能转换成原子,enif_schedule_nif返回badarg例外。

flags

必须设置为0普通的NIF。如果仿真器是在启用了脏调度程序支持的情况下生成的,flags可以设置为ERL_NIF_DIRTY_JOB_CPU_BOUND如果作业被期望是cpu绑定的,或者ERL_NIF_DIRTY_JOB_IO_BOUND对于那些将被I/O限制的工作。如果仿真程序中无法使用脏调度程序线程,则尝试调度这样的作业会导致notsup例外。

argcargv

可以是传递到调用NIF的原件,也可以是调用NIF创建的值。

调用NIF必须使用enif_schedule_nif作为它自己的返回值。

注意enif_schedule_nif顾名思义,只为以后的执行安排NIF。调用NIF不会阻止等待计划的NIF执行和返回。这意味着调用NIF不能期望接收计划的NIF返回值,并将其用于进一步的操作。

int enif_select(ErlNifEnv* env, ErlNifEvent event, enum ErlNifSelectFlags mode, void* obj, const ErlNifPid* pid, ERL_NIF_TERM ref)

当操作系统特定的事件对象准备好进行读或写操作时,此函数可用于接收异步通知。

论辩event标识事件对象。关于Unix系统,函数select/poll都是用的。事件对象必须是套接字、管道或其他文件描述符对象。select/poll可以用。

参数mode描述要等待的事件类型。它可以是ERL_NIF_SELECT_READERL_NIF_SELECT_WRITE或按位或组合件,等待两者。它也可以ERL_NIF_SELECT_STOP在下面进一步描述。当读或写事件被触发时,这样的通知消息被发送到由pid以下标识的过程:

{select, Obj, Ref, ready_input | ready_output}

ready_inputready_output指示事件对象是否已准备好进行读取或写入。

论辩pid可能是NULL若要指示调用进程,请执行以下操作。

参数obj是从中获得的资源对象enif_alloc_resource。资源对象的目的是作为事件对象的容器来管理其状态和生命周期。在通知消息中接收资源的句柄为Obj

论辩ref必须是从erlang:make_ref/0或者原子undefined.它将作为Ref在通知中。如果有选择性receive语句用于等待通知,然后在receive将利用运行时优化来绕过队列中所有先前接收的消息。

通知只是一次性的。要接收更多相同类型的通知(读或写),enif_select必须在收到每个通知后重复呼叫。

使用ERL_NIF_SELECT_STOPmode为了安全地关闭已传递到一个事件对象enif_select。当可以安全地关闭事件对象时,将调用stop资源的回调obj。即使已收到所有通知并且没有进一步的呼叫,enif_select也必须使用这种关闭事件对象的安全方式。

第一次呼叫enif_select对于特定的操作系统event将在事件对象和包含的资源之间建立关系。的所有后续调用event必须将其包含的资源作为参数传递。obj.关系在enif_select已被调用modeERL_NIF_SELECT_STOP以及相应的stop回调回来了。一个资源可以包含多个事件对象,但是一个事件对象只能包含在一个资源中。一种资源在其所包含的所有关系都被消除之前,是不会被摧毁的。

使用enif_monitor_process连同enif_select检测失败的Erlang进程并防止它们导致资源及其包含的OS事件对象的永久泄漏。

在成功时返回一个非负值,其中可以设置以下位数:

ERL_NIF_SELECT_STOP_CALLED停止回调被直接调用enif_selectERL_NIF_SELECT_STOP_SCHEDULED停止回调计划在某个其他线程上运行或稍后由此线程运行。

如果调用失败,可以设置折叠位,则返回一个负值:

ERL_NIF_SELECT_INVALID_EVENT论辩event不是有效的OS事件对象。ERL_NIF_SELECT_FAILED系统调用未能将事件对象添加到轮询集。

按位使用并测试返回值中的特定位。在未来的版本中可能会添加新的重要位,以便为失败的调用和成功的调用提供更详细的信息。不要使用类似于==,因为这可能导致应用程序停止工作。

例子:

retval = enif_select(env, fd, ERL_NIF_SELECT_STOP, resource, ref);
if (retval < 0) {
    /* handle error */
}
/* Success! */
if (retval & ERL_NIF_SELECT_STOP_CALLED) {
    /* ... */
}

ErlNifPid *enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)

初始化PID变量*pid若要表示调用进程,请执行以下操作。

回报pid...

int enif_send(ErlNifEnv* env, ErlNifPid* to_pid, ErlNifEnv* msg_env, ERL_NIF_TERM msg)

向进程发送消息。

env调用进程的环境。一定是NULL只有当从创建的线程调用时。*to_pid接收过程的PID。PID是指本地节点上的进程。msg_env消息术语的环境。必须是与进程无关的环境。enif_alloc_env或者零。msg要发送的消息项。

回报true如果消息已成功发送。回报false如果发送操作失败,即:

  • *to_pid不引用活动的本地进程。
  • 当前正在执行的进程(即发件人)不活动。

msg_env所有条款(包括msg)的消息环境通过成功的呼叫而失效enif_send。环境要么被enif_free_env清理出来以便重用enif_clear_env

如果msg_env设置为NULLmsg术语被复制,调用后原始术语及其环境仍然有效。

当使用支持SMP的仿真器时,此函数才是线程安全的.。它只能用于来自NIF调用线程的非SMP仿真器.

通过msg_env作为NULL仅支持从ERTS 8.0(二郎/ OTP 19)。

unsigned enif_sizeof_resource(void* obj)

获取资源对象的字节大小obj而获得enif_alloc_resource

int enif_snprintf(char *str, size_t size, const char *format, ...)

类似于snprintf但是这个格式字符串也接受"%T",这是Erlang术语的格式。

void enif_system_info(ErlNifSysInfo *sys_info_ptr, size_t size)

driver_system_info一样。

int enif_term_to_binary(ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *bin)

分配一个新的二进制文件enif_alloc_binary并存储编码结果。term根据Erlang外部术语格式。

回报true关于成功,或false如果分配失败。

另见erlang:term_to_binary/1enif_binary_to_term

int enif_thread_create(char *name,ErlNifTid *tid,void * (*func)(void *),void *args,ErlNifThreadOpts *opts)

erl_drv_thread_create一样。

void enif_thread_exit(void *resp)

erl_drv_thread_exit一样。

int enif_thread_join(ErlNifTid, void **respp)

erl_drv_thread_join一样。

ErlNifThreadOpts *enif_thread_opts_create(char *name)

erl_drv_thread_opts_create一样。

void enif_thread_opts_destroy(ErlNifThreadOpts *opts)

erl_drv_thread_opts_destroy一样。

ErlNifTid enif_thread_self(void)

erl_drv_thread_self一样。

int enif_thread_type(void)

确定当前正在执行的线程的类型。正值表示调度程序线程,负值或零表示另一种线程。目前存在以下特定类型(将来可能会扩展):

ERL_NIF_THR_UNDEFINED

未定义的线程,它不是调度程序线程。

ERL_NIF_THR_NORMAL_SCHEDULER

一个正常的调度程序线程。

ERL_NIF_THR_DIRTY_CPU_SCHEDULER

脏CPU调度器线程。

ERL_NIF_THR_DIRTY_IO_SCHEDULER

一个脏的I/O调度器线程。

ErlNifTime enif_time_offset(ErlNifTimeUnit time_unit)

返回当前时间偏移Erlang monotonic timeErlang system time转换为time_unit作为论据通过。

time_unit返回值的时间单位。

回报ERL_NIF_TIME_ERROR如果使用无效的时间单元参数调用,或者从不是调度程序线程的线程调用。

另见ErlNifTimeErlNifTimeUnit

void *enif_tsd_get(ErlNifTSDKey key)

erl_drv_tsd_get一样。

int enif_tsd_key_create(char *name, ErlNifTSDKey *key)

erl_drv_tsd_key_create一样。

void enif_tsd_key_destroy(ErlNifTSDKey key)

erl_drv_tsd_key_destroy一样。

void enif_tsd_set(ErlNifTSDKey key, void *data)

erl_drv_tsd_set一样。

int enif_whereis_pid(ErlNifEnv *env, ERL_NIF_TERM name, ErlNifPid *pid)

使用已注册的名称查找进程。

env调用进程的环境。一定是NULL只有当从创建的线程调用时。name注册进程的名称,如原子。*pidErlNifPid其中存储解析的进程id。

关于成功,布景*pid注册的本地进程。name和回报true.如果name不是一个已注册的进程,也不是一个原子,false会被退回并且*pid没有变化。

作为erlang:whereis/1,但仅限于过程。见enif_whereis_port解析已注册的端口。

int enif_whereis_port(ErlNifEnv *env, ERL_NIF_TERM name, ErlNifPort *port)

以注册名称查找端口。

env调用进程的环境。一定是NULL只有当从创建的线程调用时。name注册端口的名称,如原子。*portErlNifPort其中存储解析端口id。

关于成功,布景*port注册的港口name和回报true.如果name不是注册端口,也不是原子,false会被退回并且*port没有变化。

作为erlang:whereis/1,但仅限于港口。见enif_whereis_pid解析已注册的进程。

ERTS/erl_nif相关

Erlang 20

Erlang 是一种通用的面向并发的编程语言,可应付大规模开发活动的程序设计语言和运行环境。

主页 https://www.erlang.org/
源码 https://github.com/erlang/otp
版本 20
发布版本 20.1