erl_driver
erl_driver
C库
erl_driver
图书馆摘要
用于Erlang驱动程序的API函数。
描述
Erlang驱动程序是包含一组本地驱动程序回调函数的库,当某些事件发生时,Erlang虚拟机会调用这些函数。一个驱动程序可以有多个实例,每个实例都与一个Erlang端口相关联。
警告
使用此功能时要格外小心。
驱动程序回调作为VM本机代码的直接扩展执行。执行不是在安全的环境中进行的。VM不可能
提供与执行Erlang代码相同的服务,例如抢占式调度或内存保护。如果驱动程序回调函数不能很好地运行,整个VM就会出现错误。
- 驱动程序回调--崩溃将使整个VM崩溃。
- 错误实现的驱动程序回调可能导致VM内部状态不一致,这可能导致VM崩溃,或者在调用驱动程序回调之后的任意一点上VM的各种错误行为。
- 驱动程序回调在返回之前执行
lengthy work
会降低虚拟机的响应速度,并可能导致混杂的奇怪行为。这种奇怪的行为包括但不限于极端内存使用和调度程序之间的负载平衡不佳。Erlang/OTP版本之间因长时间工作而出现的奇怪行为也会有所不同。
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_entry
到ERL_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
,当添加新功能时,它将递增。运行时系统使用驱动程序的次要版本来确定要使用的特性。
ErlDrvTime
ErlDrvTimeUnit
erl_drv_monotonic_time
erl_drv_time_offset
erl_drv_convert_time_unit
重写64位驱动程序接口
ERTS 5.9引入了两种新的整数类型,ErlDrvSizeT
和ErlDrvSSizeT
,如果有必要,可以容纳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,因为这是int
32位可以容纳的数据块)的数据块才需要进行这些更改。但有可能会想到非理智的调用约定,这会使驱动程序回调混淆导致故障的论据。
注
参数类型的变化是从signed到unsigned。这可能会导致问题,例如,如果您只是在整个地方更改类型的循环终止条件或错误条件。
更大
size
字段
ErlIOVec
在size
现场ErlIOVec
已更改为ErlDrvSizeT
从int
。检查所有使用该字段的代码。
自动类型转换可能仅在遇到大于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_refc
,driver_binary_inc_refc
和driver_binary_dec_refc
。
一些驱动程序调用,例如driver_enq_binary
,增加驱动程序引用计数,以及其他,如driver_deq
减少它。
使用驱动程序二进制而不是普通缓冲区通常更快,因为模拟器不需要复制数据,只使用指针。
驱动程序中分配的驱动程序二进制文件将与驱动程序一起driver_alloc_binary
释放(除非另有说明)driver_free_binary
。(请注意,如果在仿真器中仍然引用了驱动程序,则这不一定会将其解除分配,否则ref-count不会为零。)
驱动程序二进制文件用于driver_output2
和driver_outputv
呼叫,并在队列中。还有驱动程序的回调outputv
使用驱动程序二进制文件。
如果驱动程序出于某种原因想要保留一个驱动程序二进制文件,例如在一个静态变量中,引用计数将被增加,并且二进制文件稍后可以在stop
回调中被释放driver_free_binary
。
注意,驱动程序和模拟器共享作为驱动程序的二进制文件。驱动程序不得更改从模拟器接收到的二进制文件或发送给模拟器的二进制文件。
由于ERTS 5.5(Erlang/OTP R11B)orig_bytes
保证正确对齐以存储双精度数组(通常为8字节对齐)。
ErlDrvData
驱动程序特定数据的句柄,传递给驱动程序回调。它是一个指针,通常是对驱动程序中的特定指针进行类型转换。
SysIOVec
系统I/O矢量,writev
在Unix和WSASend
Win32上使用。它用于ErlIOVec
。
ErlIOVec
typedef struct ErlIOVec {
int vsize;
ErlDrvSizeT size;
SysIOVec* iov;
ErlDrvBinary** binv;
} ErlIOVec;
仿真器和驱动程序使用的I / O向量是一个二进制文件列表,SysIOVec
指向二进制文件的缓冲区。它用于driver_outputv
和outputv
驱动程序回调。另外,驱动程序队列是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_refc
,driver_pdl_inc_refc
和driver_pdl_dec_refc
分别。
ErlDrvTid
线程标识符
又见erl_drv_thread_create
,erl_drv_thread_exit
,erl_drv_thread_join
,erl_drv_thread_self
,和erl_drv_equal_tids
。
ErlDrvThreadOpts
int suggested_stack_size;
传递给erl_drv_thread_create
4.存在以下领域:
suggested_stack_size以千瓦为单位,建议使用多大的堆栈。值<0表示默认大小。
另见erl_drv_thread_opts_create
,erl_drv_thread_opts_destroy
和erl_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_create
,erl_drv_cond_destroy
,erl_drv_cond_signal
,erl_drv_cond_broadcast
,和erl_drv_cond_wait
。
ErlDrvRWLock
读/写锁。用于允许多个线程读取共享数据,同时只允许一个线程写入相同的数据。多个线程可以同时读取锁定rwlock,而一次只有一个线程可以读取/写入锁定rwlock。
另见erl_drv_rwlock_create
,erl_drv_rwlock_destroy
,erl_drv_rwlock_rlock
,erl_drv_rwlock_tryrlock
,erl_drv_rwlock_runlock
,erl_drv_rwlock_rwlock
,erl_drv_rwlock_tryrwlock
,和erl_drv_rwlock_rwunlock
。
ErlDrvTSDKey
可与线程特定数据关联的键。
又见erl_drv_tsd_key_create
,erl_drv_tsd_key_destroy
,erl_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
在与仿真器线程分离的线程中调用。这使驱动程序可以在不阻塞仿真器的情况下执行耗时的阻塞操作。
异步线程池的大小可以用命令行参数来设定+A
在erl(1)
。如果异步线程池不可用,则在线程调用中进行同步调用driver_async
。可以通过检索异步线程池中的当前异步线程数driver_system_info
。
如果线程池可用,则使用线程。如果参数key
是NULL
,来自池的线程以循环方式使用,每次调用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_async
是NULL
在驱动程序条目中,async_free
函数被调用。
返回值为-1
如果driver_async
呼叫失败。
注
从ERTS 5.5.4.3开始,异步线程池中线程的默认堆栈大小为16千字节,即32位体系结构上的64千字节。选择这个小的默认大小是因为异步线程的数量可能非常大。对于使用Erlang / OTP的驱动程序,缺省堆栈大小已足够,但对于使用该driver_async
功能的其他动态链接驱动程序来说,可能不够大。异步线程池中线程的建议堆栈大小可以通过+a
in 命令行参数来配置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/2
。output
从erlang:send/2
和调用erlang:port_command/2
。outputv
从erlang:send/2
和调用erlang:port_command/2
。control
来自erlang:port_control/3
。call
来自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 monitor1和monitor2equal,< 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
创建新端口的端口(驱动程序实例)的端口句柄。
调用者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 lock
与port
在调用期间被调用线程锁定。
int driver_enq(ErlDrvPort port, char* buf, ErlDrvSizeT len)
将数据排入驱动程序队列。数据buf
被复制(len
字节)并放置在驱动程序队列的末尾。驱动程序队列通常以FIFO方式使用。
驱动程序队列可用于将来自仿真器的输出排队到驱动程序(从驱动程序到仿真程序的数据由仿真程序在正常的Erlang消息队列中排队)。如果驱动程序必须等待慢速设备等,并且想要退回到仿真程序,这可能很有用。驱动程序队列被实现为ErlIOVec
。
当队列包含数据时,驱动程序直到队列为空时才关闭。
返回值是0
。
可以从任何线程调用此函数,如果port data lock
与port
在调用期间被调用线程锁定。
int driver_enq_bin(ErlDrvPort port, ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)
在驱动程序队列中对驱动程序二进制文件进行排队。数据bin
在offset
有长len
放在队列的末尾。这个函数通常比driver_enq
,因为不需要复制任何数据。
可以从任何线程调用此函数,如果port data lock
与port
在调用期间被调用线程锁定。
返回值是0
。
int driver_enqv(ErlDrvPort port, ErlIOVec *ev, ErlDrvSizeT skip)
中的数据队列ev
跳过第一个skip
它的字节,在驱动程序队列的末尾。它比driver_enq
,因为不需要复制任何数据。
返回值是0
。
可以从任何线程调用此函数,如果port data lock
与port
在调用期间被调用线程锁定。
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_atom
和driver_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_caller
或driver_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)
将数据从驱动程序二进制文件发送到端口所有者进程。它有一个头缓冲区(hbuf
和hlen
)就像driver_output2
。参数hbuf
可以NULL
。
参数offset
是二进制的偏移量,并且len
要发送的字节数。
驱动程序二进制文件是使用driver_alloc_binary
。
报头中的数据以列表的形式发送,二进制以Erlang二进制的形式发送到列表的尾部。
例如,如果hlen是2,端口所有者进程就会收到[H1, H2 | <<T>>]。
返回值为0
正常使用。
请注意,使用Erlang中的二进制语法,驱动程序应用程序可以直接从二进制文件匹配头文件,因此头文件可以放在二进制文件中,并且hlen
可以设置为0
。
int driver_output_term(ErlDrvPort port, ErlDrvTermData* term, int n)
警告
这一功能已被废弃。
使用erl_drv_output_term
相反。
参数term
和n
工作erl_drv_output_term
。
请注意,此函数是不
线程安全,即使在使用SMP支持的模拟器时也是如此。
int driver_output2(ErlDrvPort port, char *hbuf, ErlDrvSizeT hlen, char *buf, ErlDrvSizeT len)
首先发送hbuf
(长度为hlen
)数据作为列表,而不管端口设置如何。然后buf
作为二进制或列表发送。例如,如果hlen
是3
,端口所有者进程就会收到[H1, H2, H3 | T]
。
将数据作为列表头发送的目的,是为了方便对接收到的数据进行匹配。
返回值为0
正常使用。
int driver_outputv(ErlDrvPort port, char* hbuf, ErlDrvSizeT hlen, ErlIOVec *ev, ErlDrvSizeT skip)
将数据从I / O向量发送ev
到端口所有者进程。它有一个头缓冲区(hbuf
和hlen
),就像driver_output2
。
参数skip
的跳过的字节数。ev
头部的矢量。
你得到的向量ErlIOVec
从驱动程序队列%28中键入outputv
驱动入口功能。你也可以自己做,如果你想送几个ErlDrvBinary
立刻缓冲。通常使用起来更快。driver_output
或者。
例如,如果hlen是2并且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 lock
与port
在调用期间被调用线程锁定。
ErlDrvSizeT driver_peekqv(ErlDrvPort port, ErlIOVec *ev)
将驱动程序队列检索到提供的ErlIOVec
ev
它还返回队列大小。这是从队列中获取数据的两种方法之一。
如果ev
是NULL
,所有的-1
类型转换为ErlDrvSizeT
都被归还了。
这个函数不会从队列中删除任何内容,必须使用该函数完成driver_deq
。
可以从任何线程调用此函数,如果port data lock
与port
在调用期间被调用线程锁定。
int driver_pushq(ErlDrvPort port, char* buf, ErlDrvSizeT len)
将数据放在驱动程序队列的首位。数据buf
复制%28len
字节%29并放置在队列的开头。
返回值是0
。
可以从任何线程调用此函数,如果port data lock
与port
在调用期间被调用线程锁定。
int driver_pushq_bin(ErlDrvPort port, ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)
将数据放入二进制文件bin
,在offset
有长len
在司机队伍的最前面。它通常比driver_pushq
,因为不需要复制任何数据。
可以从任何线程调用此函数,如果port data lock
与port
在调用期间被调用线程锁定。
返回值是0
。
int driver_pushqv(ErlDrvPort port, ErlIOVec *ev, ErlDrvSizeT skip)
将数据放入ev
跳过第一个skip
它的字节数,位于驱动程序队列的开头。它比driver_pushq
,因为不需要复制任何数据。
返回值是0
。
可以从任何线程调用此函数,如果port data lock
与port
在调用期间被调用线程锁定。
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_READ
,ERL_DRV_WRITE
和ERL_DRV_USE
。前两个指定是否等待读取事件和/或写入事件。触发的读取事件调用ready_input
和触发的写入事件调用ready_output
。
注
某些操作系统(Windows)不区分读取和写入事件。触发事件的回调函数仅取决于值mode
。
ERL_DRV_USE
指定是使用事件对象还是要关闭它。在支持smp的模拟器上,清除所有事件后关闭事件对象是不安全的。driver_select
已经回来了。另一个线程仍然可以在内部使用事件对象。若要安全关闭事件对象,请调用driver_select
带着ERL_DRV_USE
和on==0
,它清除所有事件,然后调用stop_select
或安排在关闭事件对象安全时调用它。ERL_DRV_USE
与事件对象的第一个事件一起设置。设置是无害的ERL_DRV_USE
即使已经完成了。清除所有事件但保持ERL_DRV_USE
SET表示我们正在使用事件对象,并且可能会再次设置事件。
注
ERL_DRV_USE
在Erlang/OTP R13中添加。旧的驱动程序仍然像以前一样工作,但建议更新它们以使用ERL_DRV_USE
和stop_select
以确保以安全方式关闭事件对象。
返回值是0
,除非ready_input
/ ready_output
是NULL
,在这种情况下-1
。
int driver_send_term(ErlDrvPort port, ErlDrvTermData receiver, ErlDrvTermData* term, int n)
警告
这一功能已被废弃。
使用erl_drv_send_term
相反。
注
运行时系统在任意线程执行时无法正确检查此函数的参数。这可能会导致函数在需要时不会失败。
参数term
和n
工作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 lock
与port
在调用期间被调用线程锁定。
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
字节是不同的,如果ev
含len
字节或更多,它是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 flag
在driver_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
时间单位val
。to
返回值的时间单位。
回报ERL_DRV_TIME_ERROR
如果使用无效的时间单位参数调用。
另见ErlDrvTime
和ErlDrvTimeUnit
...
int erl_drv_equal_tids(ErlDrvTid tid1, ErlDrvTid tid2)
比较两个线程标识符,tid1
和tid2
为了平等。
回报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
如果使用无效的时间单元参数调用,或者从不是调度程序线程的线程调用。
另见ErlDrvTime
和ErlDrvTimeUnit
。
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
和有符号整数数据类型。ErlDrvSInt
64位宽在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_STRING
。ERL_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_binary
,erl_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
而不是创建一个ErlDrvBinary
透driver_alloc_binary
然后将二进制文件传递给ERL_DRV_BINARY
.运行时系统通常会在以下情况下更智能地分配二进制文件:ERL_DRV_BUF2BINARY
被使用了。但是,如果要传递的二进制文件的内容已驻留在ErlDrvBinary
,通常情况下,最好是将二进制文件传递给ERL_DRV_BINARY
而ErlDrvBinary
有疑问。
的ERL_DRV_UINT
,ERL_DRV_BUF2BINARY
和ERL_DRV_EXT2TERM
项类型ERTS 5.6进行了介绍。
当使用支持SMP的仿真器时,此函数才是线程安全的.
int erl_drv_putenv(const char *key, char *value)
设置环境变量的值。
key
是NULL
-终止字符串,包含环境变量的名称。
value
是NULL
-终止字符串,包含环境变量的新值。
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
。
参数port
,term
,并在中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
标识创建的线程的字符串。它用于在计划的未来调试功能中识别线程。
回报0
在成功的时候,否则errno
值以指示错误。新创建的线程开始在func
,和func
通过arg
作为争论。何时erl_drv_thread_create
返回时,新创建的线程的线程标识符可在*tid
...opts
可以是NULL
指针,或指向ErlDrvThreadOpts
结构。如果opts
是NULL
指针,使用默认选项,否则使用传递的选项。
警告
你不能自己分配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 time
和Erlang system time
转换为time_unit
作为论据通过。
time_unit
返回值的时间单位。
回报ERL_DRV_TIME_ERROR
如果使用无效的时间单元参数调用,或者从不是调度程序线程的线程调用。
另见ErlDrvTime
和ErlDrvTimeUnit
。
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_create
。erl_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
。错误的原子是einval
,enoent
等等。它可以用来从驱动程序制作错误条款。
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
。)
目前只有两个有意义的值flags
:0
意味着数据以列表形式返回,并且PORT_CONTROL_FLAG_BINARY
意味着数据以二进制形式返回control
。
另见
driver_entry(3),erlang(3),erl_ddll(3),部分How to Implement an Alternative Carrier for the Erlang Distribution>在用户指南中