非常教程

Erlang 20参考手册

ERTS/erl_driver

erl_driver

C库

erl_driver

图书馆摘要

用于Erlang驱动程序的API函数。

描述

Erlang驱动程序是包含一组本地驱动程序回调函数的库,当某些事件发生时,Erlang虚拟机会调用这些函数。一个驱动程序可以有多个实例,每个实例都与一个Erlang端口相关联。

警告

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

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

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

从ERTS 5.5.3开始,驱动程序界面已扩展(请参见扩展标记)。扩展接口引入 版本管理,在驱动程序初始化时将能力标志(请参阅 driver_flags)传递给运行时系统,以及一些新的驱动程序API函数。

注意

从ERTS 5.9开始,旧的驱动程序必须重新编译并使用扩展接口。它们还必须调整到支持 64位的驱动程序接口

驱动程序使用erl_driver.h中声明的API函数调用模拟器。它们用于从驱动程序输出数据,使用定时器等。

每个驱动程序实例都与一个端口相关联。每个港口都有一个港口拥有者流程。与港口的通信通常通过港口所有者进程完成。大部分函数都将端口句柄作为参数。这标识了驱动程序实例。请注意,该端口句柄必须由驱动程序存储,当从仿真程序调用驱动程序时不会发出该请求(请参阅 driver_entry)。

一些函数采用ErlDrvBinary类型的参数 ,一个驱动程序二进制文件。它将被调用者分配和释放。直接使用二进制文件可避免一次额外的数据复制。

许多输出函数都有一个“头缓冲区”,带有 hbuf和hlen参数。该缓冲区在发送二进制文件(或列表,取决于端口模式)之前作为列表发送。匹配从端口收到的消息时,这很方便。(尽管在最新的Erlang版本中有二进制语法,它使您能够在二进制文件的开头匹配。)

在支持SMP的运行时系统中,驱动程序被锁定在驱动程序级别或端口级别(驱动程序实例级别)上。默认情况下会使用驱动程序级锁定,即一次只有一个仿真程序线程将在驱动程序中执行代码。如果使用端口级锁定,多个仿真器线程可以同时在驱动程序中执行代码。不过,一次只有一个线程会调用对应于同一端口的驱动程序回调。要启用端口级锁定,请在驱动程序 使用的driver_entry中设置ERL_DRV_FLAG_USE_PORT_LOCKING 驱动程序标志。当使用端口级锁定时,驱动程序编写器负责将所有访问同步到由端口共享的数据(驱动程序实例)。

如果使用驱动程序级别锁定,大多数在具有SMP支持的运行时系统之前编写的驱动程序可以运行在支持SMP的运行系统中,而不会被重写。

注意

假定驱动不访问其他驱动。如果驱动程序彼此访问,则它们必须提供自己的线程安全同步机制。这种“驱动间的沟通”是强烈的不鼓励。

以前,在没有SMP支持的运行时系统中,特定的驱动程序回调总是从同一个线程调用。这是不是在支持SMP的运行时系统的情况。无论使用哪种锁定方案,都可以从不同的线程调用驱动程序回调。例如,对于完全相同的端口,可以从两个不同的线程连续调用两次完全相同的回调。这对大多数驱动来说不是问题,但可以。依赖于在同一个线程中调用的所有回调的驱动程序必须在具有SMP支持的运行时系统中使用之前重写。

注意

无论使用哪种锁定方案,都可以从不同的线程调用驱动程序回调。

这个API中的大多数函数都不是线程安全的,也就是说,它们不能从任何线程调用。没有记录为线程安全的函数只能从驱动程序回调或从驱动程序回调调用中降序的函数调用中调用。请注意,可以从不同的线程调用驱动程序回调。但是,这对于此API中的任何函数都不是问题,因为模拟器可以控制这些线程。

警告

功能没有明确记录为线程安全是 不是线程安全的。另请注意,某些函数在具有SMP支持的运行时系统中使用时仅为线程安全的。

没有明确记录为线程安全的函数可能在某个时间点在运行时系统中具有线程安全的实现。但是,这样的实现可以随时更改为线程不安全的实现,恕不另行通知

只能使用明确记录为任意线程的线程安全的函数。

正如本节开头的警告文字所述,驱动程序回调速度相对较快至关重要。很难给出允许驱动程序回调工作的确切最大时间量,但通常情况良好的驱动程序回调将在1毫秒内返回。这可以通过使用不同的方法来实现。如果您完全控制要在驱动程序回调中执行的代码,最好的方法是将工作划分为多个工作块,并使用零超时触发多次调用 超时回调。函数erl_drv_consume_timeslice可以用于确定何时触发此类超时回拨呼叫。但是,有时它不能以这种方式实现,例如调用第三方库时。在这种情况下,您通常希望将工作分派给另一个线程。关于线程原语的信息在下面提供。

功能

驱动程序需要用Erlang完成的所有功能都是通过驱动程序API函数执行的。函数存在以下功能:

定时器功能

控制驱动程序可以使用的计时器。计时器会在指定的时间后调用超时输入功能。每个驱动程序实例只有一个计时器可用。

队列处理

每个驱动程序实例都有一个关联队列 该队列是一个 SysIOVec,它用作缓冲区。它主要用于驱动程序缓冲要写入设备的数据,它是一个字节流。如果端口所有者进程关闭了驱动程序,并且队列不为空,则驱动程序未关闭。这使驱动程序可以在关闭前清空其缓冲区。

如果使用端口数据锁,则可以从任何线程处理队列。有关更多信息,请参阅ErlDrvPDL

输出功能

通过这些功能,驱动程序将数据发送回仿真器。数据作为邮件被端口所有者进程接收,请参阅 erlang:open_port / 2。矢量函数和采用驱动程序二进制的函数速度更快,因为它们避免复制数据缓冲区。还有一种从驱动程序发送条款的快速方式,无需通过二进制术语格式。

失败

驱动程序可以退出并发出Erlang错误信号。这只适用于严重错误,当驱动程序不可能保持打开。

异步调用

Erlang / OTP R7B及更高版本提供了异步函数调用,使用Erlang提供的线程池。还有一个select调用,可以用于异步驱动程序。

多线程

提供了一个像多线程API一样的POSIX线程。Erlang驱动程序线程API仅提供POSIX线程API提供的功能的子集。提供的子集或多或少是多线程编程所需的基本功能:

  • Threads
  • Mutexes
  • Condition variables
  • Read/write locks
  • Thread-specific data

Erlang驱动程序线程API可以与POSIX线程API一起使用,在un-ice上使用,在Windows上可以与Windows原生线程API结合使用。Erlang驱动程序线程API具有可移植性的优点,但在某些情况下,您希望使用POSIX线程API或Windows本机线程API的功能。

Erlang驱动程序线程API只在从错误条件中恢复时才返回错误代码。如果从错误条件中恢复是不合理的,则整个运行时系统将被终止。例如,如果创建互斥锁操作失败,则返回错误代码,但如果互斥锁操作失败,则整个运行时系统将终止。

注意,Erlang驱动程序线程API中没有“条件变量等待超时”。这是因为pthread_cond_timedwait当系统时钟突然改变时,并不总是保证您会按预期的方式从呼叫中醒来。Erlang运行时系统必须能够应付系统时钟的突然变化。因此,我们从Erlang驱动程序线程API中省略了它。在Erlang驱动程序的情况下,超时可以而且将要使用Erlang驱动程序API的计时器功能来处理。

为了使Erlang驱动程序线程API发挥作用,必须在运行时系统中启用线程支持。,Erlang驱动程序可以检查是否启用了线程支持。driver_system_info请注意,Erlang驱动程序api中的某些函数只有在运行时系统支持smp时才是线程安全的,并且可以通过driver_system_info还请注意,Erlang驱动程序API中的许多函数是线程安全,无论是否启用SMP支持。如果函数未被记录为线程安全,则为线安全。

在仿真器线程中执行时,非常重要你解锁在让线程脱离控制之前已锁定的锁;否则为极有可能将整个仿真器锁住。

如果您需要在模拟器线程中使用特定于线程的数据,那么在线程处于您的控制之下时,只有线程特定的数据集,并且在让线程超出控制之前清除线程特定的数据。

将来,调试功能可能会与Erlang驱动程序线程API集成。创建实体的所有函数都采用name争论。目前name参数未使用,但在实现调试功能时将使用它。如果您命名所有创建良好的实体,调试功能将能够为您提供更好的错误报告。

添加/删除驱动程序

驱动程序可以添加驱动程序,然后再删除驱动程序。

监测过程

驱动程序可以监视不拥有端口的进程。

版本管理

版本管理对于设置了extended_marker他们的领域driver_entryERL_DRV_EXTENDED_MARKER...erl_driver.h定义:

  • ERL_DRV_EXTENDED_MARKER
  • ERL_DRV_EXTENDED_MAJOR_VERSION,当对Erlang运行时系统进行不兼容的驱动程序更改时,它将递增。通常,在以下情况下重新编译驱动程序就足够了。ERL_DRV_EXTENDED_MAJOR_VERSION已经发生了变化,但在罕见的情况下,这意味着驱动程序必须稍微修改一下。如果是这样的话,这当然会被记录在案。
  • ERL_DRV_EXTENDED_MINOR_VERSION,当添加新功能时,它将递增。运行时系统使用驱动程序的次要版本来确定要使用的特性。

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

