erlang中不能没有消息和异步过程,NIF也必须有此项能力,这个能力是通过enif_send实现的,它可以在NIF中向一个进程发送消息,但由于消息本身需要跨进程传递,消息的生命周期可能很长,而在erlang NIF部分接口实现(一)中可以看到,NIF每次调用所使用的ErlNifEnv结构是位于process_main函数的栈上的,由这个ErlNifEnv结构分配消息所占用的内存是不可能的,因此需要一个长期存在的ErlNifEnv结构来回收消息的内存,而ErlNifEnv结构是附着于一个进程的,同时也需要一个Process结构,产生分配内存的堆。
为了构建这个长期存在的ErlNifEnv结构,需要能够动态的分配ErlNifEnv结构:
struct enif_msg_environment_t
{
ErlNifEnv env;
Process phony_proc;
};
ErlNifEnv* enif_alloc_env(void)
{
struct enif_msg_environment_t* msg_env =
erts_alloc_fnf(ERTS_ALC_T_NIF, sizeof(struct enif_msg_environment_t));
Eterm* phony_heap = (Eterm*) msg_env; /* dummy non-NULL ptr */
msg_env->env.hp = phony_heap;
msg_env->env.hp_end = phony_heap;
msg_env->env.heap_frag = NULL;
msg_env->env.mod_nif = NULL;
msg_env->env.tmp_obj_list = NULL;
msg_env->env.proc = &msg_env->phony_proc;
memset(&msg_env->phony_proc, 0, sizeof(Process));
HEAP_START(&msg_env->phony_proc) = phony_heap;
HEAP_TOP(&msg_env->phony_proc) = phony_heap;
HEAP_LIMIT(&msg_env->phony_proc) = phony_heap;
HEAP_END(&msg_env->phony_proc) = phony_heap;
MBUF(&msg_env->phony_proc) = NULL;
msg_env->phony_proc.id = ERTS_INVALID_PID;
#ifdef FORCE_HEAP_FRAGS
msg_env->phony_proc.space_verified = 0;
msg_env->phony_proc.space_verified_from = NULL;
#endif
return &msg_env->env;
}
该函数将在内存中产生一个enif_msg_environment_t结构,它包含两个成员,env和 phony_proc,env即为长期ErlNifEnv结构,而phony_proc即为env附着的伪Process结构,env借助phony_proc的堆分配消息所占的内存。
任何需要发送的消息的term,都必须通过这个动态产生的env分配,而之后调用enif_send发送消息时,消息及其动态env必须一致。
可以通过enif_free_env来释放enif_msg_environment_t结构:
void enif_free_env(ErlNifEnv* env)
{
enif_clear_env(env);
erts_free(ERTS_ALC_T_NIF, env);
}
也可以通过enif_clear_env来清洗一个ErlNifEnv结构以重用:
void enif_clear_env(ErlNifEnv* env)
{
struct enif_msg_environment_t* menv = (struct enif_msg_environment_t*)env;
Process* p = &menv->phony_proc;
ASSERT(p == menv->env.proc);
ASSERT(p->id == ERTS_INVALID_PID);
ASSERT(MBUF(p) == menv->env.heap_frag);
if (MBUF(p) != NULL) {
erts_cleanup_offheap(&MSO(p));
clear_offheap(&MSO(p));
free_message_buffer(MBUF(p));
MBUF(p) = NULL;
menv->env.heap_frag = NULL;
}
ASSERT(HEAP_TOP(p) == HEAP_END(p));
menv->env.hp = menv->env.hp_end = HEAP_TOP(p);
ASSERT(!is_offheap(&MSO(p)));
free_tmp_objs(env);
}
有了这个长期存在的ErlNifEnv结构,就可以利用它来产生消息所需要的内存并发送该消息了:
int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, ErlNifEnv* msg_env, ERL_NIF_TERM msg)
{
struct enif_msg_environment_t* menv = (struct enif_msg_environment_t*)msg_env;
ErtsProcLocks rp_locks = 0;
Process* rp;
Process* c_p;
ErlHeapFragment* frags;
#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP)
ErtsProcLocks rp_had_locks;
#endif
Eterm receiver = to_pid->pid;
int flush_me = 0;
if (env != NULL) {
c_p = env->proc;
if (receiver == c_p->id) {
rp_locks = ERTS_PROC_LOCK_MAIN;
flush_me = 1;
}
}
else {
#ifdef ERTS_SMP
c_p = NULL;
#else
erl_exit(ERTS_ABORT_EXIT,"enif_send: env==NULL on non-SMP VM");
#endif
}
#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP)
rp_had_locks = rp_locks;
#endif
rp = erts_pid2proc_opt(c_p, ERTS_PROC_LOCK_MAIN,
receiver, rp_locks, ERTS_P2P_FLG_SMP_INC_REFC);
/* 临时增加消息目的进程的引用计数,防止在发送途中目的进程被销毁。 */
if (rp == NULL) {
ASSERT(env == NULL || receiver != c_p->id);
return 0;
}
flush_env(msg_env);
frags = menv->env.heap_frag;
ASSERT(frags == MBUF(&menv->phony_proc));
if (frags != NULL) {
/* Move all offheap's from phony proc to the first fragment.
Quick and dirty, but erts_move_msg_mbuf_to_heap doesn't care. */
ASSERT(!is_offheap(&frags->off_heap));
frags->off_heap = MSO(&menv->phony_proc);
clear_offheap(&MSO(&menv->phony_proc));
menv->env.heap_frag = NULL;
MBUF(&menv->phony_proc) = NULL;
}
ASSERT(!is_offheap(&MSO(&menv->phony_proc)));
if (flush_me) {
flush_env(env); /* Needed for ERTS_HOLE_CHECK */
}
erts_queue_message(rp, &rp_locks, frags, msg, am_undefined
#ifdef USE_VM_PROBES
, NIL
#endif
);
/* 这是erlang内部由于将消息投递到目的进程消息队列的函数 */
if (rp_locks) {
ERTS_SMP_LC_ASSERT(rp_locks == (rp_had_locks | (ERTS_PROC_LOCK_MSGQ |
ERTS_PROC_LOCK_STATUS)));
erts_smp_proc_unlock(rp, (ERTS_PROC_LOCK_MSGQ | ERTS_PROC_LOCK_STATUS));
}
erts_smp_proc_dec_refc(rp);
if (flush_me) {
cache_env(env);
}
return 1;
}
该接口的to_pid参数即为消息的目的进程,msg_env即为通过enif_alloc_env所产生的长期ErlNifEnv,通过前面的代码分析可以发现,该结构实际嵌入一个enif_msg_environment_t结构,msg参数是需要发送的消息,它是由msg_env所分配的term,消息将通过erts_queue_message转移到目的进程的消息队列中。
在有了异步消息投递能力后,NIF可以的功能将极大的丰富。
分享到:
相关推荐
erlang nif test demo
https://blog.csdn.net/ap114/article/details/118092301
原贴 https://blog.csdn.net/ap114/article/details/118092301 用rust 开发 erlang nif的正确做法
Windows下使用NIF扩展Erlang完整例子,包含nif工程项目,erlang引用例子。 配套文章:http://blog.csdn.net/mycwq/article/details/17527485
Rust 中的 Erlang NIF 这是一个如何在 Rust 中实现 NIF 的示例。 它对我有用,也可能对你有用,但如果它吃掉了你的作业,请不要生气。 虽然这将是可行的写现实世界的代码下面这个例子,因为整个erl_nif.h接口可用...
erlang -c语言程序接口.pdferlang -c语言程序接erlang -c语言程序接口.pdf口.pdf
该库以nif库的形式实现,可以最快地访问sqlite数据库。 这可能是有风险的,因为nif库或sqlite数据库中的错误可能会使整个Erlang VM崩溃。 如果您不想冒险,总是可以从单独的erlang节点访问sqlite nif。 特别注意...
在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实现慢...
erl_nif 扩展erlang的另外一种方法
这是第一卷。 在2008 CN Erlounge III的“Erlang应用程序接口”讲演的视频。PPT等其它资料在这里: http://blog.csdn.net/aimingoo/archive/2009/01/14/3777765.aspx 有关信息参见: ...
示例Rustler是一个用于以安全的Rust代码编写Erlang NIF的库。 这意味着应该没有任何办法。 入门指南 示例Rustler是一个用于以安全的Rust代码编写Erlang NIF的库。 这意味着应该没有办法使BEAM(Erlang VM)崩溃。 该...
盗贼文档|入门|例子Rustler 是一个用安全的 Rust 代码编写 Erlang NIF 的库。这意味着应该没有办法让 BEAM (Erlang VM) 崩溃。该库提供了用于生成与 BEAM 交互的样板的工具,处理 Erlang 术语的编码和解码,并在它们...
enif_protobuf:使用enif(Erlang nif)的Google Protobuf实现
erlang的timer和实现机制 Erlang程序设计
erlang_tc Erlang NIF用于 。 建造 $ make 测试 $ make test
我在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、移入和移出位或通过...
按位:NIF示例显示Erlang调度程序的问题bitwise模块实现了几个Erlang本机实现函数(NIF),旨在显示NIF对Erlang调度程序线程可能产生的几种不同影响。 该模块提供的功能的几个变体exor/2采用一个二进制和一个字节值...
erlang四大behaviour之四-supervisor