持久资源是NIF中一类非常有用接口,可以把资源看成各种数据结构描述符,然后在各个模块间传递数据结构,从而使得写erlang程序像写c程序一样,弥补一些erlang程序在性能上的不足。
使用持久资源,需要首先创建持久资源类,这些工作可以在NIF被load时进行。
ErlNifResourceType*
enif_open_resource_type(ErlNifEnv* env,
const char* module_str,
const char* name_str,
ErlNifResourceDtor* dtor,
ErlNifResourceFlags flags,
ErlNifResourceFlags* tried)
{
ErlNifResourceType* type = NULL;
ErlNifResourceFlags op = flags;
Eterm module_am, name_am;
ASSERT(erts_smp_thr_progress_is_blocking());
ASSERT(module_str == NULL); /* for now... */
module_am = make_atom(env->mod_nif->mod->module);
name_am = enif_make_atom(env, name_str);
type = find_resource_type(module_am, name_am);
if (type == NULL) {
if (flags & ERL_NIF_RT_CREATE) {
type = erts_alloc(ERTS_ALC_T_NIF, sizeof(struct enif_resource_type_t));
type->dtor = dtor;
type->module = module_am;
type->name = name_am;
erts_refc_init(&type->refc, 1);
type->owner = env->mod_nif;
type->prev = &resource_type_list;
type->next = resource_type_list.next;
type->next->prev = type;
type->prev->next = type;
op = ERL_NIF_RT_CREATE;
}
}
else {
if (flags & ERL_NIF_RT_TAKEOVER) {
steal_resource_type(type);
op = ERL_NIF_RT_TAKEOVER;
}
else {
type = NULL;
}
}
if (type != NULL) {
type->owner = env->mod_nif;
type->dtor = dtor;
if (type->dtor != NULL) {
erts_refc_inc(&type->owner->rt_dtor_cnt, 1);
}
erts_refc_inc(&type->owner->rt_cnt, 1);
}
if (tried != NULL) {
*tried = op;
}
return type;
}
该接口用于创建一类持久资源,资源名为参数name_str,为资源分配资源描述符ErlNifResourceType,并为其记录资源析构器dtor,为了能使得持久资源也使用erlang的gc机制,需要为资源提供一个析构器回调,使得在资源没有被使用时,由gc正确释放。持久资源的析构器需要用户自行实现。
void* enif_alloc_resource(ErlNifResourceType* type, size_t size)
{
Binary* bin = erts_create_magic_binary(SIZEOF_ErlNifResource(size), &nif_resource_dtor);
ErlNifResource* resource = ERTS_MAGIC_BIN_DATA(bin);
resource->type = type;
erts_refc_inc(&bin->refc, 1);
#ifdef DEBUG
erts_refc_init(&resource->nif_refc, 1);
#endif
erts_refc_inc(&resource->type->refc, 2);
return resource->data;
}
#define SIZEOF_ErlNifResource(SIZE) (offsetof(ErlNifResource,data) + (SIZE))
由于持久资源相当于一段内存,因此其最重要的功能即为内存分配,enif_alloc_resource用于分配资源所占的内存。
ERTS_GLB_INLINE Binary *erts_create_magic_binary(Uint size, void (*destructor)(Binary *))
{
Uint bsize = ERTS_MAGIC_BIN_SIZE(size);
Binary* bptr = erts_alloc_fnf(ERTS_ALC_T_BINARY, bsize);
if (!bptr)
erts_alloc_n_enomem(ERTS_ALC_T2N(ERTS_ALC_T_BINARY), bsize);
ERTS_CHK_BIN_ALIGNMENT(bptr);
bptr->flags = BIN_FLAG_MAGIC;
bptr->orig_size = ERTS_MAGIC_BIN_ORIG_SIZE(size);
erts_refc_init(&bptr->refc, 0);
ERTS_MAGIC_BIN_DESTRUCTOR(bptr) = destructor;
return bptr;
}
#define ERTS_MAGIC_BIN_SIZE(Sz) (offsetof(ErtsMagicBinary,magic_bin_data) + (Sz))
可以看到持久资源的内存分配器,也是ERTS_ALC_A_BINARY,但是其分配的内存包括三个部分:
1.ErtsMagicBinary:用于将资源融入ERTS_ALC_A_BINARY分配器;
2.ErlNifResource:用于将建立持久资源的抽象层;
3.持久资源本身:持久资源所占内存部分。
这里有个值得注意的地方,在erts_create_magic_binary调用erts_alloc_fnf时,返回的不是一个ErtsMagicBinary结构,而是一个Binary结构。
typedef struct binary {
ERTS_INTERNAL_BINARY_FIELDS
SWord orig_size;
char orig_bytes[1]; /* to be continued */
} Binary;
typedef struct {
ERTS_INTERNAL_BINARY_FIELDS
SWord orig_size;
void (*destructor)(Binary *);
char magic_bin_data[1];
} ErtsMagicBinary;
#define ERTS_INTERNAL_BINARY_FIELDS \
UWord flags; \
erts_refc_t refc; \
ERTS_BINARY_STRUCT_ALIGNMENT
ErtsMagicBinary与Binary在拥有公共的成员,包括flags,refc等,而flags中记录了数据结构的真正类型,erts_create_magic_binary将此处分配的Binary初始化为BIN_FLAG_MAGIC类型,这是一种多态的手法。
除了分配内存,enif_alloc_resource还将资源的析构器设置为nif_resource_dtor:
static void nif_resource_dtor(Binary* bin)
{
ErlNifResource* resource = (ErlNifResource*) ERTS_MAGIC_BIN_DATA(bin);
ErlNifResourceType* type = resource->type;
ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == &nif_resource_dtor);
if (type->dtor != NULL) {
ErlNifEnv env;
pre_nif_noproc(&env, type->owner);
type->dtor(&env,resource->data);
post_nif_noproc(&env);
}
if (erts_refc_dectest(&type->refc, 0) == 0) {
ASSERT(type->next == NULL);
ASSERT(type->owner != NULL);
ASSERT(type->owner->mod == NULL);
steal_resource_type(type);
erts_free(ERTS_ALC_T_NIF, type);
}
}
可以看到,这个析构器是真正的外层融入gc的析构器,它从资源的
ErtsMagicBinary结构中取得ErlNifResource结构,利用这个结构内记录的资源真正的析构器释放资源。
enif_alloc_resource在为资源分配内存时,还需要为资源设置正确的类型,类型即为该函数的第一个参数。
资源可以通过make接口传递到erlang模块中:
ERL_NIF_TERM enif_make_resource(ErlNifEnv* env, void* obj)
{
ErlNifResource* resource = DATA_TO_RESOURCE(obj);
ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_DATA(resource);
Eterm* hp = alloc_heap(env,PROC_BIN_SIZE);
return erts_mk_magic_binary_term(&hp, &MSO(env->proc), &bin->binary);
}
ERTS_GLB_INLINE Eterm
erts_mk_magic_binary_term(Eterm **hpp, ErlOffHeap *ohp, Binary *mbp)
{
ProcBin *pb = (ProcBin *) *hpp;
*hpp += PROC_BIN_SIZE;
ASSERT(mbp->flags & BIN_FLAG_MAGIC);
pb->thing_word = HEADER_PROC_BIN;
pb->size = 0;
pb->next = ohp->first;
ohp->first = (struct erl_off_heap_header*) pb;
pb->val = mbp;
pb->bytes = (byte *) mbp->orig_bytes;
pb->flags = 0;
erts_refc_inc(&mbp->refc, 1);
return make_binary(pb);
}
首先需要在进程堆上分配一个ProcBin结构,使得包裹资源的Binary能够传递到进程执行流中去,注意,通常binary在小于64字节时,可以直接分配到进程堆上,但是由于资源并不是特定于进程的,所以应该由ERTS_ALC_A_BINARY分配器分配,而不是由ERTS_ALC_A_EHEAP分配器分配,其生命周期也与进程无关。另外需要注意的是,持久资源对应的ProcBin结构,其数据长度为0,用户在调用资源创建接口建立的资源,在控制台上的输出也是<<>>。
如果将来需要在某个时机取得资源,可以调用如下接口:
int enif_get_resource(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifResourceType* type,
void** objp)
{
ProcBin* pb;
Binary* mbin;
ErlNifResource* resource;
if (!ERTS_TERM_IS_MAGIC_BINARY(term)) {
return 0;
}
pb = (ProcBin*) binary_val(term);
/*if (pb->size != 0) {
return 0; / * Or should we allow "resource binaries" as handles? * /
}*/
mbin = pb->val;
resource = (ErlNifResource*) ERTS_MAGIC_BIN_DATA(mbin);
if (ERTS_MAGIC_BIN_DESTRUCTOR(mbin) != &nif_resource_dtor
|| resource->type != type) {
return 0;
}
*objp = resource->data;
return 1;
}
#define ERTS_MAGIC_BIN_DATA(BP) \
((void *) ((ErtsBinary *) (BP))->magic_binary.magic_bin_data)
binary本身是有多态机制的,在从binary中提取资源描述符时,也需要判断binary是否ErtsMagicBinary,可以通过
ERTS_TERM_IS_MAGIC_BINARY宏进行判断,资源描述符ErlNifResource是包裹在ErtsMagicBinary结构中的,而真正的资源数据结构又是包裹在ErlNifResource结构中的,就像剥洋葱,一层一层的剥去外层数据结构,最终到的持久资源本身的数据结构。
gc可以在正确的时机释放持久资源所占的内存了,如果用户需要主动释放内存,可以调用:
void enif_release_resource(void* obj)
{
ErlNifResource* resource = DATA_TO_RESOURCE(obj);
ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_DATA(resource);
ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == &nif_resource_dtor);
#ifdef DEBUG
erts_refc_dec(&resource->nif_refc, 0);
#endif
if (erts_refc_dectest(&bin->binary.refc, 0) == 0) {
erts_bin_free(&bin->binary);
}
}
由于资源可以被共享,因此资源本身是有引用计数的,当引用计数减为0,资源就可以被真正释放了。
增加资源的引用计数可以通过如下接口:
void enif_keep_resource(void* obj)
{
ErlNifResource* resource = DATA_TO_RESOURCE(obj);
ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_DATA(resource);
ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == &nif_resource_dtor);
#ifdef DEBUG
erts_refc_inc(&resource->nif_refc, 1);
#endif
erts_refc_inc(&bin->binary.refc, 2);
}
增加和减少资源的引用计数的次数必须匹配,否则可能引发问题。
至此,持久资源的主要接口的实现就介绍完了,用户使用时,可以先通过enif_open_resource_type建立资源类型的描述符,然后利用此描述符,使用enif_alloc_resource分配资源所占用的内存空间,使用enif_make_resource将资源导出到erlang模块层,在进程间传递资源描述符,资源再传回NIF时,可以通过enif_get_resource取回资源描述符中的资源数据结构,同时可以通过enif_keep_resource来共享资源,通过enif_release_resource来放弃使用资源,gc系统也会正确回收引用计数为0的资源,开发者再也不用担心内存没有被正确释放了。
持久资源为NIF的开发带来了极大的便利,用户可以将一些大规模的数据结构一次传入内存,生成一个资源描述符,然后在进程间传递资源描述符而不是资源数据本身,减轻每次资源数据拷贝的开销,同时持久资源也是线程安全的,写erlang程序也可以像写c程序一样高效了。
分享到:
相关推荐
在Erlang生态系统中,Native Implemented Functions (NIFs) 是一种机制,允许开发者用其他语言(如C、C++或Rust)编写性能关键部分的代码,然后在Erlang虚拟机(VM)中调用。Rustler是一个库,专门用于简化使用Rust...
Erlang NIF(Native Implemented Functions)是Erlang虚拟机提供的一种机制,允许开发者用C语言或者其他低级语言编写性能关键部分的代码,并在Erlang系统中无缝调用。这种方式可以充分利用C语言的高效性,同时保持...
在Erlang中,`NIF (Native Implemented Functions)` 是一种机制,允许开发者使用其他语言(如C或Rust)编写高效性能的代码,并在Erlang虚拟机中调用。本篇主要介绍如何利用Rust语言来开发Erlang NIF,并探讨`erl_nif...
示例Rustler是一个用于以安全的Rust代码编写Erlang NIF的库。 这意味着应该没有任何办法。 入门指南 示例Rustler是一个用于以安全的Rust代码编写Erlang NIF的库。 这意味着应该没有办法使BEAM(Erlang VM)崩溃。 该...
在Erlang中,为了实现与C或其他低级语言的高效交互,Erlang提供了一个名为`erl_nif`的接口。本文将深入探讨`erl_nif`,了解它是如何扩展Erlang的功能,并讨论如何使用它来提升性能。 `erl_nif`(Erlang NIF,Native...
Windows下使用NIF扩展Erlang完整例子,包含nif工程项目,erlang引用例子。 配套文章:http://blog.csdn.net/mycwq/article/details/17527485
Rust 中的 Erlang NIF 这是一个如何在 Rust 中实现 NIF 的示例。 它对我有用,也可能对你有用,但如果它吃掉了你的作业,请不要生气。 虽然这将是可行的写现实世界的代码下面这个例子,因为整个erl_nif.h接口可用...
#### 三、Erlang与C语言接口的工作原理 在Erlang中,与C语言程序进行通信通常是通过端口(Port)机制来实现的。端口是Erlang提供的一种特殊机制,用于实现与外部程序的交互。下面详细介绍端口机制的工作原理: 1. **...
ErlPuzzle - Erlang 的 libpuzzle NIF Puzzle 库旨在快速找到视觉上相似的图像(gif、png、jpg),即使它们已被调整大小、重新压缩、重新着色或稍微修改。 该库是免费的、轻量级的但非常快速、可配置、易于使用,...
在Erlang NIF中实现的SHA-224,SHA-256,SHA-384,SHA-512。描述erlsha2库应用程序使用Erlang NIF实施SHA-2安全哈希标准(SHA-224,SHA-256,SHA-384,SHA-512)。 (它也提供纯的Erlang实现,尽管它们比C NIF实现慢...
该库以nif库的形式实现,可以最快地访问sqlite数据库。 这可能是有风险的,因为nif库或sqlite数据库中的错误可能会使整个Erlang VM崩溃。 如果您不想冒险,总是可以从单独的erlang节点访问sqlite nif。 特别注意...
- **Erlang模块**:Erlang代码部分,定义了与NIF交互的接口函数,这些函数在Erlang代码中可以像普通函数一样调用。 - **NIF接口定义**:Erlang模块中会有`erlang:nifEXPORTS`列表,列出所有暴露给Erlang的NIF函数...
盗贼文档|入门|例子Rustler 是一个用安全的 Rust 代码编写 Erlang NIF 的库。这意味着应该没有办法让 BEAM (Erlang VM) 崩溃。该库提供了用于生成与 BEAM 交互的样板的工具,处理 Erlang 术语的编码和解码,并在它们...
在Erlang应用中,这些NIF函数可以通过标准的Erlang/Elixir接口调用,就像调用普通的Erlang模块一样。这样,开发者可以在保持Erlang/Elixir应用的并发性和容错性的同时,利用Rust的性能和安全性来处理计算密集型任务...
这是第一卷。 在2008 CN Erlounge III的“Erlang应用程序接口”讲演的视频。PPT等其它资料在这里: http://blog.csdn.net/aimingoo/archive/2009/01/14/3777765.aspx 有关信息参见: ...
**enif_protobuf** 是一个基于 **Erlang ...总之,enif_protobuf是Erlang社区对Google Protobuf的一种强大实现,通过Erlang NIF机制实现了高性能的数据序列化和反序列化,简化了Erlang系统与其他系统之间的数据交换。
此外,提供的"erlang的timer和实现机制.pdf"和"更多erlang资料下载.txt"也是进一步学习和研究的好资源。 总之,Erlang的timer模块是其强大并发能力的一个体现,通过熟练掌握这一部分,开发者可以构建出更加高效和...
我在Erlounge III大会上的讲演PPT。 相关的视频在这里: http://groups.google.com/group/erlang-china/browse_thread/thread/2154c39503795edc
wpi - 用于 Raspberry Pi 的 WiringPi 库的 Erlang NIF 此应用程序是围绕库的 Erlang 包装器,该库是 Arduino 的 Wiring 库的 Raspberry Pi 方言。 wpi 使得读取和写入 GPIO 引脚、写入 LCD、移入和移出位或通过...