仿真器拒绝加载不使用扩展驱动程序接口的驱动程序,以允许使用64位驱动程序,因为 在Erlang / OTP R15B中引入了对回调输出控制调用的不兼容类型更改 。使用旧类型编写的驱动程序会编译警告,并在调用模拟器时返回垃圾大小,导致它读取随机内存并创建巨大的错误结果blob。

因此仅仅重新编译用R15B类型的版本管理编写的驱动程序是不够的; 类型必须在驱动程序建议其他重写中更改,尤其是关于大小变量。重新编译时调查所有警告。

此外,API驱动函数driver_output *和 driver_vec_to_buf,driver_alloc / realloc的*和 driver_ *队列功能改变为具有更大的长度参数和返回值。这是一个较小的问题,因为通过较小类型的代码会在调用中自动转换它们,并且只要驱动程序不处理溢出int的大小,所有操作都将像以前一样工作。

时间测量

支持驱动程序中的时间测量:

  • ErlDrvTime
  • ErlDrvTimeUnit
  • erl_drv_monotonic_time
  • erl_drv_time_offset
  • erl_drv_convert_time_unit

重写64位驱动程序接口

ERTS 5.9引入了两种新的整数类型,ErlDrvSizeTErlDrvSSizeT,如果有必要,可以容纳64位大小。

为了不更新驱动程序而只重新编译,它可能在构建32位计算机时工作,从而产生错误的安全性。希望这将产生许多重要的警告。但是,当稍后为64位计算机重新编译相同的驱动程序时,威尔是警告和几乎肯定的崩溃。所以这是推迟更新驱动程序而不修正警告的想法。

当重新编译时gcc,使用标志-Wstrict-prototypes得到更好的警告。如果使用其他编译器,请尝试查找类似的标志。

以下是重写前ERTS 5.9驱动程序的清单,最重要的是首先:

驱动程序回调的返回类型

重写驱动程序回调control使用返回类型ErlDrvSSizeT而不是int...

重写驱动程序回调call使用返回类型ErlDrvSSizeT而不是int...

这些更改对于避免仿真器崩溃或更糟地导致故障是必不可少的。没有它们,驱动程序就可以将32位高的垃圾返回给模拟器,从而导致它从随机字节中构建一个巨大的结果,要么崩溃内存分配,要么通过驱动程序调用获得随机结果。

驱动程序回调的参数

output现在驱动程序回调将ErlDrvSizeT作为第三个参数而不是先前的int

control现在,驱动程序回调将获得ErlDrvSizeT第4和第6个参数,而不是先前的参数int

call现在,驱动程序回调将获得ErlDrvSizeT第4和第6个参数,而不是先前的参数int

Sane编译器的调用约定可能仅仅需要驱动程序处理需要64位大小字段(大多数大于2 GB,因为这是int32位可以容纳的数据块)的数据块才需要进行这些更改。但有可能会想到非理智的调用约定,这会使驱动程序回调混淆导致故障的论据。

参数类型的变化是从signed到unsigned。这可能会导致问题,例如,如果您只是在整个地方更改类型的循环终止条件或错误条件。

更大size字段ErlIOVec

size现场ErlIOVec已更改为ErlDrvSizeTint。检查所有使用该字段的代码。

自动类型转换可能仅在遇到大于32位的驱动程序时才需要进行这些更改。

size字段从已签名变为未签名。这可能会导致问题,例如,如果您只是在整个地方更改类型的循环终止条件或错误条件。

参数和返回驱动程序API中的值。

许多驱动程序API函数都将参数类型和/或返回值ErlDrvSizeT从主要改为int。自动类型转换可能仅在遇到大于32位的驱动程序时才需要进行这些更改。

driver_output第三参数driver_output2第三和第五参数driver_output_binary第三,第五和第六参数driver_outputv第三和第五参数driver_vec_to_buf第三参数和返回值driver_alloc第一参数driver_realloc第二参数driver_alloc_binary第一参数driver_realloc_binary第二参数driver_enq第三参数driver_pushq第三参数driver_deq第二参数和返回值driver_sizeq返回值driver_enq_bin第三和第四参数driver_pushq_bin第三和第四参数driver_enqv第三参数driver_pushqv第三个参数driver_peekqv返回值

这是从签名到未签名的变化。这可能会导致问题,例如,如果您只是在整个地方更改类型,则循环终止条件和错误条件。

数据类型

ErlDrvSizeT

用作无符号整数类型size_t

ErlDrvSSizeT

一个有符号的整数类型,大小ErlDrvSizeT

ErlDrvSysInfo

typedef struct ErlDrvSysInfo {
   int driver_major_version;
   int driver_minor_version;
   char *erts_version;
   char *otp_release;
   int thread_support;
   int smp_support;
   int async_threads;
   int scheduler_threads;
   int nif_major_version;
   int nif_minor_version;
   int dirty_scheduler_support;
} ErlDrvSysInfo;

ErlDrvSysInfo结构用于存储有关Erlang运行时系统的信息。driver_system_info在传递给ErlDrvSysInfo结构的引用时写入系统信息。结构中的字段如下所示:

driver_major_version

ERL_DRV_EXTENDED_MAJOR_VERSION运行时系统编译时的值。该值与ERL_DRV_EXTENDED_MAJOR_VERSION编译驱动程序时使用的值相同; 否则运行时系统会拒绝加载驱动程序。

driver_minor_version

价值ERL_DRV_EXTENDED_MINOR_VERSION在编译运行时系统时。此值可能与ERL_DRV_EXTENDED_MINOR_VERSION在编译驱动程序时使用。

erts_version

包含运行时系统版本号的字符串(与返回值相同erlang:system_info(version))。

otp_release

包含OTP版本号的字符串(与返回的相同erlang:system_info(otp_release))。

thread_support

如果运行时系统具有线程支持,则为!= 0值;否则0

smp_support

运行时系统具有SMP支持时的值!= 0;否则0

async_threads

异步线程池中使用的异步线程数driver_async(与返回值相同erlang:system_info(thread_pool_size))。

scheduler_threads

运行时系统使用的调度程序线程数(与返回的相同erlang:system_info(schedulers))。

nif_major_version

价值ERL_NIF_MAJOR_VERSION在编译运行时系统时。

nif_minor_version

价值ERL_NIF_MINOR_VERSION在编译运行时系统时。

dirty_scheduler_support

!= 0如果运行时系统支持脏调度程序线程,则为值; 否则0

ErlDrvBinary

typedef struct ErlDrvBinary {
   ErlDrvSint orig_size;
   char orig_bytes[];
} ErlDrvBinary;

ErlDrvBinary结构是一个二进制文件,在仿真器和驱动程序之间发送。所有的二进制文件都是引用计数 当driver_binary_free被调用时,引用计数递减,当它达到零时,二进制被解除分配。orig_size是二进制大小,orig_bytes是缓冲区。ErlDrvBinary没有固定的大小,其大小是orig_size + 2 * sizeof(int)

refc字段已被删除。一个的引用计数ErlDrvBinary现在存储在别处。的引用计数ErlDrvBinary可以通过被访问driver_binary_get_refcdriver_binary_inc_refcdriver_binary_dec_refc

一些驱动程序调用,例如driver_enq_binary,增加驱动程序引用计数,以及其他,如driver_deq减少它。

使用驱动程序二进制而不是普通缓冲区通常更快,因为模拟器不需要复制数据,只使用指针。

驱动程序中分配的驱动程序二进制文件将与驱动程序一起driver_alloc_binary释放(除非另有说明)driver_free_binary。(请注意,如果在仿真器中仍然引用了驱动程序,则这不一定会将其解除分配,否则ref-count不会为零。)

驱动程序二进制文件用于driver_output2driver_outputv呼叫,并在队列中。还有驱动程序的回调outputv使用驱动程序二进制文件。

如果驱动程序出于某种原因想要保留一个驱动程序二进制文件,例如在一个静态变量中,引用计数将被增加,并且二进制文件稍后可以在stop回调中被释放driver_free_binary

注意,驱动程序和模拟器共享作为驱动程序的二进制文件。驱动程序不得更改从模拟器接收到的二进制文件或发送给模拟器的二进制文件。

由于ERTS 5.5(Erlang/OTP R11B)orig_bytes保证正确对齐以存储双精度数组(通常为8字节对齐)。

ErlDrvData

驱动程序特定数据的句柄,传递给驱动程序回调。它是一个指针,通常是对驱动程序中的特定指针进行类型转换。

SysIOVec

系统I/O矢量,writev在Unix和WSASendWin32上使用。它用于ErlIOVec

ErlIOVec

