查看源代码 erl_nif

Erlang NIF 库的 API 函数。

描述

NIF 库包含 Erlang 模块某些函数的本机实现。 本机实现的函数(NIF)像任何其他函数一样被调用,对调用者没有任何区别。 NIF 库构建为动态链接库文件,并通过调用 erlang:load_nif/2 在运行时加载。

警告

请极其谨慎地使用此功能。

本机函数作为 VM 本机代码的直接扩展执行。 执行不在安全环境中进行。 VM 无法 提供与执行 Erlang 代码时提供的相同服务,例如抢占式调度或内存保护。 如果本机函数行为不当,整个 VM 将会行为不端。

  • 崩溃的本机函数将导致整个 VM 崩溃。
  • 错误实现的本机函数可能会导致 VM 内部状态不一致,这可能会导致 VM 崩溃,或在调用本机函数后的任何时间点导致 VM 的各种错误行为。
  • 在返回之前执行 耗时工作 的本机函数会降低 VM 的响应能力,并可能导致各种奇怪的行为。 这种奇怪的行为包括但不限于极端的内存使用以及调度程序之间的不良负载平衡。 由于耗时工作而可能发生的奇怪行为也可能在 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]).

-nifs([hello/0]).

-on_load(init/0).

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

hello() ->
      erlang:nif_error("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().
"Hello world!"

在上面的示例中,on_load 指令用于在加载模块时自动调用函数 init。 函数 init 又调用 erlang:load_nif/2,后者加载 NIF 库并将 hello 函数替换为其 C 语言中的本机实现。 加载后,NIF 库是持久的。 在清除其所属的模块实例之前,它不会被卸载。

-nifs() 属性指定模块中要由 NIF 替换的函数。

每个 NIF 必须在 Erlang 中实现,以便在 NIF 库成功加载之前调用该函数。 一个典型的这种存根实现是调用 erlang:nif_error,它将引发异常。 如果 NIF 库缺少某些操作系统或硬件架构的实现,Erlang 函数也可以用作回退实现。

注意

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_atomenif_is_identicalenif_compare

    所有类型为 ERL_NIF_TERM 的项都属于类型为 ErlNifEnv 的环境,除了在加载期间(通过回调 loadupgrade)创建的原子。 项的生命周期由其环境对象的生命周期控制。 所有读取或写入项的 API 函数都将该项所属的环境作为第一个函数参数。 但是,加载期间创建的原子可以在任何 ErlNifEnv 中作为项引用。 也就是说,最佳实践是在加载期间创建所有原子,并将它们存储在静态/全局变量中,例如

    #include <erl_nif.h>
    
    ERL_NIF_TERM world_atom;
    
    static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
    {
        world_atom = enif_make_atom(env, "world");
        return 0;
    }
    
    static ERL_NIF_TERM hello(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
    {
        ERL_NIF_TERM hello_string = enif_make_string(env, "Hello", ERL_NIF_LATIN1);
        return enif_make_tuple2(env, hello_string, world_atom);
    }
    
    static ErlNifFunc nif_funcs[] = { { "hello", 0, hello } };
    
    ERL_NIF_INIT(niftest, nif_funcs, load, NULL, NULL, NULL)
  • 二进制文件 - 类型为二进制的项使用结构类型 ErlNifBinary 进行访问,该结构包含指向原始二进制数据的指针(data)和数据大小(size),以字节为单位。 datasize 都是只读的,并且只能通过调用 API 函数来写入。 但是,ErlNifBinary 的实例始终由用户分配(通常作为局部变量)。

    只有在调用 enif_alloc_binaryenif_realloc_binary 之后,data 指向的原始数据才是可变的。 所有其他对二进制文件进行操作的函数都将数据保持为只读。 可变二进制文件最终必须使用 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 充当纯函数并且仅读取提供的参数,则 NIF 都是线程安全的,无需任何显式同步。 当您通过静态变量或 enif_priv_data 写入共享状态时,您需要提供自己的显式同步。 这包括在线程之间共享的进程独立环境中的项。 如果您将资源对象视为可变的,则它们也需要同步。

    库初始化回调 loadupgrade 即使对于共享状态数据也是线程安全的。

  • 版本管理 - 当构建 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 库中对时间测量的支持

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

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

    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
    如本手册页开头警告文本中所述,本机函数相对快速地返回至关重要。很难给出本机函数允许工作的确切最大时间量,但通常一个表现良好的本机函数应在 1 毫秒内返回给其调用者。这可以通过不同的方法实现。如果您可以完全控制本机函数中执行的代码,最好的方法是将工作划分为多个工作块,并多次调用本机函数。但是,并非总是可以这样做,例如,在调用第三方库时。

    可以使用 enif_consume_timeslice() 函数来告知运行时系统 NIF 调用的时长。通常,除非 NIF 执行速度非常快,否则始终应使用此函数。

    如果 NIF 调用太长,则必须通过以下方法之一处理,以避免响应能力下降、调度器负载平衡问题和其他奇怪的行为:

    • 让步 NIF - 如果可以拆分长时间运行的 NIF 的功能,以便通过一系列较短的 NIF 调用来实现其工作,则应用程序有两个选择:

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

      以这种方式分解长时间运行的函数使虚拟机可以在 NIF 调用之间重新获得控制权。

      从性能和系统特性角度来看,始终首选这种方法,而不是下面描述的其他替代方法。

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

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

      正确分类脏作业非常重要。I/O 绑定的作业应分类为 I/O 绑定,而 CPU 绑定的作业应分类为 CPU 绑定。如果您将 CPU 绑定的作业分类为 I/O 绑定的作业,则脏 I/O 调度程序可能会使普通调度程序陷入饥饿状态。I/O 绑定的作业应等待 I/O 或花费有限的时间来移动数据。

      要调度脏 NIF 执行,应用程序有两个选择:

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

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

      在进程执行脏 NIF 时,某些与其通信的操作可能需要很长时间才能完成。在脏 NIF 返回之前,无法挂起或垃圾回收执行脏 NIF 的进程。因此,等待此类操作完成的其他进程可能必须等待很长时间。阻塞多重调度(即,调用 erlang:system_flag(multi_scheduling, block))也可能需要很长时间才能完成。这是因为所有脏调度程序上所有正在进行的脏操作都必须在阻塞操作完成之前完成。

      但是,许多与执行脏 NIF 的进程通信的操作可以在执行脏 NIF 时完成。例如,通过 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 库的魔术宏。它应在全局文件范围内计算。

    MODULE 是 Erlang 模块的名称,用作不带字符串引号的标识符。它由宏字符串化。

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

    loadupgradeunload 是指向函数的指针。调用 loadupgrade 之一来初始化库。调用 unload 来释放库。所有内容都在下面单独描述。

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

    如果通过 --enable-static-nifs 编译用于静态包含的 NIF 库,则必须将宏 STATIC_ERLANG_NIF_LIBNAME 定义为存档文件的名称(不包括文件扩展名 .a),并且不带字符串引号。它只能包含 C 标识符中允许的字符。必须在包含 erl_nif.h 之前定义该宏。如果改为使用较旧的宏 STATIC_ERLANG_NIF,则存档文件的名称必须与模块的名称匹配。

  • int (*load)(ErlNifEnv* caller_env, void** priv_data, ERL_NIF_TERM load_info) - 当加载 NIF 库并且此模块不存在以前加载的库时,会调用 load

    如果库需要在 NIF 调用之间保持状态,则可以将 *priv_data 设置为指向某些私有数据。enif_priv_data 返回此指针。调用 load 时,*priv_data 初始化为 NULL

    load_infoerlang:load_nif/2 的第二个参数。

    如果 load 返回除 0 以外的任何值,则库加载失败。如果不需要初始化,则 load 可以为 NULL

  • int (*upgrade)(ErlNifEnv* caller_env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info) - 当加载 NIF 库并且此模块的旧代码已加载 NIF 库时,会调用 upgrade

    load 的工作方式相同,但 *old_priv_data 已经包含上次调用旧模块实例的 loadupgrade 设置的值。调用 upgrade 时,*priv_data 初始化为 NULL。允许写入 *priv_data*old_priv_data

    如果 upgrade 返回除 0 以外的任何值,或者 upgradeNULL,则库加载失败。

  • void (*unload)(ErlNifEnv* caller_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 调用之间存储指向进程绑定环境的指针是无用且危险的。

    • 回调环境
      作为第一个参数传递给所有非 NIF 回调函数 (loadupgradeunloaddtordownstopdyncall)。其工作方式类似于进程绑定环境,但使用一个临时伪进程,该伪进程在回调返回时“终止”。可以在此环境中创建项,但它们仅在回调期间可访问。

    • 进程独立环境
      通过调用 enif_alloc_env 创建。此环境可用于在 NIF 调用之间存储项,以及使用 enif_send 发送项。进程独立环境及其所有项在您使用 enif_free_envenif_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;

    通过其名称、arity 和实现描述 NIF。

    • fptr - 指向实现 NIF 的函数的指针。

    • argv - 包含传递给 NIF 的函数参数。

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

    • flags - 对于常规 NIF 为 0(因此对于静态初始化的 ErlNifFunc 实例可以省略其值)。

      flags 可用于指示 NIF 是一个 脏 NIF,该 NIF 将在脏调度器线程上执行。

      如果脏 NIF 预期为 CPU 密集型,则其 flags 字段应设置为 ERL_NIF_DIRTY_JOB_CPU_BOUNDERL_NIF_DIRTY_JOB_IO_BOUND

      注意

      如果设置了 ERL_NIF_DIRTY_JOB_*_BOUND 标志之一,并且运行时系统不支持脏调度器,则运行时系统会拒绝加载 NIF 库。

  • ErlNifBinary

    typedef struct {
        size_t 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

  • ErlNifOnHaltCallback

    typedef void ErlNifOnHaltCallback(void *priv_data);

    on halt 回调函数的函数原型。

    可以使用 enif_set_option() 安装 on halt 回调。当运行时系统正在停止时,将调用此类已安装的回调。

  • ErlNifOnUnloadThreadCallback

    typedef void ErlNifOnUnloadThreadCallback(void *priv_data);

    on_unload_thread 回调函数的函数原型。

    可以使用 enif_set_option() 安装 on_unload_thread 回调。当此模块实例被清除时,每个调度器线程将调用此类安装的回调。

  • ErlNifOption - 可以使用 enif_set_option() 设置的选项的枚举。

    当前有效的选项

    • ERL_NIF_OPT_DELAY_HALT - 启用运行时系统停止的延迟,同时启用刷新,直到 NIF 库中的所有 NIF 调用返回。

    • ERL_NIF_OPT_ON_HALT - 安装一个回调,该回调将在运行时系统停止且启用刷新时调用。

    • ERL_NIF_OPT_ON_UNLOAD_THREAD - 当 NIF 库所属的模块实例被清除为旧代码时,由每个调度器线程 安装一个回调,该回调将被调用。

  • ErlNifPid - 进程标识符 (pid)。与 pid 项(ERL_NIF_TERM 的实例)相反,ErlNifPid 是自包含的,并且不绑定到任何 环境ErlNifPid 是一个不透明类型。它可以被复制、在内存中移动、遗忘等等。

  • ErlNifPort - 端口标识符。与端口 ID 项(ERL_NIF_TERM 的实例)相反,ErlNifPort 是自包含的,并且不绑定到任何 环境ErlNifPort 是一个不透明类型。它可以被复制、在内存中移动、遗忘等等。

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

  • ErlNifResourceTypeInit

    typedef struct {
        ErlNifResourceDtor* dtor;       // #1 Destructor
        ErlNifResourceStop* stop;       // #2 Select stop
        ErlNifResourceDown* down;       // #3 Monitor down
        int members;
        ErlNifResourceDynCall* dyncall; // #4 Dynamic call
    } ErlNifResourceTypeInit;

    enif_open_resource_type_x enif_init_resource_type 读取的初始化结构。

  • ErlNifResourceDtor

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

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

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

  • ErlNifResourceDown

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

    资源 down 函数的函数原型,代表 enif_monitor_process 调用。obj 是资源,pid 是正在退出的被监视进程的身份,mon 是监视器的身份。

  • ErlNifResourceStop

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

    资源 stop 函数的函数原型,代表 enif_select 调用。obj 是资源,event 是 OS 事件,如果该调用直接来自 enif_selectis_direct_call 为 true,如果它是计划的调用(可能来自另一个线程)则为 false。

  • ErlNifResourceDynCall

    typedef void ErlNifResourceDynCall(ErlNifEnv* caller_env, void* obj, void* call_data);

    动态资源调用函数的函数原型,由 enif_dynamic_resource_call 调用。参数 obj 是资源对象,call_data 是传递的 enif_dynamic_resource_call 的最后一个参数。

  • ErlNifCharEncoding

    typedef enum {
        ERL_NIF_LATIN1,
        ERL_NIF_UTF8,
    }ErlNifCharEncoding;

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

  • 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_make_unique_integer 请求的属性的枚举。对于默认属性,请使用值 0

    • ERL_NIF_UNIQUE_POSITIVE - 仅返回正整数。

    • ERL_NIF_UNIQUE_MONOTONIC - 仅返回与创建时间对应的 严格单调递增的整数。

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

    • ERL_NIF_INTERNAL_HASH - 不可移植的哈希函数,它仅保证在单个 Erlang VM 实例中,相同项的哈希值相同。

      它接受 32 位盐值,并生成 0..2^32-1 范围内的哈希值。

    • ERL_NIF_PHASH2 - 可移植的哈希函数,它为相同的 Erlang 项提供相同的哈希值,而与机器架构和 ERTS 版本无关。

      它忽略盐值,并生成 0..2^27-1 范围内的哈希值。

      ERL_NIF_INTERNAL_HASH 慢。它对应于 erlang:phash2/1

  • SysIOVec - 系统 I/O 向量,如 Unix 上的 writev 和 Win32 上的 WSASend 所用。它在 ErlNifIOVec 中以及通过 enif_ioq_peek 使用。

  • ErlNifIOVec

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

    一个 I/O 向量,包含 iovcnt 个指向数据的 SysIOVec。它被 enif_inspect_iovecenif_ioq_enqv 使用。

  • ErlNifIOQueueOpts - 用于配置 ErlNifIOQueue 的选项。

    • ERL_NIF_IOQ_NORMAL - 创建一个普通的 I/O 队列

enif_alloc()

void * enif_alloc(
        size_t size);

分配 size 字节的内存。

如果分配失败,则返回 NULL

返回的指针适合于任何可以放入分配内存中的内置类型。

enif_alloc_binary()

int enif_alloc_binary(
        size_t size,
        ErlNifBinary* bin);

分配一个新的 size 字节大小的二进制数据。初始化由 bin 指向的结构,使其引用已分配的二进制数据。必须通过 enif_release_binary 释放二进制数据,或者通过 enif_make_binary 将所有权转移到 Erlang 项。分配的(和拥有的)ErlNifBinary 可以在 NIF 调用之间保留。

如果不需要重新分配或在 NIF 调用之间保持数据存活,请考虑使用 enif_make_new_binary,因为它会在可能的情况下在进程堆上分配小二进制数据。

成功时返回 true,如果分配失败,则返回 false

enif_alloc_env()

ErlNifEnv * enif_alloc_env();

分配一个新的进程独立环境。 该环境可用于保存未绑定到任何进程的项。 稍后可以使用 enif_make_copy 将这些项复制到进程环境,或使用 enif_send 作为消息发送到进程。

返回指向新环境的指针。

自 OTP R14B 起可用

enif_alloc_resource()

void * enif_alloc_resource(
        ErlNifResourceType* type,
        unsigned size);

分配一个类型为 type 且大小为 size 字节的内存管理资源对象。

自 OTP R13B04 起可用

enif_binary_to_term()

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

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

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

另请参见 ErlNifBinaryToTermerlang:binary_to_term/2enif_term_to_binary

自 OTP 19.0 起可用

enif_clear_env()

void enif_clear_env(ErlNifEnv* env);

释放环境中的所有项,并清除环境以供重用。该环境必须使用 enif_alloc_env 分配。

自 OTP R14B 起可用

enif_compare()

int enif_compare(
        ERL_NIF_TERM lhs,
        ERL_NIF_TERM rhs);

如果 lhs < rhs,则返回一个小于 0 的整数;如果 lhs = rhs,则返回 0;如果 lhs > rhs,则返回一个大于 0 的整数。对应于 Erlang 运算符 ==/==<<>=>(但不是 =:==/=)。

自 OTP R13B04 起可用

enif_compare_monitors()

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

比较两个 ErlNifMonitor。也可以用于暗示监视器的一些人为顺序,无论出于何种原因。

如果 monitor1monitor2 相等,则返回 0;如果 monitor1 < monitor2,则返回 < 0;如果 monitor1 > monitor2,则返回 > 0

自 OTP 20.0 起可用

enif_compare_pids()

int enif_compare_pids(
        const ErlNifPid *pid1,
        const ErlNifPid *pid2);

根据项顺序比较两个 ErlNifPid

如果 pid1pid2 相等,则返回 0;如果 pid1 < pid2,则返回 < 0;如果 pid1 > pid2,则返回 > 0

自 OTP 22.0 起可用

enif_cond_broadcast()

void enif_cond_broadcast(
        ErlNifCond *cnd);

erl_drv_cond_broadcast 相同。

自 OTP R13B04 起可用

enif_cond_create()

ErlNifCond * enif_cond_create(
        char *name);

erl_drv_cond_create 相同。

自 OTP R13B04 起可用

enif_cond_destroy()

void enif_cond_destroy(
        ErlNifCond *cnd);

erl_drv_cond_destroy 相同。

自 OTP R13B04 起可用

enif_cond_name()

char* enif_cond_name(
        ErlNifCond* cnd);

erl_drv_cond_name 相同。

自 OTP 21.0 起可用

enif_cond_signal()

void enif_cond_signal(
        ErlNifCond *cnd);

erl_drv_cond_signal 相同。

自 OTP R13B04 起可用

enif_cond_wait()

void enif_cond_wait(
        ErlNifCond *cnd,
        ErlNifMutex *mtx);

erl_drv_cond_wait 相同。

自 OTP R13B04 起可用

enif_consume_timeslice()

int enif_consume_timeslice(
        ErlNifEnv *env,
        int percent);

向运行时系统提示自上次提示以来,或自 NIF 开始以来(如果未指定先前的提示),当前 NIF 调用消耗了多少 CPU 时间。 该时间以进程在可以暂停以给其他可运行进程让出时间之前允许执行 Erlang 代码的时间片的百分比指定。 调度时间片不是一个精确的实体,但通常可以近似为大约 1 毫秒。

请注意,是否以及如何使用此信息由运行时系统决定。 某些平台上的实现可以使用其他方式来确定消耗的 CPU 时间。无论如何,较长的 NIF 都应频繁调用 enif_consume_timeslice 以确定是否允许继续执行。

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

如果时间片已用完,则返回 1,否则返回 0。 如果返回 1,则 NIF 应尽快返回以使进程屈服。

提供此函数是为了更好地支持协作调度,提高系统响应能力,并更轻松地防止由于 NIF 垄断调度器线程而导致的 VM 行为不当。 它可以用于将长时间工作划分为多个重复的 NIF 调用,而无需创建线程。

另请参阅本手册页开头的警告文本。

自 OTP R16B 起可用

enif_convert_time_unit()

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

将时间单位为 from 的值 val 转换为时间单位为 to 的对应值。结果使用向下取整函数进行四舍五入。

  • val - 要转换时间单位的值。

  • from - val 的时间单位。

  • to - 返回值的时间单位。

如果使用无效的时间单位参数调用,则返回 ERL_NIF_TIME_ERROR

另请参见 ErlNifTimeErlNifTimeUnit

自 OTP 18.3 起可用

enif_cpu_time()

ERL_NIF_TERM enif_cpu_time(
        ErlNifEnv *env);

以与 erlang:timestamp() 相同的格式返回 CPU 时间。 CPU 时间是当前逻辑 CPU 自过去某个任意点以来执行所花费的时间。如果操作系统不支持获取此值,则 enif_cpu_time 会调用 enif_make_badarg

自 OTP 19.0 起可用

enif_demonitor_process()

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

取消先前使用 enif_monitor_process 创建的监视器。参数 obj 是指向保存监视器的资源的指针,*mon 标识该监视器。

参数 caller_env 是调用线程的环境(进程绑定回调环境),如果从非 ERTS 生成的自定义线程调用,则为 NULL

如果成功识别并删除了监视器,则返回 0。 如果无法识别监视器,则返回非零值,这意味着该监视器要么

  • 从未为此资源创建
  • 已取消
  • 已触发
  • 即将被并发线程触发

此函数是线程安全的。

自 OTP 20.0 起可用

enif_dynamic_resource_call()

int enif_dynamic_resource_call(
        ErlNifEnv* caller_env,
        ERL_NIF_TERM rt_module,
        ERL_NIF_TERM rt_name,
        ERL_NIF_TERM resource,
        void* call_data);

调用由另一个 NIF 模块实现的资源类型的代码。原子 rt_modulert_name 标识要调用的资源类型。参数 resource 标识该类型的资源对象。

已标识资源类型的回调函数 dyncall 将被调用,并传入指向资源对象 obj 的指针以及通过的参数 call_datacall_data 参数通常是指向一个结构体的指针,该结构体用于向 dyncall 函数传递参数,并将结果返回给调用者。

如果 dyncall 回调函数被调用,则返回 0。如果没有调用,则返回非零值。如果 rt_modulert_name 没有标识具有 dyncall 回调的资源类型,或者如果 resource 不是该类型的资源对象,则会发生这种情况。

自 OTP 24.0 起可用

enif_equal_tids()

int enif_equal_tids(
        ErlNifTid tid1,
        ErlNifTid tid2);

erl_drv_equal_tids 相同。

自 OTP R13B04 起可用

enif_fprintf()

int enif_fprintf(
        FILE *stream,
        const char *format,
        ...);

类似于 fprintf,但此格式字符串还接受 "%T",它会格式化类型为 ERL_NIF_TERM 的 Erlang 项。

此函数主要用于调试目的。不建议使用 %T 打印非常大的项。即使成功,该函数也可能会更改 errno

自 OTP 21.0 起可用

enif_free()

void enif_free(
        void* ptr);

释放由 enif_alloc 分配的内存。

enif_free_env()

void enif_free_env(
        ErlNifEnv* env);

释放使用 enif_alloc_env 分配的环境。该环境中创建的所有项也会被释放。

自 OTP R14B 起可用

enif_free_iovec()

void enif_free_iovec(
        ErlNifIOVec* iov);

释放从 enif_inspect_iovec 返回的 io 向量。只有在将 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);

自 OTP 20.1 起可用

enif_get_atom()

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

buf 指向的缓冲区中写入一个以 NULL 结尾的字符串,该缓冲区的大小为 size 字节,该字符串由原子 term 的字符串表示形式组成,使用 编码

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

自 OTP R13B04 起可用

enif_get_atom_length()

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

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

成功时返回 true,如果 term 不是原子或者无法使用 encoding 对原子进行编码,则返回 false

自 OTP R14B 起可用

enif_get_double()

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

*dp 设置为 term 的浮点数值。

成功时返回 true,如果 term 不是浮点数,则返回 false

自 OTP R13B04 起可用

enif_get_int()

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

*ip 设置为 term 的整数值。

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

enif_get_int64()

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

*ip 设置为 term 的整数值。

成功时返回 true,如果 term 不是整数,或者超出有符号 64 位整数的范围,则返回 false

自 OTP R14B 起可用

enif_get_local_pid()

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

如果 term 是节点本地进程的 pid,则此函数将从它初始化 pid 变量 *pid 并返回 true。否则返回 false。不检查该进程是否处于活动状态。

注意

如果参数 term 是原子 undefinedenif_get_local_pid 将返回 false。

自 OTP R14B 起可用

enif_get_local_port()

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

如果 term 标识一个节点本地端口,则此函数将从它初始化端口变量 *port_id 并返回 true。否则返回 false。不检查该端口是否处于活动状态。

自 OTP 19.0 起可用

enif_get_list_cell()

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

从列表 list 设置 *head*tail

成功时返回 true,如果它不是列表或列表为空,则返回 false

enif_get_list_length()

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

*len 设置为列表 term 的长度。

成功时返回 true,如果 term 不是一个合适的列表,则返回 false

自 OTP R14B 起可用

enif_get_long()

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

*ip 设置为 term 的长整数值。

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

自 OTP R13B04 起可用

enif_get_map_size()

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

*size 设置为映射 term 中键值对的数量。

成功时返回 true,如果 term 不是映射,则返回 false

自 OTP 18.0 起可用

enif_get_map_value()

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

*value 设置为映射 map 中与 key 关联的值。

成功时返回 true,如果 map 不是映射,或者如果 map 不包含 key,则返回 false

自 OTP 18.0 起可用

enif_get_resource()

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

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

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

enif_get_resource 不会向资源对象添加引用。但是,保证在资源句柄 term 有效的情况下,在 *objp 中接收到的指针至少是有效的。

自 OTP R13B04 起可用

enif_get_string()

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

buf 指向的缓冲区中写入一个以 NULL 结尾的字符串,该缓冲区的大小为 size,该字符串由字符串 list 中的字符组成。使用 编码 写入字符。

返回以下值之一

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

除非缓冲区 size < 1,否则写入的字符串始终以 NULL 结尾。

自 OTP R13B04 起可用

enif_get_string_length()

int enif_get_string_length(
        ErlNifEnv *env,
        ERL_NIF_TERM list,
        unsigned *len,
        ErlNifCharEncoding encoding);

*len 设置为字符串 list 的长度(不包括终止 NULL 字符的字节数),使用 编码

成功时返回 true,如果 list 不是可以使用 encoding 编码的字符串,则返回 false

自 OTP 26.0 起可用

enif_get_tuple()

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

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

成功时返回 true,如果 term 不是元组,则返回 false

自 OTP R13B04 起可用

enif_get_uint()

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

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

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

自 OTP R13B04 起可用

enif_get_uint64()

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

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

如果成功,则返回 true;如果 term 不是无符号整数,或者超出了无符号 64 位整数的范围,则返回 false

自 OTP R14B 起可用

enif_get_ulong()

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

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

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

enif_getenv()

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

erl_drv_getenv 相同。

自 OTP 18.2 起可用

enif_has_pending_exception()

int enif_has_pending_exception(
        ErlNifEnv* env,
        ERL_NIF_TERM* reason);

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

另请参阅 enif_make_badargenif_raise_exception

自 OTP 18.0 起可用

enif_hash()

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

根据指定的 ErlNifHash type 哈希 term

所用盐(如果有)的范围和返回值取决于哈希类型。

自 OTP 20.0 起可用

enif_inspect_binary()

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

使用有关二进制项 bin_term 的信息初始化 bin 指向的结构。

如果成功,则返回 true;如果 bin_term 不是二进制项,则返回 false

enif_inspect_iolist_as_binary()

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

使用与 iolist 具有相同字节内容的连续缓冲区初始化 bin 指向的结构。与 inspect_binary 一样,bin 指向的数据是临时的,不需要释放。

如果成功,则返回 true;如果 iolist 不是 iolist,则返回 false

自 OTP R13B04 起可用

enif_inspect_iovec()

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

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

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

调用此函数时,iovec 应包含指向 NULL 的指针或一个如果可能应该使用的 ErlNifIOVec 结构。例如:

/* 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 函数返回之前有效。如果 iovec 应在 NIF 调用返回后有效,则可以使用 NULL 环境调用此函数。如果没有给定环境,则 iovec 拥有向量中的数据,并且必须使用 enif_free_iovec 显式释放它。

如果成功,则返回 true;如果 iovec_term 不是 iovec,则返回 false

自 OTP 20.1 起可用

enif_ioq_create()

ErlNifIOQueue * enif_ioq_create(
        ErlNifIOQueueOpts opts);

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

自 OTP 20.1 起可用

enif_ioq_destroy()

void enif_ioq_destroy(
        ErlNifIOQueue *q);

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

自 OTP 20.1 起可用

enif_ioq_deq()

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

从 I/O 队列中出列 count 个字节。如果 size 不是 NULL,则将队列的新大小放在此处。

如果成功,则返回 true;如果 I/O 不包含 count 个字节,则返回 false。失败时,队列保持不变。

自 OTP 20.1 起可用

enif_ioq_enq_binary()

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

bin 入列到 q 中,跳过前 skip 个字节。

如果成功,则返回 true;如果 skip 大于 bin 的大小,则返回 false。二进制数据的任何所有权都将转移到队列,并且 bin 将在 NIF 调用的其余部分中被视为只读,然后被释放。

自 OTP 20.1 起可用

enif_ioq_enqv()

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

iovec 入列到 q 中,跳过前 skip 个字节。

如果成功,则返回 true;如果 skip 大于 iovec 的大小,则返回 false

自 OTP 20.1 起可用

enif_ioq_peek()

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

将 I/O 队列作为指向 SysIOVec 数组的指针获取。它还返回 iovlen 中的元素数量。

此函数不会从队列中删除任何内容,必须使用 enif_ioq_deq 来完成。

返回的数组适合与 Unix 系统调用 writev 一起使用。

自 OTP 20.1 起可用

enif_ioq_peek_head()

int enif_ioq_peek_head(
        ErlNifEnv *env,
        ErlNifIOQueue *q,
        size_t *size,
        ERL_NIF_TERM *bin_term);

将 IO 队列的头部作为二进制项获取。

如果 size 不是 NULL,则将头部的大小放在此处。

此函数不会从队列中删除任何内容,必须使用 enif_ioq_deq 来完成。

如果成功,则返回 true;如果队列为空,则返回 false

自 OTP 21.0 起可用

enif_ioq_size()

size_t enif_ioq_size(
        ErlNifIOQueue *q);

获取 q 的大小。

自 OTP 20.1 起可用

enif_is_atom()

int enif_is_atom(
        ErlNifEnv* env,
        ERL_NIF_TERM term);

如果 term 是原子,则返回 true

自 OTP R13B04 起可用

enif_is_binary()

int enif_is_binary(
        ErlNifEnv* env,
        ERL_NIF_TERM term);

如果 term 是二进制项,则返回 true

enif_is_current_process_alive()

int enif_is_current_process_alive(
        ErlNifEnv* env);

如果当前正在执行的进程当前处于活动状态,则返回 true,否则返回 false

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

自 OTP 19.0 起可用

enif_is_empty_list()

int enif_is_empty_list(
        ErlNifEnv* env,
        ERL_NIF_TERM term);

如果 term 是空列表,则返回 true

自 OTP R13B04 起可用

enif_is_exception()

int enif_is_exception(
        ErlNifEnv* env,
        ERL_NIF_TERM term);

如果 term 是异常,则返回 true。

自 OTP R14B03 起可用

enif_is_fun()

int enif_is_fun(
        ErlNifEnv* env,
        ERL_NIF_TERM term);

如果 term 是 fun,则返回 true

自 OTP R13B04 起可用

enif_is_identical()

int enif_is_identical(
        ERL_NIF_TERM lhs,
        ERL_NIF_TERM rhs);

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

自 OTP R13B04 起可用

enif_is_list()

int enif_is_list(
        ErlNifEnv* env,
        ERL_NIF_TERM term);

如果 term 是列表,则返回 true

自 OTP R14B 起可用

enif_is_map()

int enif_is_map(
        ErlNifEnv* env,
        ERL_NIF_TERM term);

如果 term 是 map,则返回 true,否则返回 false

自 OTP 18.0 起可用

enif_is_number()

int enif_is_number(
        ErlNifEnv* env,
        ERL_NIF_TERM term);

如果 term 是数字,则返回 true

自 OTP R15B 起可用

enif_is_pid()

int enif_is_pid(
        ErlNifEnv* env,
        ERL_NIF_TERM term);

如果 term 是 pid,则返回 true

自 OTP R13B04 起可用

enif_is_pid_undefined()

int enif_is_pid_undefined(
        const ErlNifPid* pid);

如果 pid 已被 enif_set_pid_undefined 设置为未定义,则返回 true

自 OTP 22.0 起可用

enif_is_port()

int enif_is_port(
        ErlNifEnv* env,
        ERL_NIF_TERM term);

如果 term 是端口,则返回 true

自 OTP R13B04 起可用

enif_is_port_alive()

int enif_is_port_alive(
        ErlNifEnv* env,
        ErlNifPort *port_id);

如果 port_id 处于活动状态,则返回 true

此函数是线程安全的。

自 OTP 19.0 起可用

enif_is_process_alive()

int enif_is_process_alive(
        ErlNifEnv* env,
        ErlNifPid *pid);

如果 pid 处于活动状态,则返回 true

此函数是线程安全的。

自 OTP 19.0 起可用

enif_is_ref()

int enif_is_ref(
        ErlNifEnv* env,
        ERL_NIF_TERM term);

如果 term 是引用,则返回 true

自 OTP R13B04 起可用

enif_is_tuple()

int enif_is_tuple(
        ErlNifEnv* env,
        ERL_NIF_TERM term);

如果 term 是元组,则返回 true

自 OTP R14B 起可用

enif_keep_resource()

int enif_keep_resource(
        void* obj);

向从 enif_alloc_resource 获取的资源对象 obj 添加引用。每次调用对象的 enif_keep_resource 都必须通过在对象被销毁之前调用 enif_release_resource 来平衡。

自 OTP R14B 起可用

enif_make_atom()

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

从以 NULL 结尾的 C 字符串 name 创建具有 ISO Latin-1 编码的原子项。如果 name 的长度超过了原子允许的最大长度(255 个字符),则 enif_make_atom 会调用 enif_make_badarg

enif_make_atom_len()

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

从长度为 len 且具有 ISO Latin-1 编码的字符串 name 创建原子项。NULL 字符被视为任何其他字符。如果 len 超过了原子允许的最大长度(255 个字符),则 enif_make_atom 会调用 enif_make_badarg

自 OTP R14B 起可用

enif_make_badarg()

ERL_NIF_TERM enif_make_badarg(
        ErlNifEnv* env);

创建一个 badarg 异常,使其从 NIF 返回,并将其与环境 env 关联。一旦 NIF 或其调用的任何函数调用了 enif_make_badarg,运行时将确保在 NIF 返回时引发 badarg 异常,即使 NIF 尝试返回非异常项也是如此。

enif_make_badarg 的返回值只能用作调用它的 NIF(直接或间接)的返回值,或者传递给 enif_is_exception,而不能传递给任何其他 NIF API 函数。

另请参阅 enif_has_pending_exceptionenif_raise_exception

注意

在 ERTS 7.0 (Erlang/OTP 18) 之前,enif_make_badarg 的返回值必须从 NIF 返回。现在取消了这个要求,因为如果调用了 enif_make_badarg,则会忽略 NIF 的返回值。

enif_make_binary()

ERL_NIF_TERM enif_make_binary(
        ErlNifEnv* env,
        ErlNifBinary* bin);

bin 创建一个二进制项。二进制数据的所有权将转移到创建的项,并且 bin 在 NIF 调用的其余部分被视为只读,然后被释放。

enif_make_copy()

ERL_NIF_TERM enif_make_copy(
        ErlNifEnv* dst_env,
        ERL_NIF_TERM src_term);

创建项 src_term 的副本。副本在环境 dst_env 中创建。源项可以位于任何环境中。

自 OTP R14B 起可用

enif_make_double()

ERL_NIF_TERM enif_make_double(
        ErlNifEnv* env,
        double d);

double 创建一个浮点数项。如果参数 double 不是有限数或为 NaN,则 enif_make_double 会调用 enif_make_badarg

自 OTP R13B04 起可用

enif_make_existing_atom()

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

尝试使用 encoding 从以 NULL 结尾的 C 字符串 name 创建一个已存在原子项。

如果原子已存在,则此函数将该项存储在 *atom 中并返回 true,否则返回 false。如果字符串 name 超过了原子允许的最大长度(255 个字符),或者 name 未正确编码,也会返回 false

自 OTP R13B04 起可用

enif_make_existing_atom_len()

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

尝试使用 encoding 从长度为 len 字节的字符串 name 创建一个已存在原子项。NULL 字符被视为任何其他字符。

如果原子已存在,则此函数将该项存储在 *atom 中并返回 true,否则返回 false。如果字符串 name 超过了原子允许的最大长度(255 个字符),或者 name 未正确编码,也会返回 false

自 OTP R14B 起可用

enif_make_int()

ERL_NIF_TERM enif_make_int(
        ErlNifEnv* env,
        int i);

创建一个整数项。

enif_make_int64()

ERL_NIF_TERM enif_make_int64(
        ErlNifEnv* env,
        ErlNifSInt64 i);

从一个带符号的 64 位整数创建一个整数项。

自 OTP R14B 起可用

enif_make_list()

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

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

如果 cnt 为 0,则返回一个空列表。

enif_make_list1()

enif_make_list2()

enif_make_list3()

enif_make_list4()

enif_make_list5()

enif_make_list6()

enif_make_list7()

enif_make_list8()

enif_make_list9()

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,以便在参数数量不匹配时获得编译时错误。

自 OTP R13B04 起可用

enif_make_list_cell()

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

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

enif_make_list_from_array()

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

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

如果 cnt 为 0,则返回一个空列表。

自 OTP R13B04 起可用

enif_make_long()

ERL_NIF_TERM enif_make_long(
        ErlNifEnv* env,
        long int i);

long int 创建一个整数项。

自 OTP R13B04 起可用

enif_make_map_put()

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 map_in 的副本,并插入带有 valuekey。如果 key 已经在 map_in 中存在,则旧的关联值将被 value 替换。

如果成功,此函数将 *map_out 设置为新的 map 并返回 true。如果 map_in 不是一个 map,则返回 false

map_in 项必须属于环境 env

自 OTP 18.0 起可用

enif_make_map_remove()

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_out 中创建 map_in 的副本,并删除 key 和关联的值。如果 map map_in 不包含 key,则 *map_out 将设置为 map_in

成功时返回 true,如果 map_in 不是 map,则返回 false

map_in 项必须属于环境 env

自 OTP 18.0 起可用

enif_make_map_update()

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 map_in 的副本,并将 key 的旧关联值替换为 new_value

如果成功,此函数将 *map_out 设置为新的 map 并返回 true。如果 map_in 不是 map,或者不包含 key,则返回 false

map_in 项必须属于环境 env

自 OTP 18.0 起可用

enif_make_map_from_arrays()

int enif_make_map_from_arrays(
        ErlNifEnv* env,
        ERL_NIF_TERM keys[],
        ERL_NIF_TERM values[],
        size_t cnt,
        ERL_NIF_TERM *map_out);

从给定的键和值创建一个 map 项。

如果成功,此函数将 *map_out 设置为新的 map 并返回 true。如果存在任何重复的键,则返回 false

所有键和值都必须属于 env

自 OTP 21.0 起可用

enif_make_monitor_term()

ERL_NIF_TERM enif_make_monitor_term(
        ErlNifEnv* env,
        const ErlNifMonitor* mon);

创建一个标识从 enif_monitor_process 接收到的给定监视器的项。

此函数主要用于调试目的。

自 OTP 22.0 起可用

enif_make_new_atom()

int enif_make_new_atom(
        ErlNifEnv *env,
        const char *name,
        ERL_NIF_TERM *atom,
        ErlNifCharEncoding encoding);

使用 encoding 从以 NULL 结尾的 C 字符串 name 创建一个原子项。

如果成功,则返回 true,并且原子项存储在 *atom 中。

否则,如果 name 的长度超过了原子允许的最大长度(255 个字符),或者 name 未正确编码,则返回 false

自 OTP 26.0 起可用

enif_make_new_atom_len()

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

使用 encoding 从长度为 len 字节的字符串 name 创建一个原子项。

如果成功,则返回 true,并且原子项存储在 *atom 中。

否则,如果字符串超过了原子允许的最大长度(255 个字符),或者字符串未正确编码,则返回 false

自 OTP 26.0 起可用

enif_make_new_binary()

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

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

返回指向原始二进制数据的指针,并将 *termp 设置为二进制项。

自 OTP R14B 起可用

enif_make_new_map()

ERL_NIF_TERM enif_make_new_map(
        ErlNifEnv* env);

创建一个空 map 项。

自 OTP 18.0 起可用

enif_make_pid()

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

*pid 创建一个 pid 项或原子 undefined

自 OTP R14B 起可用

enif_make_ref()

ERL_NIF_TERM enif_make_ref(
        ErlNifEnv* env);

创建一个类似于 erlang:make_ref/0 的引用。

自 OTP R13B04 起可用

enif_make_resource()

ERL_NIF_TERM enif_make_resource(
        ErlNifEnv* env,
        void* obj);

创建一个指向由 enif_alloc_resource 获取的内存管理资源对象的 opaque 句柄。不执行所有权转移,因为资源对象仍然需要由 enif_release_resource 释放。但是请注意,调用 enif_release_resource 可以在从 enif_make_resource 获取 term 后立即发生,在这种情况下,当 term 被垃圾回收时,资源对象将被释放。有关更多详细信息,请参阅用户指南中创建和返回资源对象的示例

注意

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

  • 当传递给 enif_get_resource 时,两个资源 term 仅当它们产生相同的资源对象指针时才相等。

  • 可以使用 term_to_binary 序列化资源 term,如果资源对象在调用 binary_to_term 时仍然存在,则可以稍后完全重新创建它。如果资源对象已被释放,则 binary_to_term 将返回一个过时的资源 term。对于过时的资源 term,enif_get_resource 将返回 false。

    当在消息中将资源 term 传递到远程节点并再次返回时,也适用相同的序列化原则。资源 term 将在除其资源对象仍然存在于内存中的节点之外的所有节点上表现为过时。

在 ERTS 9.0 (OTP-20.0) 之前,所有资源 term 都彼此相等,并且与空二进制文件 (<<>>) 相等。如果被序列化,它们将被重新创建为空的二进制文件。

自 OTP R13B04 起可用

enif_make_resource_binary()

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

创建一个由资源对象 obj (通过 enif_alloc_resource 获取) 进行内存管理的二进制 term。返回的二进制 term 由 data 指向的 size 个字节组成。在调用资源的析构函数之前,必须保持此原始二进制数据可读且未更改。二进制数据可以存储在资源对象外部,在这种情况下,析构函数负责释放数据。

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

enif_make_resource 一样,不执行所有权转移。仍然需要使用 enif_release_resource 释放资源。

自 OTP R14B 起可用

enif_make_reverse_list()

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

*list_out 设置为列表 list_in 的反向列表,并返回 true;如果 list_in 不是列表,则返回 false

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

list_in term 必须属于环境 env

自 OTP R15B 起可用

enif_make_string()

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

创建一个包含以 encoding 编码的以 NULL 结尾的字符串 string 的字符的列表。

enif_make_string_len()

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

创建一个包含长度为 len 且具有 encoding 编码的字符串 string 的字符的列表。 NULL 字符被视为任何其他字符。

自 OTP R14B 起可用

enif_make_sub_binary()

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 中完整字节的数量。

自 OTP R13B04 起可用

enif_make_tuple()

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

创建一个 arity 为 cnt 的 tuple term。期望 cnt 个类型为 ERL_NIF_TERM 的参数 (在 cnt 之后) 作为 tuple 的元素。

enif_make_tuple1()

enif_make_tuple2()

enif_make_tuple3()

enif_make_tuple4()

enif_make_tuple5()

enif_make_tuple6()

enif_make_tuple7()

enif_make_tuple8()

enif_make_tuple9()

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);

创建一个长度由函数名称指示的 tuple term。优先使用这些函数 (宏) 而不是可变参数 enif_make_tuple,以便在参数数量不匹配时获得编译时错误。

自 OTP R13B04 起可用

enif_make_tuple_from_array()

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

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

自 OTP R13B04 起可用

enif_make_uint()

ERL_NIF_TERM enif_make_uint(
        ErlNifEnv* env,
        unsigned int i);

unsigned int 创建一个整数 term。

自 OTP R13B04 起可用

enif_make_uint64()

ERL_NIF_TERM enif_make_uint64(
        ErlNifEnv* env,
        ErlNifUInt64 i);

从无符号 64 位整数创建一个整数 term。

自 OTP R14B 起可用

enif_make_ulong()

ERL_NIF_TERM enif_make_ulong(
        ErlNifEnv* env,
        unsigned long i);

unsigned long int 创建一个整数 term。

enif_make_unique_integer()

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

自 OTP 19.0 起可用

enif_map_iterator_create()

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

通过初始化 iter 指向的结构,为映射 map 创建一个迭代器。参数 entry 确定迭代器的起始位置: ERL_NIF_MAP_ITERATOR_FIRSTERL_NIF_MAP_ITERATOR_LAST

成功时返回 true,如果 map 不是映射,则返回 false。

映射迭代器仅在 map 所属的环境 env 的生命周期内有用。必须通过调用 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);

注意

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

自 OTP 18.0 起可用

enif_map_iterator_destroy()

void enif_map_iterator_destroy(
        ErlNifEnv *env,
        ErlNifMapIterator *iter);

销毁由 enif_map_iterator_create 创建的映射迭代器。

自 OTP 18.0 起可用

enif_map_iterator_get_pair()

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

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

成功时,设置 *key*value 并返回 true。如果迭代器位于头部(在第一个条目之前)或尾部(超出最后一个条目),则返回 false

自 OTP 18.0 起可用

enif_map_iterator_is_head()

int enif_map_iterator_is_head(
        ErlNifEnv *env,
        ErlNifMapIterator *iter);

如果映射迭代器 iter 位于第一个条目之前,则返回 true

自 OTP 18.0 起可用

enif_map_iterator_is_tail()

int enif_map_iterator_is_tail(
        ErlNifEnv *env,
        ErlNifMapIterator *iter);

如果映射迭代器 iter 位于最后一个条目之后,则返回 true

自 OTP 18.0 起可用

enif_map_iterator_next()

int enif_map_iterator_next(
        ErlNifEnv *env,
        ErlNifMapIterator *iter);

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

如果迭代器现在位于有效的键值条目,则返回 true;如果迭代器位于尾部(超出最后一个条目),则返回 false

自 OTP 18.0 起可用

enif_map_iterator_prev()

int enif_map_iterator_prev(
        ErlNifEnv *env,
        ErlNifMapIterator *iter);

递减映射迭代器以指向上一个键值条目。

如果迭代器现在位于有效的键值条目,则返回 true;如果迭代器位于头部(在第一个条目之前),则返回 false

自 OTP 18.0 起可用

enif_monitor_process()

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

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

参数 obj 是指向资源的指针,用于保存监视器,*target_pid 标识要监视的本地进程。

如果 mon 不是 NULL,则成功调用会将监视器的标识存储在 mon 指向的 ErlNifMonitor 结构中。此标识符用于引用监视器,以便稍后使用 enif_demonitor_process 删除或与 enif_compare_monitors 比较。当监视器触发或资源被释放时,将自动删除监视器。

参数 caller_env 是调用线程的环境(进程绑定回调环境),如果从非 ERTS 生成的自定义线程调用,则为 NULL

成功时返回 0,如果没有提供 down 回调,则返回 < 0;如果进程不再活动或 target_pid未定义,则返回 > 0。

此函数是线程安全的。

自 OTP 20.0 起可用

enif_monotonic_time()

ErlNifTime enif_monotonic_time(
        ErlNifTimeUnit time_unit);

返回当前的 Erlang 单调时间。请注意,出现负值并不少见。

time_unit 是返回值的单位。

如果使用无效的时间单位参数调用,或者从非调度器线程调用,则返回 ERL_NIF_TIME_ERROR

另请参见 ErlNifTimeErlNifTimeUnit

自 OTP 18.3 起可用

enif_mutex_create()

ErlNifMutex * enif_mutex_create(
        char *name);

erl_drv_mutex_create 相同。

自 OTP R13B04 起可用

enif_mutex_destroy()

void enif_mutex_destroy(
        ErlNifMutex *mtx);

erl_drv_mutex_destroy 相同。

自 OTP R13B04 起可用

enif_mutex_lock()

void enif_mutex_lock(
        ErlNifMutex *mtx);

erl_drv_mutex_lock 相同。

自 OTP R13B04 起可用

enif_mutex_name()

char* enif_mutex_name(
        ErlNifMutex* mtx);

erl_drv_mutex_name 相同。

自 OTP 21.0 起可用

enif_mutex_trylock()

int enif_mutex_trylock(
        ErlNifMutex *mtx);

erl_drv_mutex_trylock 相同。

自 OTP R13B04 起可用

enif_mutex_unlock()

void enif_mutex_unlock(
        ErlNifMutex *mtx);

erl_drv_mutex_unlock 相同。

自 OTP R13B04 起可用

enif_now_time()

ERL_NIF_TERM enif_now_time(
        ErlNifEnv *env);

返回一个 erlang:now() 时间戳。

此函数已弃用。

自 OTP 19.0 起可用

enif_open_resource_type()

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 库尚未创建的新实例调用。

这两个标志值可以使用按位或组合。资源类型名称是调用模块的本地名称。参数 module_str 尚未(仍然)使用,必须为 NULL。如果不需要析构函数,则 dtor 可以为 NULL

成功时,该函数返回指向资源类型的指针,并且 *tried 设置为 ERL_NIF_RT_CREATEERL_NIF_RT_TAKEOVER 以指示已完成的操作。失败时,返回 NULL 并将 *tried 设置为 flags。允许将 tried 设置为 NULL

请注意,仅允许在两个回调 loadupgrade 中调用 enif_open_resource_type。仅当调用的 load/upgrade 函数成功返回时,才会创建或接管资源类型。

另请参见 enif_open_resource_type_x

自 OTP R13B04 起可用

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_selectenif_monitor_process 一起使用的资源类型的其他回调函数。

参数 init 是指向 ErlNifResourceTypeInit 结构的指针,该结构包含资源类型的析构函数、down 和 stop 回调的函数指针。

注意

只有 ErlNifResourceTypeInit 中的成员 dtordownstopenif_open_resource_type_x 读取。要实现新的 dyncall 回调,请使用 enif_init_resource_type

自 OTP 20.0 起可用

enif_init_resource_type()

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

enif_open_resource_type_x 相同,不同之处在于它接受用于与 enif_dynamic_resource_call 一起使用的资源类型的附加回调函数。

参数 init 是指向 ErlNifResourceTypeInit 结构的指针,该结构包含回调函数指针 dtordownstop 和新的 dyncall。该结构还包含字段 members,该字段必须设置为从结构顶部开始计数的已初始化回调的数量。例如,要初始化包括 dyncall 在内的所有回调,members 应设置为 4。所有回调都是可选的,可以设置为 NULL

自 OTP 24.0 起可用

enif_port_command()

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。端口 ID 指的是本地节点上的端口。

  • msg_env - 消息项的环境。可以是使用 enif_alloc_envNULL 分配的进程独立环境。

  • msg - 要发送的消息项。与 erlang:port_command/2 的有效负载适用相同的限制。

使用 NULLmsg_env 是一种优化,它将对 enif_alloc_envenif_make_copyenif_port_commandenif_free_env 的调用分组为一个调用。仅当大多数项要从 env 复制到 msg_env 时,此优化才有用。

如果命令成功发送,则返回 true。如果命令失败,则返回 false,例如

  • *to_port 不引用本地端口。
  • 当前正在执行的进程(即发送方)未处于活动状态。
  • msg 无效。

另请参见 enif_get_local_port

自 OTP 19.0 起可用

enif_priv_data()

void * enif_priv_data(
        ErlNifEnv* env);

返回指向由 loadupgrade 设置的私有数据的指针。

自 OTP R13B04 起可用

enif_raise_exception()

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

自 OTP 18.0 起可用

enif_realloc()

void * enif_realloc(
        void* ptr,
        size_t size);

将由 enif_alloc 分配的内存重新分配为 size 字节。

如果重新分配失败,则返回 NULL

返回的指针适合于任何可以放入分配内存中的内置类型。

自 OTP 20.2 起可用

enif_realloc_binary()

int enif_realloc_binary(
        ErlNifBinary* bin,
        size_t size);

更改二进制文件 bin 的大小。源二进制文件可以是只读的,在这种情况下,它保持不变,并分配一个可变副本并将其分配给 *bin

成功时返回 true,如果内存分配失败,则返回 false

自 OTP R13B04 起可用

enif_release_binary()

void enif_release_binary(
        ErlNifBinary* bin);

释放从 enif_alloc_binary 获取的二进制文件。

enif_release_resource()

void enif_release_resource(
        void* obj);

删除对从 enif_alloc_resource 获取的资源对象 obj 的引用。当删除最后一个引用时,将析构资源对象。每次调用 enif_release_resource 都必须对应于先前对 enif_alloc_resourceenif_keep_resource 的调用。由 enif_make_resource 创建的引用只能由垃圾回收器删除。

无法保证何时调用未引用资源的析构函数。它可以由 enif_release_resource 直接调用,但也可以安排在稍后由另一个线程调用。

自 OTP R13B04 起可用

enif_rwlock_create()

ErlNifRWLock * enif_rwlock_create(
        char *name);

erl_drv_rwlock_create 相同。

自 OTP R13B04 起可用

enif_rwlock_destroy()

void enif_rwlock_destroy(
        ErlNifRWLock *rwlck);

erl_drv_rwlock_destroy 相同。

自 OTP R13B04 起可用

enif_rwlock_name()

char* enif_rwlock_name(
        ErlNifRWLock* rwlck);

erl_drv_rwlock_name 相同。

自 OTP 21.0 起可用

enif_rwlock_rlock()

void enif_rwlock_rlock(
        ErlNifRWLock *rwlck);

erl_drv_rwlock_rlock 相同。

自 OTP R13B04 起可用

enif_rwlock_runlock()

void enif_rwlock_runlock(
        ErlNifRWLock *rwlck);

erl_drv_rwlock_runlock 相同。

自 OTP R13B04 起可用

enif_rwlock_rwlock()

void enif_rwlock_rwlock(
        ErlNifRWLock *rwlck);

erl_drv_rwlock_rwlock 相同。

自 OTP R13B04 起可用

enif_rwlock_rwunlock()

void enif_rwlock_rwunlock(
        ErlNifRWLock *rwlck);

erl_drv_rwlock_rwunlock 相同。

自 OTP R13B04 起可用

enif_rwlock_tryrlock()

int enif_rwlock_tryrlock(
        ErlNifRWLock *rwlck);

erl_drv_rwlock_tryrlock 相同。

自 OTP R13B04 起可用

enif_rwlock_tryrwlock()

int enif_rwlock_tryrwlock(
        ErlNifRWLock *rwlck);

erl_drv_rwlock_tryrwlock 相同。

自 OTP R13B04 起可用

enif_schedule_nif()

ERL_NIF_TERM enif_schedule_nif(
        ErlNifEnv* caller_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[]);

调度 NIF fp 执行。此函数允许应用程序将长时间运行的工作分解为多个常规 NIF 调用,或者调度一个脏 NIF在脏调度器线程上执行。

  • caller_env - 必须是调用 NIF 的进程绑定环境。

  • fun_name - 为计划执行的 NIF 提供一个名称。如果无法转换为原子,则 enif_schedule_nif 将返回 badarg 异常。

  • flags - 对于常规 NIF,必须设置为 0。如果模拟器在构建时启用了脏调度器支持,则如果该作业预计是 CPU 密集型的,则 flags 可以设置为 ERL_NIF_DIRTY_JOB_CPU_BOUND,对于 I/O 密集型的作业,则可以设置为 ERL_NIF_DIRTY_JOB_IO_BOUND。如果模拟器中没有脏调度器线程,则尝试调度此类作业会导致 notsup 异常。

  • argcargv - 可以是传递到调用 NIF 的原始值,也可以是调用 NIF 创建的值。

调用 NIF 必须使用 enif_schedule_nif 的返回值作为其自身的返回值。

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

自 OTP 17.3 起可用

enif_select()

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_STOPERL_NIF_SELECT_CANCEL,将在下面进一步描述。当触发读取或写入事件时,会将如下通知消息发送到由 pid 标识的进程

{select, Obj, Ref, ready_input | ready_output}

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

注意

为了完全控制消息格式,请使用 erts-11.0 (OTP-22.0) 中引入的更新函数enif_select_readenif_select_write

参数 pid 可以是 NULL 以指示调用进程。它不能设置为未定义

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

参数 ref 必须是从 erlang:make_ref/0 获取的引用,或者原子 undefined。它将在通知中作为 Ref 传递。如果使用选择性的 receive 语句等待通知,则在 receive 之前创建的引用将利用运行时优化,绕过队列中所有先前接收到的消息。

通知是一次性的。要接收相同类型的进一步通知(读取或写入),在接收到每个通知后必须重复调用 enif_select

ERL_NIF_SELECT_CANCEL 可用于取消先前选择的事件。它必须与 ERL_NIF_SELECT_READ 和/或 ERL_NIF_SELECT_WRITE 按位或组合使用,以指示要取消的事件类型。当指定 ERL_NIF_SELECT_CANCEL 时,将忽略参数 pidref。返回值将指示事件是否确实已取消,或者是否可能已发送通知。

使用 ERL_NIF_SELECT_STOP 作为 mode,以便安全地关闭已传递给 enif_select 的事件对象。当可以安全地关闭事件对象时,将调用资源 objstop 回调。即使已收到(或取消)所有通知并且没有进一步调用 enif_select,也必须使用这种安全关闭事件对象的方式。 ERL_NIF_SELECT_STOP 将首先取消任何选定的事件,然后再调用或计划 stop 回调。当指定 ERL_NIF_SELECT_STOP 时,将忽略参数 pidref

对特定操作系统 event 的首次调用 enif_select 将在事件对象和包含资源之间建立关系。 对 event 的所有后续调用都必须将其包含资源作为参数 obj 传递。 当 enif_selectmodeERL_NIF_SELECT_STOP 并且相应的 stop 回调已返回时,该关系将解除。 一个资源可以包含多个事件对象,但一个事件对象只能包含在一个资源中。 在所有包含的关系都解除之前,资源不会被销毁。

注意

enif_monitor_processenif_select 一起使用,以检测失败的 Erlang 进程,并防止它们导致资源及其包含的操作系统事件对象的永久泄漏。

成功时返回非负值,其中可以设置以下位

  • ERL_NIF_SELECT_STOP_CALLED - enif_select 直接调用了 stop 回调。

  • ERL_NIF_SELECT_STOP_SCHEDULED - stop 回调被计划在其他线程上运行,或稍后由此线程运行。

  • ERL_NIF_SELECT_READ_CANCELLED - 读取事件已通过 ERL_NIF_SELECT_CANCELERL_NIF_SELECT_STOP 取消,并保证不会生成 ready_input 通知消息。

  • ERL_NIF_SELECT_WRITE_CANCELLED - 写入事件已通过 ERL_NIF_SELECT_CANCELERL_NIF_SELECT_STOP 取消,并保证不会生成 ready_output 通知消息。

如果调用失败,则返回负值,其中可以设置以下位

  • ERL_NIF_SELECT_INVALID_EVENT - 参数 event 不是有效的操作系统事件对象。

  • 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) {
    /* ... */
}

注意

在 erts-11.0 (OTP-22.0) 中引入了 mode 标志 ERL_NIF_SELECT_CANCEL 和返回标志 ERL_NIF_SELECT_READ_CANCELLEDERL_NIF_SELECT_WRITE_CANCELLED

自 OTP 20.0 起可用

enif_select_read()

自 OTP 22.0 起可用

enif_select_write()

int enif_select_read(
        ErlNifEnv* env,
        ErlNifEvent event,
        void* obj,
        const ErlNifPid* pid,
        ERL_NIF_TERM msg,
        ErlNifEnv* msg_env);
int enif_select_write(
        ErlNifEnv* env,
        ErlNifEvent event,
        void* obj,
        const ErlNifPid* pid,
        ERL_NIF_TERM msg,
        ErlNifEnv* msg_env);

这些是 enif_select 的变体,您可以在其中提供自己的消息术语 msg,该术语将发送到进程,而不是预定义的元组 {select,_,_,_}.

参数 msg_env 必须是 NULL 或使用 enif_alloc_env 分配的 msg 的环境。 如果参数 msg_envNULL,则将复制术语 msg,否则,成功调用 enif_select_readenif_select_write 将使 msgmsg_env 失效。 然后使用 enif_free_env 释放环境,或者使用 enif_clear_env 清理环境以重新使用。 不成功的调用将使 msgmsg_env 仍然有效。

除了消息格式外,enif_select_readenif_select_write 的行为与 enif_select 完全相同,参数 modeERL_NIF_SELECT_READERL_NIF_SELECT_WRITE。 要取消或关闭事件,请使用 enif_select

自 OTP 22.0 起可用

enif_self()

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

初始化 *pid 处的 ErlNifPid 变量以表示调用进程。

如果成功,则返回 pid,如果 caller_env 不是 进程绑定环境,则返回 NULL。

自 OTP R14B 起可用

enif_send()

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

向进程发送消息。

  • caller_env - 调用线程的环境(进程绑定回调 环境),如果从非 ERTS 生成的自定义线程调用,则为 NULL

  • *to_pid - 接收进程的 PID。该 PID 必须指向本地节点上的进程。

  • msg_env - 消息项的环境。必须是使用 enif_alloc_env 分配的进程独立环境,或者为 NULL。

  • msg - 要发送的消息项。

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

  • *to_pid 不指向存活的本地进程。
  • 当前正在执行的进程(即发送方)未处于活动状态。

成功调用 enif_send 后,消息环境 msg_env 及其所有项(包括 msg)将失效。该环境要么使用 enif_free_env 释放,要么使用 enif_clear_env 清除以便重用。如果调用失败,则 msgmsg_env 仍然有效。

如果将 msg_env 设置为 NULL,则会复制 msg 项,并且调用后原始项及其环境仍然有效。

此函数是线程安全的。

注意

仅在 ERTS 8.0 (Erlang/OTP 19) 及更高版本中支持将 msg_env 作为 NULL 传递。

自 OTP R14B 起可用

enif_set_option()

int enif_set_option(
        ErlNifEnv *env,
        ErlNifOption opt,
	...);

设置一个选项。成功时返回零。失败时返回非零值。当前可以设置以下选项:

  • ERL_NIF_OPT_DELAY_HALT

    enif_set_option(env, ERL_NIF_OPT_DELAY_HALT)

    启用运行时系统暂停延迟,并启用刷新,直到 NIF 库中所有对 NIF 的调用都返回。如果尚未启用延迟暂停功能,即使进程仍在 NIF 库的 NIF 中执行,启用刷新的暂停也可能完成。请注意,返回在这里指的是 NIF 将控制权返回给运行时系统的第一个点,而不是对 NIF 的调用向调用该 NIF 的 Erlang 代码返回值的点。也就是说,如果在系统暂停时,您从 NIF 中使用 enif_schedule_nif() 安排 NIF 的执行,即使已为 NIF 库启用延迟暂停,安排的 NIF 调用也不会执行。

    当调用其中一个 erlang:halt() BIF 时,运行时系统会暂停。默认情况下启用刷新,但可以使用 erlang:halt/2 BIF 禁用。禁用刷新后,延迟暂停设置将不起作用。也就是说,即使启用了延迟暂停设置,运行时系统也会在不等待 NIF 返回的情况下暂停。有关详细信息,请参阅 erlang:halt/2{flush, boolean()} 选项。

    ERL_NIF_OPT_DELAY_HALT 选项只能在 NIF 库加载期间在 NIF 库 load()upgrade() 调用内部调用 enif_set_option() 时设置,如果在其他位置设置则会失败。 env 参数必须是传递给 load()upgrade() 调用的回调环境。此选项也只能设置一次。也就是说,一旦启用,延迟暂停设置就无法更改。延迟暂停设置与加载 NIF 库的模块实例相关联。也就是说,如果加载了使用 NIF 库的新旧版本的模块,它们可以具有相同或不同的延迟暂停设置。

    延迟暂停功能可以与 on halt 回调结合使用。在这种情况下,on halt 回调通常用于通知库中 NIF 中阻塞的进程,现在应该返回,以便让运行时系统完成暂停。此类 NIF 应为脏 NIF,因为普通 NIF 绝不应长时间阻塞。

  • ERL_NIF_OPT_ON_HALT

    enif_set_option(env, ERL_NIF_OPT_ON_HALT, on_halt)

    安装一个回调,该回调将在运行时系统启用刷新的情况下暂停时调用。

    当调用其中一个 erlang:halt() BIF 时,运行时系统会暂停。默认情况下启用刷新,但可以使用 erlang:halt/2 BIF 禁用。禁用刷新后,即使安装了任何 on halt 回调,运行时系统也会在不调用任何 on halt 回调的情况下暂停。有关详细信息,请参阅 erlang:halt/2{flush, boolean()} 选项。

    ERL_NIF_OPT_ON_HALT 选项只能在 NIF 库加载期间在 NIF 库 load()upgrade() 调用内部调用 enif_set_option() 时设置,如果在其他位置调用则会失败。 env 参数必须是传递给 load()upgrade() 调用的回调环境。on_halt 参数应为要安装的回调的函数指针。

    on_halt 回调将与加载 NIF 库的模块实例相关联。也就是说,如果加载了使用 NIF 库的新旧版本的模块,它们可以都安装了不同的、没有或者相同的 on halt 回调。在 代码清除期间卸载 NIF 库时,已安装的 on halt 回调将被卸载。ERL_NIF_OPT_ON_HALT 选项也只能设置一次。也就是说,一旦通过清除加载 NIF 库的模块实例以外的任何其他方式安装了 on halt 回调,就无法更改或删除它。

    当调用已安装的 on halt 回调时,它将传递一个指向 priv_data 的指针作为参数。 priv_data 指针可以在加载 NIF 库时设置。

    on halt 回调可以与 延迟暂停 结合使用,直到所有对库的调用都返回。在这种情况下,on halt 回调通常用于通知库中 NIF 中阻塞的进程,现在应该返回,以便让运行时系统完成暂停。此类 NIF 应为脏 NIF,因为普通 NIF 绝不应长时间阻塞。

  • ERL_NIF_OPT_ON_UNLOAD_THREAD

    enif_set_option(env, ERL_NIF_OPT_ON_UNLOAD_THREAD, on_unload_thread)

    安装一个回调,当 NIF 库所属的模块实例被清除为旧版本时,该回调将由每个调度器线程调用。一个典型的用途是释放线程特定的数据。

    ERL_NIF_OPT_ON_UNLOAD_THREAD 选项只能在加载 NIF 库期间在 load()upgrade() 调用内部设置,如果在其他位置调用则会失败。 env 参数必须是传递给 load()upgrade() 调用的回调环境。

    on_unload_thread 参数应为要安装的回调的函数指针。on_unload_thread 回调将与加载 NIF 库的模块实例相关联。也就是说,如果加载了使用 NIF 库的新旧版本的模块,它们可以都安装了不同的、没有或者相同的 on_unload_thread 回调。ERL_NIF_OPT_ON_UNLOAD_THREAD 选项只能设置一次,并且一旦为模块实例安装后就无法更改或删除。

    当调用已安装的 on_unload_thread 回调时,它将传递一个指向 priv_data 的指针作为参数。 priv_data 指针可以在加载 NIF 库时设置。

    on_unload_thread 函数的调用由不同的调度器线程并发进行。线程之间没有强制同步。但是,只有在所有对 on_unload_thread 的调用都返回后,才会对模块实例进行 unload() 回调的最终调用。

自 OTP 26.0 起可用

enif_set_pid_undefined()

void enif_set_pid_undefined(
        ErlNifPid* pid);

ErlNifPid 变量设置为未定义。请参阅 enif_is_pid_undefined

自 OTP 22.0 起可用

enif_sizeof_resource()

unsigned enif_sizeof_resource(
        void* obj);

获取通过 enif_alloc_resource 获取的资源对象 obj 的字节大小。

自 OTP R13B04 起可用

enif_snprintf()

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

类似于 snprintf,但此格式字符串还接受 "%T",它格式化类型为 ERL_NIF_TERM 的 Erlang 项。

此函数主要用于调试目的。不建议使用 %T 打印非常大的项。即使成功,该函数也可能会更改 errno

自 OTP 19.0 起可用

enif_system_info()

void enif_system_info(
        ErlNifSysInfo *sys_info_ptr,
        size_t size);

driver_system_info 相同。

自 OTP R13B04 起可用

enif_term_to_binary()

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

使用 enif_alloc_binary 分配新的二进制数据,并存储根据 Erlang 外部项格式编码 term 的结果。

成功时返回 true,如果分配失败,则返回 false

另请参阅 erlang:term_to_binary/1enif_binary_to_term

自 OTP 19.0 起可用

enif_term_type()

ErlNifTermType enif_term_type(
        ErlNifEnv *env,
        ERL_NIF_TERM term);

确定给定项的类型。该项必须是一个普通的 Erlang 项,而不是由 enif_raise_exceptionenif_schedule_nif 或类似函数返回的特殊项。

目前定义了以下类型:

  • ERL_NIF_TERM_TYPE_ATOM

  • ERL_NIF_TERM_TYPE_BITSTRING - 位串或二进制数据

  • ERL_NIF_TERM_TYPE_FLOAT

  • ERL_NIF_TERM_TYPE_FUN

  • ERL_NIF_TERM_TYPE_INTEGER

  • ERL_NIF_TERM_TYPE_LIST - 列表,可以是空的,也可以是非空的

  • ERL_NIF_TERM_TYPE_MAP

  • ERL_NIF_TERM_TYPE_PID

  • ERL_NIF_TERM_TYPE_PORT

  • ERL_NIF_TERM_TYPE_REFERENCE

  • ERL_NIF_TERM_TYPE_TUPLE

请注意,将来可能会添加新的类型,因此调用者必须准备好处理未知类型。

自 OTP 22.0 起可用

enif_thread_create()

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

erl_drv_thread_create 相同。

自 OTP R13B04 起可用

enif_thread_exit()

void enif_thread_exit(
        void *resp);

erl_drv_thread_exit 相同。

自 OTP R13B04 起可用

enif_thread_join()

int enif_thread_join(
        ErlNifTid tid,
        void **respp);

erl_drv_thread_join 相同。

自 OTP R13B04 起可用

enif_thread_name()

char* enif_thread_name(
        ErlNifTid tid);

erl_drv_thread_name 相同。

自 OTP 21.0 起可用

enif_thread_opts_create()

ErlNifThreadOpts * enif_thread_opts_create(
        char *name);

erl_drv_thread_opts_create 相同。

自 OTP R13B04 起可用

enif_thread_opts_destroy()

void enif_thread_opts_destroy(
        ErlNifThreadOpts *opts);

erl_drv_thread_opts_destroy 相同。

自 OTP R13B04 起可用

enif_thread_self()

ErlNifTid enif_thread_self(void);

erl_drv_thread_self 相同。

自 OTP R13B04 起可用

enif_thread_type()

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 调度器线程。

自 OTP 19.0 起可用

enif_time_offset()

ErlNifTime enif_time_offset(
        ErlNifTimeUnit time_unit);

返回 Erlang 单调时间Erlang 系统时间 之间的当前时间偏移量,并转换为作为参数传递的 time_unit 单位。

time_unit 是返回值的单位。

如果使用无效的时间单位参数调用,或者从非调度器线程调用,则返回 ERL_NIF_TIME_ERROR

另请参见 ErlNifTimeErlNifTimeUnit

自 OTP 18.3 起可用

enif_tsd_get()

void * enif_tsd_get(
        ErlNifTSDKey key);

erl_drv_tsd_get 相同。

自 OTP R13B04 起可用

enif_tsd_key_create()

int enif_tsd_key_create(
        char *name,
        ErlNifTSDKey *key);

erl_drv_tsd_key_create 相同。

自 OTP R13B04 起可用

enif_tsd_key_destroy()

void enif_tsd_key_destroy(
        ErlNifTSDKey key);

erl_drv_tsd_key_destroy 相同。

自 OTP R13B04 起可用

enif_tsd_set()

void enif_tsd_set(
        ErlNifTSDKey key,
        void *data);

erl_drv_tsd_set 相同。

自 OTP R13B04 起可用

enif_vfprintf()

int enif_vfprintf(
        FILE *stream,
        const char *format,
        va_list ap);

等效于 enif_fprintf,不同之处在于它是使用 va_list 而不是可变数量的参数调用的。

自 OTP 21.0 起可用

enif_vsnprintf()

int enif_vsnprintf(
        char *str,
        size_t size,
        const char *format,
        va_list ap);

等效于 enif_snprintf,不同之处在于它是使用 va_list 而不是可变数量的参数调用的。

自 OTP 21.0 起可用

enif_whereis_pid()

int enif_whereis_pid(
        ErlNifEnv *caller_env,
        ERL_NIF_TERM name,
        ErlNifPid *pid);

通过其注册名称查找进程。

  • caller_env - 调用线程的环境(进程绑定回调 环境),如果从非 ERTS 生成的自定义线程调用,则为 NULL

  • name - 注册进程的名称,作为原子。

  • *pid - 存储已解析进程 ID 的 ErlNifPid

如果成功,则将 *pid 设置为使用 name 注册的本地进程,并返回 true。如果 name 不是已注册的进程,或者不是原子,则返回 false,并且 *pid 不会更改。

作用与 erlang:whereis/1 相同,但仅限于进程。请参阅 enif_whereis_port 以解析已注册的端口。

自 OTP 20.0 起可用

enif_whereis_port()

int enif_whereis_port(
        ErlNifEnv *caller_env,
        ERL_NIF_TERM name,
        ErlNifPort *port);

通过其注册名称查找端口。

  • caller_env - 调用线程的环境(进程绑定回调 环境),如果从非 ERTS 生成的自定义线程调用,则为 NULL

  • name - 注册端口的名称,作为原子。

  • *port - 存储已解析端口 ID 的 ErlNifPort

如果成功,则将 *port 设置为使用 name 注册的端口,并返回 true。如果 name 不是已注册的端口,或者不是原子,则返回 false,并且 *port 不会更改。

作用与 erlang:whereis/1 相同,但仅限于端口。请参阅 enif_whereis_pid 以解析已注册的进程。

自 OTP 20.0 起可用

另请参阅

erlang:load_nif/2 NIF(教程) 调试 NIF 和端口驱动程序