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部分接口实现(四)消息发送相关推荐

  1. erlang NIF部分接口实现(一)加载过程及编写框架

    最近在项目中频繁用到erlang的NIF接口,以扩展erlang虚拟机的功能,同时又能提供较高的性能. NIF(native implemented functions)从R14B开始支持,其功能在于 ...

  2. erlang NIF部分接口实现(二)类型系统和内存分配接口

    NIF的内存管理接口为enif_alloc/enif_free. erl_nif.c void* enif_alloc(size_t size) { return erts_alloc_fnf(ERT ...

  3. rocketmq 消息 自定义_RocketMQ的消息发送及消费

    RocketMQ消息支持的模式: 消息支持的模式分为三种:NormalProducer(普通同步),消息异步发送,OneWay. 消息同步发送: 普通消息的发送和接收在前面已经演示过了,在前面的案例中 ...

  4. CEMAPI实战攻略(四)——发送短消息

    CEMAPI实战攻略(四)--发送短消息 By 吴春雷 QQ:819543772 EMail:wuchunlei@163.com 四.发送短消息 发送短信是一个相对比较简单的过程,之所以拿出来一节来讨 ...

  5. java 微信api框架_Java架构学习(五十二)使用框架开发微信微信框架介绍整合微信开发框架WxJava使用微信返回消息模板接口使用WxJava框架发送模板消息http协议参数转义知识...

    一.微信框架介绍 1.access_token是公众号的全局唯一调用接口的凭证,它两个小时更新一次. 获取access_token方法: https请求方式: GET https://api.weix ...

  6. 四种策略确保 RabbitMQ 消息发送可靠性!你用哪种?

    微服务可以设计成消息驱动的微服务,响应式系统也可以基于消息中间件来做,从这个角度来说,在互联网应用开发中,消息中间件真的是太重要了. 今天,以 RabbitMQ 为例,松哥来和大家聊一聊消息中间消息发 ...

  7. 企业微信三方开发(四):发送消息

    其他链接 初识微信开发 企业微信三方开发:注册企业微信服务商 企业微信三方开发(一):回调验证及重要参数获取 企业微信三方开发(二):获取access_token 企业微信三方开发(三):网页授权登录 ...

  8. CEMAPI实战攻略(四)——发送短消息(转自http://blog.csdn.net/depraved_survival/archive/2009/03/11/3980446.aspx)

    四. 发送短消息 发送短信是一个相对比较简单的过程,之所以拿出来一节来讨论,是因为我们不仅仅要讨论如何发送短信,还要讨论一个重要的进程,也就是 tmail.exe 进程.简单的讲,这个进程在后台控制着 ...

  9. 微信公众平台模板消息发送接口文档

    为了保证用户不受到骚扰,在开发者出现需要主动提醒.通知用户时,才允许开发者在公众平台网站中模板消息库中选择模板,选择后获得模板ID,再根据模板ID向用户主动推送提醒.通知消息. 模板消息调用时主要需要 ...

最新文章

  1. 生成邮件图片签名的链接Gmail、hotmail 等... ...
  2. 2020 年 11 月程序员工资统计,Java 市场占有率仍第一
  3. STM32L0系列串口重定义的方法
  4. JAVA使用正则表达式给字符串添加分隔符
  5. 卡卡半智能扫地机器人_扫地机器人哪个牌子好?精选五款高智能的扫地机器人...
  6. (6)css常用样式属性--文字样式
  7. PAT 乙级 1008. 数组元素循环右移问题 (20) Java版
  8. 【Flutter】Dart中的抽象类和接口
  9. php_l3arning_notes_2
  10. System Center Data Protection Manager 2007之一安装
  11. 在Linux和qt下安装EasyPr遇到的问题
  12. RM2016视觉开源OpenCv2代码
  13. 开根号的笔算算法图解_手工开根号原理及其步骤
  14. EXP-00091: Exporting questionable statistics
  15. 巴黎世家土味病毒营销,B端创业初期,如何用营销壮大你的种子用户?
  16. 计算机就是三角函数,三角函数计算器
  17. Rockchip RK3588 kernel dts解析之regulator-fixed
  18. mysql远程连接3306不通问题
  19. 湿冷天扫除前挡玻璃视障 应该怎么选?
  20. Domino RESTful

热门文章

  1. 径向Kohn-Sham方程的谱有限元方法
  2. windows10计算机用户密码,怎么关闭win10系统的电脑开机密码?
  3. Flutter智慧城市App
  4. 如何网络成瘾 - 作者:貘貘(转自GameRes论坛)
  5. 便宜好用的无线蓝牙耳机推荐:性价比超高的蓝牙耳机
  6. ap计算机科学公式,ap的计算公式.ppt
  7. LeetCode 2065. 最大化一张图中的路径价值
  8. Linux系统安装snmp服务
  9. 说说恶意软件吧~~Malware 分析
  10. FontAwesome图标字体库和CSS框架