typedef struct ErlIOVec {
  int vsize;
  ErlDrvSizeT size;
  SysIOVec* iov;
  ErlDrvBinary** binv;
} ErlIOVec;

仿真器和驱动程序使用的I / O向量是一个二进制文件列表,SysIOVec指向二进制文件的缓冲区。它用于driver_outputvoutputv驱动程序回调。另外,驱动程序队列是ErlIOVec

ErlDrvMonitor

当驱动程序为进程创建监视器时,将ErlDrvMonitor填充 。这是一种不透明的数据类型,可以分配给不透明的数据类型,但不使用提供的比较函数进行比较(即,它的行为类似于结构)。

驱动程序编写器将在调用时提供用于存储显示器的内存driver_monitor_process。数据的地址不会存储在驱动程序之外,因此ErlDrvMonitor可以用作任何其他数据,它可以被复制,移动到内存中,被遗忘等等。

ErlDrvNowData

ErlDrvNowData结构包含一个时间戳,由过去某个任意点的三个值组成。这三个结构成员是:

megasecs自任意时间点以来经过的全部兆字节数自任意时间点以来经过secs的全部秒数自任意时间点microsecs以来经过的全部微秒数ErlDrvPDL

如果某些特定于端口的数据必须从调用驱动程序回调的线程访问,则可以使用端口数据锁来同步对数据的操作。当前,模拟器与端口数据锁关联的唯一特定端口数据是驱动程序队列。

通常情况下,驱动程序实例没有端口数据锁定。如果驱动程序实例想要使用端口数据锁定,则必须通过调用创建端口数据锁定driver_pdl_create

一旦创建了端口数据锁,每次访问与端口数据锁相关的数据都必须在端口数据锁被锁定时完成。端口数据锁被锁定和解锁。driver_pdl_lock,和driver_pdl_unlock分别。

端口数据锁定是引用计数,并且当引用计数达到零时,它将被销毁。仿真器至少会在创建锁定时递增引用计数,并在与锁关联的端口终止后递减引用计数。仿真程序还会在异步作业入队时递增引用计数,并在调用异步作业时递减引用计数。另外,驾驶员有责任确保参考计数在驾驶员最后一次使用锁定之前未达到零。引用计数可以被读取,递增,并且由递减driver_pdl_get_refcdriver_pdl_inc_refcdriver_pdl_dec_refc分别。

ErlDrvTid

线程标识符

又见erl_drv_thread_createerl_drv_thread_exiterl_drv_thread_joinerl_drv_thread_self,和erl_drv_equal_tids

ErlDrvThreadOpts

int suggested_stack_size;

传递给erl_drv_thread_create4.存在以下领域:

suggested_stack_size以千瓦为单位,建议使用多大的堆栈。值<0表示默认大小。

另见erl_drv_thread_opts_createerl_drv_thread_opts_destroyerl_drv_thread_create

ErlDrvMutex

互斥锁。用于同步对共享数据的访问。一次只有一个线程可以锁定互斥对象。

另见erl_drv_mutex_create,,,erl_drv_mutex_destroy,,,erl_drv_mutex_lock,,,erl_drv_mutex_trylock,和erl_drv_mutex_unlock...

ErlDrvCond

条件变量。在线程在继续执行之前必须等待特定条件出现时才使用。条件变量必须与关联的互斥体一起使用。

另见erl_drv_cond_createerl_drv_cond_destroyerl_drv_cond_signalerl_drv_cond_broadcast,和erl_drv_cond_wait

ErlDrvRWLock

读/写锁。用于允许多个线程读取共享数据,同时只允许一个线程写入相同的数据。多个线程可以同时读取锁定rwlock,而一次只有一个线程可以读取/写入锁定rwlock。

另见erl_drv_rwlock_createerl_drv_rwlock_destroyerl_drv_rwlock_rlockerl_drv_rwlock_tryrlockerl_drv_rwlock_runlockerl_drv_rwlock_rwlockerl_drv_rwlock_tryrwlock,和erl_drv_rwlock_rwunlock

ErlDrvTSDKey

可与线程特定数据关联的键。

又见erl_drv_tsd_key_createerl_drv_tsd_key_destroyerl_drv_tsd_set,和erl_drv_tsd_get

ErlDrvTime

用于时间表示的带符号64位整数类型。

ErlDrvTimeUnit

驱动程序API支持的时间单位的枚举:

ERL_DRV_SEC秒数ERL_DRV_MSEC毫秒ERL_DRV_USEC微秒ERL_DRV_NSEC纳秒

出口

void add_driver_entry(ErlDrvEntry *de)

将驱动程序项添加到Erlang已知的驱动程序列表中。大init参数函数de叫做。

使用这个函数来添加动态加载代码中的驱动程序是很危险的。如果添加的驱动程序的驱动程序代码与.so正常动态加载的驱动程序(加载该erl_ddll接口)驻留在相同的动态加载模块(即文件)中,则调用driver_lock_driver程序将在添加驱动程序条目之前进行调用。

一般不赞成使用此功能。

void *driver_alloc(ErlDrvSizeT size)

分配在中指定大小的内存块size,并返回它。这只会在内存不足时NULL返回,在这种情况下会返回。(这通常是一个包装malloc)。

内存分配必须明确释放与相应的调用driver_free(除非另有说明)。

这个函数是线程安全的.

ErlDrvBinary *driver_alloc_binary(ErlDrvSizeT size)

用至少为size字节的内存块分配驱动程序二进制文件,并返回指向它的指针或NULL失败时(内存不足)。当驱动程序二进制文件发送到仿真器时,不得更改。每个分配的二进制文件将被相应的调用释放driver_free_binary(除非另有说明)。

注意,驱动程序二进制文件有一个内部引用计数器。这意味着driver_free_binary它可能不会真的处理掉它。如果它被发送到模拟器,则可以在那里引用它。

驱动程序二进制文件有一个字段,orig_bytes,它标志着二进制文件中数据的开始。

这个函数是线程安全的.

long driver_async(ErlDrvPort port, unsigned int* key, void (*async_invoke)(void*), void* async_data, void (*async_free)(void*))

执行异步调用。该函数async_invoke在与仿真器线程分离的线程中调用。这使驱动程序可以在不阻塞仿真器的情况下执行耗时的阻塞操作。

异步线程池的大小可以用命令行参数来设定+Aerl(1)。如果异步线程池不可用,则在线程调用中进行同步调用driver_async。可以通过检索异步线程池中的当前异步线程数driver_system_info

如果线程池可用,则使用线程。如果参数keyNULL,来自池的线程以循环方式使用,每次调用driver_async使用池中的下一个线程。使用参数key集时,此行为已更改。两个相同的值*key始终获得相同的线程。

为了确保驱动程序实例始终使用相同的线程,可以使用以下调用:

unsigned int myKey = driver_async_port_key(myPort);

r = driver_async(myPort, &myKey, myData, myFunc);    

myKey每个驱动程序实例初始化一次就足够了。

如果一个线程已经在工作,那么这些调用就会排队并按顺序执行。对每个驱动程序实例使用相同的线程可确保按顺序进行调用。

async_data是功能async_invoke和论点的论据async_free。它通常是指向包含管道或事件的结构的指针,可用于指示异步操作已完成。数据将被释放async_free

异步操作完成后,ready_async调用驱动程序入口函数。如果ready_asyncNULL在驱动程序条目中,async_free函数被调用。

返回值为-1如果driver_async呼叫失败。

从ERTS 5.5.4.3开始,异步线程池中线程的默认堆栈大小为16千字节,即32位体系结构上的64千字节。选择这个小的默认大小是因为异步线程的数量可能非常大。对于使用Erlang / OTP的驱动程序,缺省堆栈大小已足够,但对于使用该driver_async功能的其他动态链接驱动程序来说,可能不够大。异步线程池中线程的建议堆栈大小可以通过+ain 命令行参数来配置erl(1)

unsigned int driver_async_port_key(ErlDrvPort port)

计算密钥供以后使用driver_async。密钥均匀分布,以便实现端口ID和异步线程ID之间的公平映射。

在Erlang/OTP R16之前,可以通过适当的转换将端口ID用作密钥,但在重写端口子系统后,情况就不再一样了。使用此功能,您可以像之前的Erlang/OTP R16一样根据端口ID实现相同的分配。

long driver_binary_dec_refc(ErlDrvBinary *bin)

减少引用计数bin并返回减量后达到的引用计数。

这个函数是线程安全的.

调用二进制驱动程序的引用计数通常会减少driver_free_binary

driver_binary_dec_refc如果引用计数达到零,则不释放二进制文件。driver_binary_dec_refc当您确定不会达到零参考计数时才使用。

long driver_binary_get_refc(ErlDrvBinary *bin)

返回当前引用计数bin

这个函数是线程安全的。

long driver_binary_inc_refc(ErlDrvBinary *bin)

增加引用计数bin并返回增量后到达的引用计数。

这个函数是线程安全的。

ErlDrvTermData driver_caller(ErlDrvPort port)

返回进行当前对驱动程序调用的进程的进程ID。进程ID可用于driver_send_term将数据发回给调用者。driver_caller仅当当前在下列其中一个驱动程序回调中执行时才返回有效数据:

start来自erlang:open_port/2outputerlang:send/2和调用erlang:port_command/2outputverlang:send/2和调用erlang:port_command/2control来自erlang:port_control/3call来自erlang:port_call/3

请注意,该函数不是线程安全的,即使使用了支持SMP的仿真器也不行。

int driver_cancel_timer(ErlDrvPort port)

取消设定的定时器driver_set_timer

返回值是0

int driver_compare_monitors(const ErlDrvMonitor *monitor1, const ErlDrvMonitor *monitor2)

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

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

ErlDrvTermData driver_connected(ErlDrvPort port)

返回端口所有者进程。

请注意,此函数是线程安全,即使在使用SMP支持的模拟器时也是如此。

ErlDrvPort driver_create_port(ErlDrvPort port, ErlDrvTermData owner_pid, char* name, ErlDrvData drv_data)

创建一个新端口,执行与创建新端口的端口相同的驱动程序代码。

port创建新端口的端口(驱动程序实例)的端口句柄。

owner_pidErlang进程的进程ID成为新端口的所有者。这个过程将被链接到新的端口。你通常想用driver_caller(port)as owner_pidname新端口的端口名称。您通常要使用与驱动程序名称(driver_name字段driver_entry)相同的端口名称。

drv_data稍后传入的驱动程序定义的句柄调用驱动程序回调。注意,driver start callback这个新的驱动实例没有被调用。驱动程序定义的句柄通常在创建driver start callback端口时创建erlang:open_port/2

调用者driver_create_port时允许操作新创建的端口。driver_create_port已经回来了。何时port level locking使用时,只允许创建端口操作新创建的端口,直到模拟器调用的当前驱动程序回调返回为止。

int driver_demonitor_process(ErlDrvPort port, const ErlDrvMonitor *monitor)

取消先前创建的监视器。

回报0如果监视器被移除,如果监视器不再存在,则>0。

ErlDrvSizeT driver_deq(ErlDrvPort port, ErlDrvSizeT size)

通过将头指针向前移动到驱动程序队列中,size字节。队列中的数据被解除分配。

返回成功时队列中剩余的字节数,否则返回-1

可以从任何线程调用此函数,如果port data lockport在调用期间被调用线程锁定。

int driver_enq(ErlDrvPort port, char* buf, ErlDrvSizeT len)

将数据排入驱动程序队列。数据buf被复制(len字节)并放置在驱动程序队列的末尾。驱动程序队列通常以FIFO方式使用。

驱动程序队列可用于将来自仿真器的输出排队到驱动程序(从驱动程序到仿真程序的数据由仿真程序在正常的Erlang消息队列中排队)。如果驱动程序必须等待慢速设备等,并且想要退回到仿真程序,这可能很有用。驱动程序队列被实现为ErlIOVec

当队列包含数据时,驱动程序直到队列为空时才关闭。

返回值是0

可以从任何线程调用此函数,如果port data lockport在调用期间被调用线程锁定。

int driver_enq_bin(ErlDrvPort port, ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)

在驱动程序队列中对驱动程序二进制文件进行排队。数据binoffset有长len放在队列的末尾。这个函数通常比driver_enq,因为不需要复制任何数据。

可以从任何线程调用此函数,如果port data lockport在调用期间被调用线程锁定。

返回值是0

int driver_enqv(ErlDrvPort port, ErlIOVec *ev, ErlDrvSizeT skip)

中的数据队列ev跳过第一个skip它的字节,在驱动程序队列的末尾。它比driver_enq,因为不需要复制任何数据。

返回值是0

可以从任何线程调用此函数,如果port data lockport在调用期间被调用线程锁定。

int driver_failure(ErlDrvPort port, int error)int driver_failure_atom(ErlDrvPort port, char *string)int driver_failure_posix(ErlDrvPort port, int error)

向Erlang表明驱动程序遇到错误并将被关闭。端口被关闭,并且元组{'EXIT', error, Err}被发送到端口所有者进程,其中错误是错误原子(driver_failure_atomdriver_failure_posix)或整数(driver_failure)。

驱动程序只有在出现严重错误的情况下才会失败,例如,驱动程序无法保持打开状态,例如缓冲区分配内存不足。对于正常的错误,发送错误代码更合适driver_output

返回值是0

int driver_failure_eof(ErlDrvPort port)

向Erlang发出信号,说明司机遇到EOF,除非端口是带选项打开的,否则将关闭eof,在这种情况下eof被送到港口。否则,端口将关闭,并且'EXIT'消息被发送到端口所有者进程。

返回值是0

void driver_free(void *ptr)

释放被ptr.内存将被分配给driver_alloc所有分配的内存都将被释放,只分配一次。驱动程序中没有垃圾收集。

这个函数是线程安全的.

void driver_free_binary(ErlDrvBinary *bin)

释放驱动程序二进制文件bin,以前分配给driver_alloc_binary.由于Erlang中的二进制数是参考计数,二进制可能仍然存在。

这个函数是线程安全的.

ErlDrvTermData driver_get_monitored_process(ErlDrvPort port, const ErlDrvMonitor *monitor)

返回与活动监视器关联的进程ID。它可以用于process_exit回调以获得退出进程的进程标识。

回报driver_term_nil如果监视器不再存在。

int driver_get_now(ErlDrvNowData *now)

警告

此功能已弃用。不要使用它。改为使用erl_drv_monotonic_time(也许与其结合erl_drv_time_offset)。

将时间戳记读入参数指向的内存中now。有关特定字段的信息,请参阅ErlDrvNowData

返回值是0,除非now指针无效,在这种情况下它是< 0

int driver_lock_driver(ErlDrvPort port)

将端口port在内存中使用的驱动程序锁定在模拟程序进程的其余时间内。在这个调用之后,驱动程序就像Erlang的静态链接驱动程序一样。

ErlDrvTermData driver_mk_atom(char* string)

返回一个给定名称的原子。string原子是创建的,不会改变,因此返回值可以保存和重用,这比几次查找原子要快。

请注意,此函数是线程安全,即使在使用SMP支持的模拟器时也是如此。

ErlDrvTermData driver_mk_port(ErlDrvPort port)

将端口句柄转换为Erlang术语格式,可在erl_drv_output_term和中使用erl_drv_send_term

请注意,此函数是线程安全,即使在使用SMP支持的模拟器时也是如此。

int driver_monitor_process(ErlDrvPort port, ErlDrvTermData process, ErlDrvMonitor *monitor)

从驱动程序开始监视进程。监视进程时,流程退出将导致对提供的process_exit中的回调ErlDrvEntry结构。大ErlDrvMonitor结构被填充,以便以后删除或比较。

参数process的返回值。driver_callerdriver_connected打电话。

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

int driver_output(ErlDrvPort port, char *buf, ErlDrvSizeT len)

将数据从驱动程序发送到模拟器。数据以术语或二进制数据的形式接收,这取决于驱动程序端口是如何打开的。

数据在端口所有者处理%27消息队列中排队。请注意,这不会对模拟器%28产生影响,因为驱动程序和仿真程序运行在同一个线程%29中。

参数buf指向要发送的数据,以及len字节数。

所有输出函数的返回值都是0正常使用的。如果驱动程序用于分发,它可能会失败并返回-1

int driver_output_binary(ErlDrvPort port, char *hbuf, ErlDrvSizeT hlen, ErlDrvBinary* bin, ErlDrvSizeT offset, ErlDrvSizeT len)

将数据从驱动程序二进制文件发送到端口所有者进程。它有一个头缓冲区(hbufhlen)就像driver_output2。参数hbuf可以NULL

参数offset是二进制的偏移量,并且len要发送的字节数。

驱动程序二进制文件是使用driver_alloc_binary

报头中的数据以列表的形式发送,二进制以Erlang二进制的形式发送到列表的尾部。

例如,如果hlen2,端口所有者进程就会收到[H1, H2 | <<T>>]

返回值为0正常使用。

请注意,使用Erlang中的二进制语法,驱动程序应用程序可以直接从二进制文件匹配头文件,因此头文件可以放在二进制文件中,并且hlen可以设置为0

int driver_output_term(ErlDrvPort port, ErlDrvTermData* term, int n)

警告

这一功能已被废弃。使用erl_drv_output_term相反。

参数termn工作erl_drv_output_term

请注意,此函数是线程安全,即使在使用SMP支持的模拟器时也是如此。

int driver_output2(ErlDrvPort port, char *hbuf, ErlDrvSizeT hlen, char *buf, ErlDrvSizeT len)

首先发送hbuf(长度为hlen)数据作为列表,而不管端口设置如何。然后buf作为二进制或列表发送。例如,如果hlen3,端口所有者进程就会收到[H1, H2, H3 | T]

将数据作为列表头发送的目的,是为了方便对接收到的数据进行匹配。

返回值为0正常使用。

int driver_outputv(ErlDrvPort port, char* hbuf, ErlDrvSizeT hlen, ErlIOVec *ev, ErlDrvSizeT skip)

将数据从I / O向量发送ev到端口所有者进程。它有一个头缓冲区(hbufhlen),就像driver_output2

参数skip的跳过的字节数。ev头部的矢量。

你得到的向量ErlIOVec从驱动程序队列%28中键入outputv驱动入口功能。你也可以自己做,如果你想送几个ErlDrvBinary立刻缓冲。通常使用起来更快。driver_output或者。

例如,如果hlen2并且ev指向三个二进制文件的数组,则端口所有者进程将接收[H1, H2, <<B1>>, <<B2>> | <<B3>>]

返回值为0正常使用。

该评论driver_output_binary也适用于driver_outputv

ErlDrvPDL driver_pdl_create(ErlDrvPort port)

创建与该关联的端口数据锁port

一旦创建了一个端口数据锁,它必须在该驱动程序队列上的所有操作过程中被锁定port

返回成功时新创建的端口数据锁定,否则返回NULL。如果该函数port无效或者端口数据锁已经与该关联关联,则该函数失败port

long driver_pdl_dec_refc(ErlDrvPDL pdl)

减少作为参数(pdl)传递的端口数据锁的引用计数。

在执行减量后返回当前引用计数。

这个函数是线程安全的.

long driver_pdl_get_refc(ErlDrvPDL pdl)

返回作为参数(pdl)传递的端口数据锁的当前引用计数。

这个函数是线程安全的.

long driver_pdl_inc_refc(ErlDrvPDL pdl)

增加作为参数(pdl)传递的端口数据锁的引用计数。

执行增量后的当前引用计数将返回。

这个函数是线程安全的。

void driver_pdl_lock(ErlDrvPDL pdl)

锁定作为参数(pdl)传递的端口数据锁。

这个函数是线程安全的.

void driver_pdl_unlock(ErlDrvPDL pdl)

解锁作为参数(pdl)传递的端口数据锁。

这个函数是线程安全的.

SysIOVec *driver_peekq(ErlDrvPort port, int *vlen)

将驱动程序队列检索为指向SysIOVec中的元素数。vlen这是从队列中获取数据的两种方法之一。

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

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

可以从任何线程调用此函数,如果port data lockport在调用期间被调用线程锁定。

ErlDrvSizeT driver_peekqv(ErlDrvPort port, ErlIOVec *ev)

将驱动程序队列检索到提供的ErlIOVecev它还返回队列大小。这是从队列中获取数据的两种方法之一。

如果evNULL,所有的-1类型转换为ErlDrvSizeT都被归还了。

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

可以从任何线程调用此函数,如果port data lockport在调用期间被调用线程锁定。

int driver_pushq(ErlDrvPort port, char* buf, ErlDrvSizeT len)

将数据放在驱动程序队列的首位。数据buf复制%28len字节%29并放置在队列的开头。

返回值是0

可以从任何线程调用此函数,如果port data lockport在调用期间被调用线程锁定。

int driver_pushq_bin(ErlDrvPort port, ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)

将数据放入二进制文件bin,在offset有长len在司机队伍的最前面。它通常比driver_pushq,因为不需要复制任何数据。

可以从任何线程调用此函数,如果port data lockport在调用期间被调用线程锁定。

返回值是0

int driver_pushqv(ErlDrvPort port, ErlIOVec *ev, ErlDrvSizeT skip)

将数据放入ev跳过第一个skip它的字节数,位于驱动程序队列的开头。它比driver_pushq,因为不需要复制任何数据。

返回值是0

可以从任何线程调用此函数,如果port data lockport在调用期间被调用线程锁定。

int driver_read_timer(ErlDrvPort port, unsigned long *time_left)

读取计时器的当前时间,并将结果放入time_left。这是发生超时之前的毫秒时间。

返回值是0

void *driver_realloc(void *ptr, ErlDrvSizeT size)

调整内存块的大小,或者分配新块,复制数据并释放旧块。指针返回到重新分配的内存。失败时(内存不足)NULL将返回。(这通常是一个包装realloc。)

这个函数是线程安全的.

ErlDrvBinary *driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size)

调整驱动程序二进制文件的大小,同时保留数据。

成功时返回调整后的驱动程序二进制文件 NULL失败时返回(内存不足)。

这个函数是线程安全的.

int driver_select(ErlDrvPort port, ErlDrvEvent event, int mode, int on)

驱动程序使用此函数为模拟器提供要检查的事件。这使模拟器能够在发生异步事件时调用驱动程序。

参数event标识特定于操作系统的事件对象。在Unix系统上,使用了函数select/ poll。事件对象必须是一个插座或管(或其它对象select/ poll可以使用)。在Windows上,使用Win32 API函数WaitForMultipleObjects。这对事件对象设置了其他限制; 请参阅Win32 SDK文档。

参数on就是1用于设置事件和0为了清除他们。

参数mode是按位或组合ERL_DRV_READERL_DRV_WRITEERL_DRV_USE。前两个指定是否等待读取事件和/或写入事件。触发的读取事件调用ready_input和触发的写入事件调用ready_output

某些操作系统(Windows)不区分读取和写入事件。触发事件的回调函数仅取决于值mode

ERL_DRV_USE指定是使用事件对象还是要关闭它。在支持smp的模拟器上,清除所有事件后关闭事件对象是不安全的。driver_select已经回来了。另一个线程仍然可以在内部使用事件对象。若要安全关闭事件对象,请调用driver_select带着ERL_DRV_USEon==0,它清除所有事件,然后调用stop_select或安排在关闭事件对象安全时调用它。ERL_DRV_USE与事件对象的第一个事件一起设置。设置是无害的ERL_DRV_USE即使已经完成了。清除所有事件但保持ERL_DRV_USESET表示我们正在使用事件对象,并且可能会再次设置事件。

ERL_DRV_USE在Erlang/OTP R13中添加。旧的驱动程序仍然像以前一样工作,但建议更新它们以使用ERL_DRV_USEstop_select以确保以安全方式关闭事件对象。

返回值是0,除非ready_input/ ready_outputNULL,在这种情况下-1

int driver_send_term(ErlDrvPort port, ErlDrvTermData receiver, ErlDrvTermData* term, int n)

警告

这一功能已被废弃。使用erl_drv_send_term相反。

运行时系统在任意线程执行时无法正确检查此函数的参数。这可能会导致函数在需要时不会失败。

参数termn工作erl_drv_output_term

当使用支持SMP的仿真器时,此函数才是线程安全的.

int driver_set_timer(ErlDrvPort port, unsigned long time)

在驱动程序上设置一个计时器,当驱动程序超时时,该定时器将向下计数并调用该驱动程序。参数time计时器过期前的时间(毫秒)。

当计时器到达0并且过期,驱动程序输入函数timeout叫做。

注意,每个驱动程序实例上只存在一个计时器;设置一个新计时器将替换一个旧的计时器。

返回值是0,除非timeout驱动程序功能是NULL,在这种情况下-1

ErlDrvSizeT driver_sizeq(ErlDrvPort port)

返回驱动程序队列中当前的字节数。

可以从任何线程调用此函数,如果port data lockport在调用期间被调用线程锁定。

void driver_system_info(ErlDrvSysInfo *sys_info_ptr, size_t size)

将有关Erlang运行时系统的信息写入ErlDrvSysInfo第一个参数引用的结构中。第二个参数是ErlDrvSysInfo结构的大小,也就是说sizeof(ErlDrvSysInfo)

有关特定字段的信息,请参阅ErlDrvSysInfo

ErlDrvSizeT driver_vec_to_buf(ErlIOVec *ev, char *buf, ErlDrvSizeT len)

ev通过将它们复制到buf大小的缓冲区中来收集几个数据段len

如果要将数据从驱动程序发送到端口所有者进程,则使用速度会更快driver_outputv

返回值是缓冲区中留下的空间,也就是说,如果ev含有少于len字节是不同的,如果evlen字节或更多,它是0如果有多个头字节,这会更快,因为二进制语法可以直接从二进制文件构造整数。

void erl_drv_busy_msgq_limits(ErlDrvPort port, ErlDrvSizeT *low, ErlDrvSizeT *high)

设置和获取用于控制端口消息队列的繁忙状态的限制。

当消息队列上排队的命令数据量达到high限制。当消息队列上排队的命令数据量低于low限制。命令数据在此上下文中使用Port ! {Owner, {command, Data}}port_command/[2,3]请注意,这些限制只涉及尚未到达端口的命令数据。大busy port功能可以用于已到达端口的数据。

有效限制是范围内的值。[ERL_DRV_BUSY_MSGQ_LIM_MIN, ERL_DRV_BUSY_MSGQ_LIM_MAX].限制自动调整为正常。也就是说,系统调整值,使所使用的下限低于或等于所使用的上限。默认情况下,上限为8kB,下限为4kB。

通过传递指向包含值的整数变量的指针。ERL_DRV_BUSY_MSGQ_READ_ONLY,当前使用的限制将被读取并写入整数变量。可以通过传递指向包含有效限制的整数变量的指针来设置新的限制。传递的值被写入内部限制。然后调整内部限制。在此之后,调整后的限制被写回整数变量,从中读取新的值。值以字节为单位。

忙消息队列特征可以通过设置被禁用或者ERL_DRV_FLAG_NO_BUSY_MSGQ driver flagdriver_entry所使用的驱动器,或者通过调用此函数以ERL_DRV_BUSY_MSGQ_DISABLED作为极限(低或高)。当此功能被禁用时,不能再次启用。阅读限制时,ERL_DRV_BUSY_MSGQ_DISABLED如果此功能已被禁用,则都是。

如果端口繁忙或端口消息队列繁忙,则向端口发送命令数据的进程将被挂起。当端口或端口消息队列都没有繁忙时,挂起的进程将被恢复。

有关繁忙端口功能的信息,请参阅set_busy_port...

void erl_drv_cond_broadcast(ErlDrvCond *cnd)

在条件变量上广播。也就是说,如果其他线程正在等待正在广播的条件变量,他们中的一员被唤醒了。

cnd指向要广播的条件变量的指针。

这个函数是线程安全的.

ErlDrvCond *erl_drv_cond_create(char *name)

创建条件变量并返回指向它的指针。

name标识创建的条件变量的字符串。它用于识别计划中的未来调试功能中的条件变量。

回报NULL在失败的时候。创建条件变量的驱动程序负责在卸载驱动程序之前销毁它。

这个函数是线程安全的.

void erl_drv_cond_destroy(ErlDrvCond *cnd)

销毁先前由...创建的条件变量erl_drv_cond_create

cnd指向要销毁的条件变量的指针。

这个函数是线程安全的.

char *erl_drv_cond_name(ErlDrvCond *cnd)

返回指向条件名称的指针。

cnd是指向初始化条件的指针。

此函数仅用于调试目的。

void erl_drv_cond_signal(ErlDrvCond *cnd)

条件变量上的信号。也就是说,如果其他线程正在等待发出信号的条件变量,1他们中的一个被唤醒了。

cnd指向要启动的条件变量的指针。

这个函数是线程安全的.

void erl_drv_cond_wait(ErlDrvCond *cnd, ErlDrvMutex *mtx)

等待条件变量。调用线程被阻塞,直到另一个线程通过在条件变量上发送信号或广播来唤醒它。在阻塞调用线程之前,它会解锁作为参数传递的互斥锁。当调用线程被唤醒时,它会在返回之前锁定相同的互斥对象。也就是说,在调用此函数时,当前必须由调用线程锁定互斥对象。

cnd指向要等待的条件变量的指针。mtx是指向互斥对象的指针,以便在等待时解锁。

erl_drv_cond_wait即使没有人在条件变量上发出信号或广播,也可以返回。代码调用erl_drv_cond_wait总是准备好erl_drv_cond_wait即使线程等待的条件尚未发生,也会返回。也就是说,当从erl_drv_cond_wait,始终检查条件是否已发生,如果没有调用erl_drv_cond_wait再来一次。

这个函数是线程安全的.

int erl_drv_consume_timeslice(ErlDrvPort port, int percent)

向运行时系统提供一个提示,说明当前驱动程序回调调用自上次提示以来消耗了多少CPU时间,如果没有先前的提示,则提示自回调开始以来所消耗的CPU时间。

port执行端口的端口句柄。percent全时间切片的近似消耗分数(百分比)。

时间指定为允许端口在将CPU交给其他可运行的端口或进程之前执行的完整时间片的分数(以百分比为单位)。有效范围是[1, 100]调度时间片不是一个精确的实体,但通常可以近似为1毫秒。

请注意,要由运行时系统来确定是否以及如何使用这些信息。某些平台上的实现可以使用其他方法来确定时间片的消耗部分。不管如何,长时间的驱动程序回调应该经常调用此函数,以确定是否允许它继续执行。

如果时间片已经耗尽,此函数将返回一个非零值,如果允许继续执行回调,则返回零。如果返回一个非零值,则驱动程序回调将尽快返回,以便端口能够产生结果。

该功能是为了更好地支持协同调度,提高系统响应能力,以及更容易防止由于端口垄断调度线程而导致的VM的错误行为。它可以用于将冗长的工作划分为一些重复的驱动回调调用,而不需要使用线程。

亦见重要warning此手册页开头的文本。

ErlDrvTime erl_drv_convert_time_unit(ErlDrvTime val, ErlDrvTimeUnit from, ErlDrvTimeUnit to)

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

val用于转换时间单位的值。from时间单位valto返回值的时间单位。

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

另见ErlDrvTimeErlDrvTimeUnit...

int erl_drv_equal_tids(ErlDrvTid tid1, ErlDrvTid tid2)

比较两个线程标识符,tid1tid2为了平等。

回报0它们是不相等的,值也不等于0如果他们是平等的。

线程标识符可以在线程终止后很快重用。因此,如果与所涉及的线程标识符之一对应的线程在保存线程标识符后终止,则erl_drv_equal_tids不可能给出预期的结果。

这个函数是线程安全的.

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

检索环境变量的值。

key一个NULL包含环境变量的名称封端的字符串。value指向输出缓冲区的指针。value_size指向一个整数的指针。该整数用于传递输入和输出大小(参见下文)。

当调用此函数时,*value_size的大小。value缓冲器。

成功时0返回,环境变量的值已写入value缓冲区,并*value_size包含NULL写入value缓冲区的值的字符串长度(不包括终止字符)。

如果失败,即没有找到这样的环境变量,则值<0会被归还。当value缓冲区太小,值>0会被退回并且*value_size已设置为所需的缓冲区大小。

警告

千万不能使用libc中的getenv或类似的C库接口从驱动器。

这个函数是线程安全的.

void erl_drv_init_ack(ErlDrvPort port, ErlDrvData res)

确认端口的启动。

port端口(驱动程序实例)的端口句柄进行确认。 res端口初始化的结果。可以是与返回值相同的值start,即任何错误代码或ErlDrvData将用于此端口的值。

当这个函数被称为erlang:open_port调用被返回,就像start函数刚刚被调用。它只能在旗子上使用。ERL_DRV_FLAG_USE_INIT_ACK已在连接驱动程序上设置。

ErlDrvTime erl_drv_monotonic_time(ErlDrvTimeUnit time_unit)

回报Erlang monotonic time注意,负值并不少见。

time_unit返回值的时间单位。

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

另见ErlDrvTimeErlDrvTimeUnit

ErlDrvMutex *erl_drv_mutex_create(char *name)

创建互斥并返回指向它的指针。

name标识创建的互斥对象的字符串。它用于识别计划中的未来调试功能中的互斥对象。

回报NULL在失败的时候。创建互斥对象的驱动程序负责在卸载驱动程序之前销毁它。

这个函数是线程安全的.

void erl_drv_mutex_destroy(ErlDrvMutex *mtx)

破坏以前由erl_drv_mutex_create.互斥必须处于未锁定状态,才能被销毁。

mtx是指向要销毁的互斥物的指针。

这个函数是线程安全的.

void erl_drv_mutex_lock(ErlDrvMutex *mtx)

锁定互斥物。调用线程将被阻塞,直到互斥锁住为止。当前已锁定互斥对象的线程。不可能再次锁定相同的互斥锁。

mtx是指向要锁定的互斥对象的指针。

警告

如果当您让线程无法控制时,将互斥锁在模拟器线程中,则您将极有可能整个模拟器死锁。

这个函数是线程安全的.

char *erl_drv_mutex_name(ErlDrvMutex *mtx)

返回指向互斥对象名称的指针。

mtx是指向初始化互斥对象的指针。

此函数仅用于调试目的。

int erl_drv_mutex_trylock(ErlDrvMutex *mtx)

试图锁定互斥对象。当前已锁定互斥对象的线程。不可能再试着锁定同一个互斥体。

mtx是指向互斥锁的指针。

0成功时返回,否则返回EBUSY

警告

如果当您让线程无法控制时,将互斥锁在模拟器线程中,则您将极有可能整个模拟器死锁。

这个函数是线程安全的.

void erl_drv_mutex_unlock(ErlDrvMutex *mtx)

打开互斥锁。当前,互斥锁必须由调用线程锁定。

mtx是指向要解锁的互斥对象的指针。

这个函数是线程安全的.

int erl_drv_output_term(ErlDrvTermData port, ErlDrvTermData* term, int n)

以特殊驱动程序术语格式向端口所有者进程发送数据。这是一种从驱动程序中快速传递术语数据的方法。它不需要二进制转换,因此端口所有者进程以普通的Erlang术语接收数据。大erl_drv_send_term函数可用于发送到本地节点上的任何进程。

参数port不是一个普通端口句柄,但使用一个端口句柄转换driver_mk_port

参数term指向ErlDrvTermData带着n元素。此数组包含以驱动程序术语格式描述的术语。每个项都由数组中的1-4个元素组成。第一个术语有一个术语类型,然后是参数。参数port指定发送端口。

元组,映射和列表(字符串除外,参见下文)都是以反向抛光表示法构建的,所以要构建元组,首先指定元素,然后指定元组项,然后使用计数。同样的列表和地图。

  • 元组必须用元素数指定。(元素在ERL_DRV_TUPLE该词之前。)
  • 必须使用键值对的数量指定映射N。键值对必须ERL_DRV_MAP按以下顺序排列:key1,value1,key2,value2,...,keyN,valueN。重复密钥是不允许的。
  • 一个列表必须用元素数量来指定,包括最后一个词的尾部ERL_DRV_LIST

特殊术语ERL_DRV_STRING_CONS用于在列表中的字符串中“拼接”,以这种方式指定的字符串本身并不是列表,但元素是周围列表的元素。

Term type            Arguments
---------            ---------
ERL_DRV_NIL
ERL_DRV_ATOM         ErlDrvTermData atom (from driver_mk_atom(char *string))
ERL_DRV_INT          ErlDrvSInt integer
ERL_DRV_UINT         ErlDrvUInt integer
ERL_DRV_INT64        ErlDrvSInt64 *integer_ptr
ERL_DRV_UINT64       ErlDrvUInt64 *integer_ptr
ERL_DRV_PORT         ErlDrvTermData port (from driver_mk_port(ErlDrvPort port))
ERL_DRV_BINARY       ErlDrvBinary *bin, ErlDrvUInt len, ErlDrvUInt offset
ERL_DRV_BUF2BINARY   char *buf, ErlDrvUInt len
ERL_DRV_STRING       char *str, int len
ERL_DRV_TUPLE        int sz
ERL_DRV_LIST         int sz
ERL_DRV_PID          ErlDrvTermData pid (from driver_connected(ErlDrvPort port)
                     or driver_caller(ErlDrvPort port))
ERL_DRV_STRING_CONS  char *str, int len
ERL_DRV_FLOAT        double *dbl
ERL_DRV_EXT2TERM     char *buf, ErlDrvUInt len
ERL_DRV_MAP          int sz

无符号整数数据类型ErlDrvUInt和有符号整数数据类型。ErlDrvSInt64位宽在64位运行时系统上,32位宽在32位运行时系统上。它们是在erts 5.6中引入的,并替换了一些int以上列表中的参数。

无符号整数数据类型ErlDrvUInt64和有符号整数数据类型。ErlDrvSInt64总是64位宽。它们是在ERTS 5.7.4中介绍的。

构建元组{tcp, Port, [100 | Binary]},可以发出以下电话。

ErlDrvBinary* bin = ...
ErlDrvPort port = ...
ErlDrvTermData spec[] = {
    ERL_DRV_ATOM, driver_mk_atom("tcp"),
    ERL_DRV_PORT, driver_mk_port(drvport),
        ERL_DRV_INT, 100,
        ERL_DRV_BINARY, bin, 50, 0,
        ERL_DRV_LIST, 2,
    ERL_DRV_TUPLE, 3,
};
erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));    

bin是一个长度至少为50的驱动程序二进制文件,它drvport是一个端口句柄。请注意,ERL_DRV_LIST列表中的元素也是如此ERL_DRV_TUPLE

ERL_DRV_STRING_CONS术语是构建字符串的一种方式。它的工作方式与作品不同ERL_DRV_STRINGERL_DRV_STRING_CONS以相反的顺序构建一个字符串列表(而不是如何ERL_DRV_LIST工作),连接添加到列表中的字符串。尾巴必须在之前指定ERL_DRV_STRING_CONS

ERL_DRV_STRING构造一个字符串,并结束它。(所以和ERL_DRV_NIL后面的一样ERL_DRV_STRING_CONS。)

/* to send [x, "abc", y] to the port: */
ErlDrvTermData spec[] = {
    ERL_DRV_ATOM, driver_mk_atom("x"),
    ERL_DRV_STRING, (ErlDrvTermData)"abc", 3,
    ERL_DRV_ATOM, driver_mk_atom("y"),
    ERL_DRV_NIL,
    ERL_DRV_LIST, 4
};
erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));    
/* to send "abc123" to the port: */
ErlDrvTermData spec[] = {
    ERL_DRV_NIL,        /* with STRING_CONS, the tail comes first */
    ERL_DRV_STRING_CONS, (ErlDrvTermData)"123", 3,
    ERL_DRV_STRING_CONS, (ErlDrvTermData)"abc", 3,
};
erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));    

ERL_DRV_EXT2TERM术语类型用于通过与所述编码的术语external format,也就是已被编码的术语erlang:term_to_binaryerl_interface:ei(3),等。例如,如果binp是一个指向一个ErlDrvBinary包含{17, 4711}与之编码的术语的指针external format,并且您想用标记将它包装在一个二元组中my_tag,也就是说{my_tag, {17, 4711}},您可以执行如下操作:

ErlDrvTermData spec[] = {
        ERL_DRV_ATOM, driver_mk_atom("my_tag"),
        ERL_DRV_EXT2TERM, (ErlDrvTermData) binp->orig_bytes, binp->orig_size
    ERL_DRV_TUPLE, 2,
};
erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));    

建立地图#{key1 => 100, key2 => {200, 300}},可以发出以下电话。

ErlDrvPort port = ...
ErlDrvTermData spec[] = {
    ERL_DRV_ATOM, driver_mk_atom("key1"),
        ERL_DRV_INT, 100,
    ERL_DRV_ATOM, driver_mk_atom("key2"),
        ERL_DRV_INT, 200,
        ERL_DRV_INT, 300,
    ERL_DRV_TUPLE, 2,
    ERL_DRV_MAP, 2
};
erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]));    

如果要传递二进制文件,并且在ErlDrvBinary,您可以从使用ERL_DRV_BUF2BINARY而不是创建一个ErlDrvBinarydriver_alloc_binary然后将二进制文件传递给ERL_DRV_BINARY.运行时系统通常会在以下情况下更智能地分配二进制文件:ERL_DRV_BUF2BINARY被使用了。但是,如果要传递的二进制文件的内容已驻留在ErlDrvBinary,通常情况下,最好是将二进制文件传递给ERL_DRV_BINARYErlDrvBinary有疑问。

ERL_DRV_UINTERL_DRV_BUF2BINARYERL_DRV_EXT2TERM项类型ERTS 5.6进行了介绍。

当使用支持SMP的仿真器时,此函数才是线程安全的.

int erl_drv_putenv(const char *key, char *value)

设置环境变量的值。

keyNULL-终止字符串,包含环境变量的名称。

valueNULL-终止字符串,包含环境变量的新值。

0成功返回,否则返回值!= 0

将空字符串("")作为值传递的结果是平台相关的。在某些平台上,变量值设置为空字符串,其他环境变量被删除。

警告

千万不能使用libc中的putenv或类似的C库接口从驱动器。

这个函数是线程安全的.

ErlDrvRWLock *erl_drv_rwlock_create(char *name)

创建一个rwlock并返回指向它的指针。

name标识创建的rwlock的字符串。它用于识别计划中的未来调试功能中的rwlock。

回报NULL在失败的时候。创建rwlock的驱动程序负责在卸载驱动程序之前销毁它。

这个函数是线程安全的.

void erl_drv_rwlock_destroy(ErlDrvRWLock *rwlck)

销毁先前由erl_drv_rwlock_create.Rwlock必须处于未锁定状态,才能被销毁。

rwlck是指向要销毁的罗洛克的指针。

这个函数是线程安全的.

char *erl_drv_rwlock_name(ErlDrvRWLock *rwlck)

返回指向rwlock名称的指针。

rwlck指向初始化的rwlock的指针。

此函数仅用于调试目的。

void erl_drv_rwlock_rlock(ErlDrvRWLock *rwlck)

读锁一个rwlock。调用线程将被阻塞,直到rwlock已被锁定。当前已读取或读/写锁定了rwlock的线程。不可能再次锁定同样的罗洛克。

rwlck是用于读取锁的rwlock的指针。

警告

如果当您让线程失去控制时,rwlock锁定在模拟器线程中,您将极有可能整个模拟器死锁。

这个函数是线程安全的.

void erl_drv_rwlock_runlock(ErlDrvRWLock *rwlck)

读解锁。当前的rwlock必须被调用线程锁定。

rwlck是指向要读取解锁的rwlock的指针。

这个函数是线程安全的.

void erl_drv_rwlock_rwlock(ErlDrvRWLock *rwlck)

读/写锁定了一个rwlock。调用线程将被阻塞,直到rwlock被读/写锁定为止。当前已读取或读/写锁定了rwlock的线程。不可能再次锁定同样的罗洛克。

rwlck指向用于读/写锁的rwlock的指针。

警告

如果当您让线程失去控制时,rwlock锁定在模拟器线程中,您将极有可能整个模拟器死锁。

这个函数是线程安全的.

void erl_drv_rwlock_rwunlock(ErlDrvRWLock *rwlck)

读/写解锁。当前的rwlock必须由调用线程锁定。

rwlck是指向要读取/写入解锁的rwlock的指针。

这个函数是线程安全的.

int erl_drv_rwlock_tryrlock(ErlDrvRWLock *rwlck)

试图读取锁的一个rwlock。

rwlck是指向rwlock的指针,以尝试读取锁。

0成功时返回,否则返回EBUSY。当前读取或读取/写入锁定rwlock的线程无法再次尝试锁定相同的rwlock。

警告

如果当您让线程失去控制时,rwlock锁定在模拟器线程中,您将极有可能整个模拟器死锁。

这个函数是线程安全的.

int erl_drv_rwlock_tryrwlock(ErlDrvRWLock *rwlck)

尝试读取/写入锁(Rwlock)。当前已读取或读/写锁定了rwlock的线程。不可能尝试再次锁定相同的Rwlock。

rwlck是指向rwlock的指针,以尝试读取/写入锁。

成功时返回0,否则返回EBUSY

警告

如果当您让线程失去控制时,rwlock锁定在模拟器线程中,您将极有可能整个模拟器死锁。

这个函数是线程安全的.

int erl_drv_send_term(ErlDrvTermData port, ErlDrvTermData receiver, ErlDrvTermData* term, int n)

此函数是驱动程序将数据发送到其他进程比端口所有者进程。参数receiver指定要接收数据的进程。

参数port不是一个普通端口句柄,但使用一个端口句柄转换driver_mk_port

参数portterm,并在中n工作erl_drv_output_term

当使用支持SMP的仿真器时,此函数才是线程安全的.

void erl_drv_set_os_pid(ErlDrvPort port, ErlDrvSInt pid)

设置在此端口上os_pid执行erlang:port_info/2操作时看到的内容。

port是用于设置pid的端口(驱动程序实例)的端口句柄。pid是设置的pid。

int erl_drv_thread_create(char *name, ErlDrvTid *tid, void * (*func)(void *), void *arg, ErlDrvThreadOpts *opts)

创建一个新线程。

name标识创建的线程的字符串。它用于在计划的未来调试功能中识别线程。

tid指向线程标识符变量的指针。

func指向要在创建的线程中执行的函数的指针。

arg指向该func函数参数的指针。

opts指向要使用的线程选项的指针或NULL

回报0在成功的时候,否则errno值以指示错误。新创建的线程开始在func,和func通过arg作为争论。何时erl_drv_thread_create返回时,新创建的线程的线程标识符可在*tid...opts可以是NULL指针,或指向ErlDrvThreadOpts结构。如果optsNULL指针,使用默认选项,否则使用传递的选项。

警告

你不能自己分配ErlDrvThreadOpts结构。它必须通过分配和初始化erl_drv_thread_opts_create

创建的线程在func返回或如果erl_drv_thread_exit由线程调用。线程的退出值将从func或作为参数传递给erl_drv_thread_exit创建线程的驱动程序负责连接线程,通过erl_drv_thread_join在司机卸货之前。不能创建“分离”线程,即不需要连接的线程。

警告

卸载之前,所有创建的线程必须由驱动程序加入。如果驱动程序未能加入在卸载之前创建的所有线程,则在卸载驱动程序代码时,运行时系统很可能会崩溃。

这个函数是线程安全的.

void erl_drv_thread_exit(void *exit_value)

使用作为参数传递的退出值终止调用线程。exit_value是指向退出值的指针或NULL...

只允许终止使用erl_drv_thread_create...

退出值以后可以由另一个线程通过erl_drv_thread_join...

这个函数是线程安全的.

int erl_drv_thread_join(ErlDrvTid tid, void **exit_value)

将调用线程与另一个线程连接起来,也就是说,调用线程将被阻塞,直到tid已经终止了。

tid要连接的线程的线程标识符。exit_value指向指向退出值的指针,或NULL...

回报0在成功的时候,否则errno值以指示错误。

一个线程只能连接一次。不止一次加入的行为是未定义的,模拟器崩溃很可能。如果exit_value == NULL,被终止的线程的出口值被忽略,否则被终止的线程的出口值被存储在*exit_value

这个函数是线程安全的.

char *erl_drv_thread_name(ErlDrvTid tid)

返回指向线程名称的指针。

tid是线程标识符。

此函数仅用于调试目的。

ErlDrvThreadOpts *erl_drv_thread_opts_create(char *name)

分配和初始化线程选项结构。

name标识创建的线程选项的字符串。它用于标识计划中的未来调试功能中的线程选项。

回报NULL在失败的时候。线程选项结构用于将选项传递给erl_drv_thread_create如果结构在传递给erl_drv_thread_create,则使用默认值。

警告

你不能自己分配ErlDrvThreadOpts结构。它必须通过分配和初始化erl_drv_thread_opts_create

这个函数是线程安全的.

void erl_drv_thread_opts_destroy(ErlDrvThreadOpts *opts)

破坏以前由...创建的线程选项erl_drv_thread_opts_create

opts指向要销毁的线程选项的指针。

这个函数是线程安全的.

ErlDrvTid erl_drv_thread_self(void)

返回调用线程的线程标识符。

这个函数是线程安全的.

ErlDrvTime erl_drv_time_offset(ErlDrvTimeUnit time_unit)

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

time_unit返回值的时间单位。

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

另见ErlDrvTimeErlDrvTimeUnit

void *erl_drv_tsd_get(ErlDrvTSDKey key)

返回特定于线程的数据。key用于调用线程。

key是特定于线程的数据键。

回报NULL如果没有数据与key用于调用线程。

这个函数是线程安全的.

int erl_drv_tsd_key_create(char *name, ErlDrvTSDKey *key)

创建特定于线程的数据键。

name标识创建的键的字符串。它用于识别计划中的未来调试功能中的密钥。

key是指向特定于线程的数据键变量的指针。

回报0在成功的时候,否则errno值以指示错误。创建密钥的驱动程序负责在卸载驱动程序之前销毁它。

这个函数是线程安全的.

void erl_drv_tsd_key_destroy(ErlDrvTSDKey key)

销毁先前由...创建的线程特定的数据键erl_drv_tsd_key_createerl_drv_tsd_set在调用之前,所有线程中使用此键的所有线程特定数据都必须清除(请参阅)erl_drv_tsd_key_destroy

key是要销毁的特定于线程的数据键。

警告

被摧毁的钥匙很可能很快就会被重复使用。因此,如果在销毁键之前未能在线程中使用此键清除特定于线程的数据,则将极有可能在系统的其他部分获取意外错误。

这个函数是线程安全的.

void erl_drv_tsd_set(ErlDrvTSDKey key, void *data)

设置特定于线程的数据。key用于调用线程。只有在线程完全处于您的控制之下时,才允许为它们设置特定于线程的数据。例如,如果在调用驱动程序回调函数的线程中设置特定于线程的数据,则必须清除该数据,即设置为NULL,在从驱动程序回调函数返回之前。

key是特定于线程的数据键。

data是指向要关联的数据的指针。key在调用线程中。

警告

如果在模拟器线程中清除线程特定于线程的数据之前,无法控制它,则可能永远无法清除该数据,从而导致系统其他部分出现意外错误。

这个函数是线程安全的.

char *erl_errno_id(int error)

给定错误编号,返回Erlang错误的原子名称error。错误的原子是einvalenoent等等。它可以用来从驱动程序制作错误条款。

int remove_driver_entry(ErlDrvEntry *de)

删除de以前添加的驱动程序条目add_driver_entry

添加的驱动程序条目。erl_ddll不能使用此接口删除Erlang接口。

void set_busy_port(ErlDrvPort port, int on)

设置和取消端口的繁忙状态。如果on为非零,端口设置为繁忙。如果为零,则端口设置为“不忙”。您通常希望将此功能与busy port message queue功能。

如果端口或端口消息队列繁忙,那么向端口发送命令数据的进程将暂停。当端口或端口消息队列都不忙时,恢复挂起的进程。命令数据是通过使用任何端口这种情况下的数据Port ! {Owner, {command, Data}}port_command/[2,3]

如果ERL_DRV_FLAG_SOFT_BUSY已设置为driver_entry,数据可以通过erlang:port_command(Port, Data, [force])即使司机已经发出了繁忙的信号。

有关繁忙端口消息队列功能的信息,请参阅erl_drv_busy_msgq_limits

void set_port_control_flags(ErlDrvPort port, int flags)

设置control驱动程序入口函数如何将数据返回给端口所有者进程的标志。(该control函数从中调用erlang:port_control/3。)

目前只有两个有意义的值flags0意味着数据以列表形式返回,并且PORT_CONTROL_FLAG_BINARY意味着数据以二进制形式返回control

另见

driver_entry(3)erlang(3)erl_ddll(3),部分How to Implement an Alternative Carrier for the Erlang Distribution>在用户指南中

ERTS/erl_driver相关

Erlang 20

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

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