NIF的内存管理接口为enif_alloc/enif_free。

erl_nif.c

void* enif_alloc(size_t size)

{

return erts_alloc_fnf(ERTS_ALC_T_NIF, (Uint) size);

}

erl_alloc.h

ERTS_ALC_INLINE

void *erts_alloc_fnf(ErtsAlcType_t type, Uint size)

{

return (*erts_allctrs[ERTS_ALC_T2A(type)].alloc)(

ERTS_ALC_T2N(type),

erts_allctrs[ERTS_ALC_T2A(type)].extra,

size);

}

可以看出NIF的内存分配将直接通过ERTS_ALC_T_NIF对应的虚拟机内存分配器ERTS_ALC_A_DRIVER分配内存,ERTS_ALC_A_DRIVER也是利用alloc_util框架实现的内存分配器,详细文档请阅读http://www.erlang.org/doc/man/erts_alloc.html。

void enif_free(void* ptr)

{

erts_free(ERTS_ALC_T_NIF, ptr);

}

void erts_free(ErtsAlcType_t type, void *ptr)

{

(*erts_allctrs[ERTS_ALC_T2A(type)].free)(

ERTS_ALC_T2N(type),

erts_allctrs[ERTS_ALC_T2A(type)].extra,

ptr);

}

对于NIF的内存释放过程也是如此,erlang虚拟机内存管理是一个非常庞杂的系统,此处将不进行分析,读者可以简单地将其看作malloc/free接口(虽然其实现要复杂的多)。

NIF的类型系统接口大同小异,基本上对于每种类型,都有一对make和get接口,稍微特殊的是binary类型。

首先来看NIF的利用进程堆分配内存的接口,它们是make类函数均要使用到的:

static ERTS_INLINE Eterm* alloc_heap(ErlNifEnv* env, unsigned need)

{

Eterm* hp = env->hp;

env->hp += need;

if (env->hp <= env->hp_end) {

return hp;

}

/* env的堆来自于其附着的进程的堆, 若env的堆有足够大的空间,则直接在堆内分配,否则将扩大堆 */

return alloc_heap_heavy(env, need, hp);

}

static Eterm* alloc_heap_heavy(ErlNifEnv* env, unsigned need, Eterm* hp)

{

env->hp = hp;

if (env->heap_frag == NULL) {

ASSERT(HEAP_LIMIT(env->proc) == env->hp_end);

HEAP_TOP(env->proc) = env->hp;

}

else {

env->heap_frag->used_size = hp - env->heap_frag->mem;

ASSERT(env->heap_frag->used_size <= env->heap_frag->alloc_size);

}

hp = erts_heap_alloc(env->proc, need, MIN_HEAP_FRAG_SZ);

/* 此处扩大进程的堆 */

env->heap_frag = MBUF(env->proc);

env->hp = hp + need;

env->hp_end = env->heap_frag->mem + env->heap_frag->alloc_size;

return hp;

}

Eterm*erts_heap_alloc(Process* p, Uint need, Uint xtra)

{

ErlHeapFragment* bp;

Eterm* htop;

Uint n;

n = need + xtra;

bp = MBUF(p);

if (bp != NULL && need <= (bp->alloc_size - bp->used_size)) {

Eterm* ret = bp->mem + bp->used_size;

bp->used_size += need;

return ret;

}

/* 进程的堆在开始时是和进程栈连在一起的,当堆不断扩大,直到不足时,分配器将为堆产生一个新的堆内存片段,之后的内存分配都将在新的堆内存片段上进行,这也是一种懒惰方法 */

bp = (ErlHeapFragment*)

ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP_FRAG, ERTS_HEAP_FRAG_SIZE(n));

/* 分配新的堆内存片段,使用ERTS_ALC_T_HEAP_FRAG对应的ERTS_ALC_A_EHEAP分配器分配内存,它也是一个通过alloc_util框架实现的内存分配器 */

htop = HEAP_TOP(p);

if (htop < HEAP_LIMIT(p)) {

*htop = make_pos_bignum_header(HEAP_LIMIT(p)-htop-1);

HEAP_TOP(p) = HEAP_LIMIT(p);

}

bp->next = MBUF(p);

MBUF(p) = bp;

/* 更新进程的堆内存片段信息,堆内存片段是一个单向列表,这也保证了进程堆的自由扩大 */

bp->alloc_size = n;

bp->used_size = need;

MBUF_SIZE(p) += n;

bp->off_heap.first = NULL;

bp->off_heap.overhead = 0;

return bp->mem;

}

#define ERTS_HEAP_ALLOC(Type, Size) \

erts_alloc((Type), (Size))

ERTS_ALC_INLINE void *erts_alloc(ErtsAlcType_t type, Uint size)
{
void *res;
res = (*erts_allctrs[ERTS_ALC_T2A(type)].alloc)(
ERTS_ALC_T2N(type),
erts_allctrs[ERTS_ALC_T2A(type)].extra,
size);
if (!res)
erts_alloc_n_enomem(ERTS_ALC_T2N(type), size);
return res;
}
对于一些常见的类型,其类型构建过程如下:
ERL_NIF_TERM enif_make_int(ErlNifEnv* env, int i)
{
#if SIZEOF_INT == ERTS_SIZEOF_ETERM
return IS_SSMALL(i) ? make_small(i) : small_to_big(i,alloc_heap(env,2));
#elif (SIZEOF_LONG == ERTS_SIZEOF_ETERM) || \
(SIZEOF_LONG_LONG == ERTS_SIZEOF_ETERM)
return make_small(i);
#endif
}
对于64位系统,无需为int分配内存,直接将数据内容放置在ERL_NIF_TERM中即可,对于32位大数字才需要分配内存,可见erlang虚拟机对内存分配已经到了抠门的地步了。
ERL_NIF_TERM enif_make_string(ErlNifEnv* env, const char* string, ErlNifCharEncoding encoding)
{
return enif_make_string_len(env, string, sys_strlen(string), encoding);
}
ERL_NIF_TERM enif_make_string_len(ErlNifEnv* env, const char* string, size_t len, ErlNifCharEncoding encoding)
{
Eterm* hp = alloc_heap(env,len*2);
ASSERT(encoding == ERL_NIF_LATIN1);
return erts_bld_string_n(&hp,NULL,string,len);
}

Eterm erts_bld_string_n(Uint **hpp, Uint *szp, const char *str, Sint len)
{
Eterm res = THE_NON_VALUE;
Sint i = len;
if (szp)
*szp += len*2;
if (hpp) {
res = NIL;
while (--i >= 0) {
res = CONS(*hpp, make_small((byte) str[i]), res);
*hpp += 2;
}
}
return res;
}
string也是列表,因此需要分配两倍内存,一个用于保存指针,另一个用于保存数据,构建string时,需要逆序遍历原先的字符串数组。
ERL_NIF_TERM enif_make_tuple(ErlNifEnv* env, unsigned cnt, ...)
{
Eterm* hp = alloc_heap(env,cnt+1);
Eterm ret = make_tuple(hp);
va_list ap;
*hp++ = make_arityval(cnt);
va_start(ap,cnt);
while (cnt--) {
*hp++ = va_arg(ap,Eterm);   
}
va_end(ap);
return ret;
}
tuple是复合类型,仅仅需要在堆上分配tuple的元组个数+1个Eterm即可,一个用于保存tuple本身,其它的用于记录tuple每个成员。
ERL_NIF_TERM enif_make_list(ErlNifEnv* env, unsigned cnt, ...)
{
if (cnt == 0) {
return NIL;
}
else {
Eterm* hp = alloc_heap(env,cnt*2);
Eterm ret = make_list(hp);
Eterm* last = &ret;
va_list ap;
va_start(ap,cnt);
while (cnt--) {
*last = make_list(hp);
*hp = va_arg(ap,Eterm);
last = ++hp;
++hp;
}
va_end(ap);
*last = NIL;
return ret;
}
}

list分配时也需要分配两倍内存,过程与string类似。
binary的构建有些特殊,分为两个阶段:分配与构造。
binary分配:
unsigned char* enif_make_new_binary(ErlNifEnv* env, size_t size,
ERL_NIF_TERM* termp)
{
flush_env(env);
*termp = new_binary(env->proc, NULL, size);
/* 分配新的binary */
cache_env(env);
return binary_bytes(*termp);
}
Eterm new_binary(Process *p, byte *buf, Uint len)
{
ProcBin* pb;
Binary* bptr;
if (len <= ERL_ONHEAP_BIN_LIMIT) {
ErlHeapBin* hb = (ErlHeapBin *) HAlloc(p, heap_bin_size(len));
hb->thing_word = header_heap_bin(len);
hb->size = len;
if (buf != NULL) {
sys_memcpy(hb->data, buf, len);
}
return make_binary(hb);
}

/* 对于小于ERL_ONHEAP_BIN_LIMIT(64)字节的binary,可以直接分配在进程堆上 */

bptr = erts_bin_nrml_alloc(len);

/* 对于大于ERL_ONHEAP_BIN_LIMIT(64)字节的binary,将通过ERTS_ALC_T_BINARY对应的ERTS_ALC_A_BINARY分配器进行分配, ERTS_ALC_A_BINARY也是利用alloc_util框架实现的内存分配器 */

bptr->flags = 0;
bptr->orig_size = len;
erts_refc_init(&bptr->refc, 1);
if (buf != NULL) {
sys_memcpy(bptr->orig_bytes, buf, len);
}

    /* 然后构建一个进程binary的结构保存刚刚分配的大额binary */
pb = (ProcBin *) HAlloc(p, PROC_BIN_SIZE);
pb->thing_word = HEADER_PROC_BIN;
pb->size = len;
pb->next = MSO(p).first;
MSO(p).first = (struct erl_off_heap_header*)pb;
pb->val = bptr;
pb->bytes = (byte*) bptr->orig_bytes;
pb->flags = 0;
OH_OVERHEAD(&(MSO(p)), pb->size / sizeof(Eterm));
return make_binary(pb);
}

ERTS_GLB_INLINE Binary *erts_bin_nrml_alloc(Uint size)

{

Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD;

void *res;

res = erts_alloc(ERTS_ALC_T_BINARY, bsize);

ERTS_CHK_BIN_ALIGNMENT(res);

return (Binary *) res;

}

binary构造:

Eterm enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin)

{

if (bin->bin_term != THE_NON_VALUE) {

return bin->bin_term;

}

else if (bin->ref_bin != NULL) {

Binary* bptr = bin->ref_bin;

ProcBin* pb;

Eterm bin_term;

/* !! Copy-paste from new_binary() !! */

pb = (ProcBin *) alloc_heap(env, PROC_BIN_SIZE);

pb->thing_word = HEADER_PROC_BIN;

pb->size = bptr->orig_size;

pb->next = MSO(env->proc).first;

MSO(env->proc).first = (struct erl_off_heap_header*) pb;

pb->val = bptr;

pb->bytes = (byte*) bptr->orig_bytes;

pb->flags = 0;

OH_OVERHEAD(&(MSO(env->proc)), pb->size / sizeof(Eterm));

bin_term = make_binary(pb);

if (erts_refc_read(&bptr->refc, 1) == 1) {

/* Total ownership transfer */

bin->ref_bin = NULL;

bin->bin_term = bin_term;

}

return bin_term;

}

else {

flush_env(env);

bin->bin_term = new_binary(env->proc, bin->data, bin->size);

cache_env(env);

return bin->bin_term;

}

}

同样,对于get系列接口,也是大同小异的:
int enif_get_int(ErlNifEnv* env, Eterm term, int* ip)
{
#if SIZEOF_INT ==  ERTS_SIZEOF_ETERM
return term_to_Sint(term, (Sint*)ip);
#elif (SIZEOF_LONG ==  ERTS_SIZEOF_ETERM) || \
(SIZEOF_LONG_LONG ==  ERTS_SIZEOF_ETERM)
Sint i;
if (!term_to_Sint(term, &i) || i < INT_MIN || i > INT_MAX) {
return 0;
}
*ip = (int) i;
return 1;
#else
#  error Unknown word size 
#endif     
}

对于64位系统和32位系统小数,直接可以从Eterm中提取数据内容,对于32位大数,需要一个较复杂的转换过程。

int enif_get_string(ErlNifEnv *env, ERL_NIF_TERM list, char* buf, unsigned len,

ErlNifCharEncoding encoding)

{

Eterm* listptr;

int n = 0;

ASSERT(encoding == ERL_NIF_LATIN1);

if (len < 1) {

return 0;

}

while (is_not_nil(list)) {

if (is_not_list(list)) {

buf[n] = '\0';

return 0;

}

listptr = list_val(list);

if (!is_byte(*listptr)) {

buf[n] = '\0';

return 0;

}

buf[n++] = unsigned_val(*listptr);

if (n >= len) {

buf[n-1] = '\0'; /* truncate */

return -len;

}

list = CDR(listptr);

}

buf[n] = '\0';

return n + 1;

}

取得string时,将重新拷贝一份。
int enif_get_tuple(ErlNifEnv* env, Eterm tpl, int* arity, const Eterm** array)
{
Eterm* ptr;
if (is_not_tuple(tpl)) {
return 0;
}
ptr = tuple_val(tpl);
*arity = arityval(*ptr);
*array = ptr+1;
return 1;
}
元组的取得较为简单,仅仅向调用者返回元组成员个数和元组成员数组。
int enif_get_list_cell(ErlNifEnv* env, Eterm term, Eterm* head, Eterm* tail)
{
Eterm* val;
if (is_not_list(term)) return 0;
val = list_val(term);
    *head = CAR(val);
    *tail = CDR(val);
return 1;
}
列表的取得过程比较麻烦,需要调用者遍历列表,不断对列表调用enif_get_list_cell直到最后一个元素。
int enif_inspect_binary(ErlNifEnv* env, Eterm bin_term, ErlNifBinary* bin)
{
ErtsAlcType_t allocator = is_proc_bound(env) ? ERTS_ALC_T_TMP : ERTS_ALC_T_NIF;
union {
struct enif_tmp_obj_t* tmp;
byte* raw_ptr;
}u;
u.tmp = NULL;
bin->data = erts_get_aligned_binary_bytes_extra(bin_term, &u.raw_ptr, allocator,
   sizeof(struct enif_tmp_obj_t));
if (bin->data == NULL) {
return 0;
}
if (u.tmp != NULL) {
u.tmp->allocator = allocator;
u.tmp->next = env->tmp_obj_list;
u.tmp->dtor = &aligned_binary_dtor;
env->tmp_obj_list = u.tmp;
}
bin->bin_term = bin_term;
    bin->size = binary_size(bin_term);
bin->ref_bin = NULL;
ADD_READONLY_CHECK(env, bin->data, bin->size); 
return 1;
}
byte*erts_get_aligned_binary_bytes_extra(Eterm bin, byte** base_ptr, ErtsAlcType_t allocator, unsigned extra)
{
byte* bytes;
Eterm* real_bin;
Uint byte_size;
Uint offs = 0;
Uint bit_offs = 0;
if (is_not_binary(bin)) {
return NULL;
}
byte_size = binary_size(bin);
real_bin = binary_val(bin);
if (*real_bin == HEADER_SUB_BIN) {
ErlSubBin* sb = (ErlSubBin *) real_bin;
if (sb->bitsize) {
return NULL;
}
offs = sb->offs;
bit_offs = sb->bitoffs;
real_bin = binary_val(sb->orig);
}
if (*real_bin == HEADER_PROC_BIN) {
bytes = ((ProcBin *) real_bin)->bytes + offs;
} else {
bytes = (byte *)(&(((ErlHeapBin *) real_bin)->data)) + offs;
}
if (bit_offs) {
byte* buf = (byte *) erts_alloc(allocator, byte_size + extra);
*base_ptr = buf;
buf += extra;
erts_copy_bits(bytes, bit_offs, 1, buf, 0, 1, byte_size*8);
bytes = buf;
}
return bytes;
}

通常取得binary的时不会有数据拷贝,除非遇到通过匹配切分出的binary,这也是一种懒惰复制方法。

主要的类型系统接口已经分析完了,对于每个类型,都有一个get和make函数,binary类例外,get函数会取得类型的数据内容,make函数会为类型分配内存,并构造类型。

erlang NIF部分接口实现(二)类型系统和内存分配接口相关推荐

  1. 【朝花夕拾】Android性能篇之(二)Java内存分配

    前言       原文:[朝花夕拾]Android性能篇之(二)Java内存分配        在内存方面,相比于C/C++程序员,咱们java系程序员算是比较幸运的,因为对于内存的分配和回收,都交给 ...

  2. cuda二维数组内存分配和数据拷贝

    uda二维数组内存分配和数据拷贝 2016-04-20 10:54 138人阅读 评论(0) 收藏 举报 分类: 机器学习(11) 人工智能(9) 版权声明:本文为博主原创文章,允许转载. 因为cud ...

  3. SylixOS中的动态内存分配【14】--- cache API中的非缓存内存分配接口实现原理

    实现原理 cache API中的非缓存内存分配接口是通过内核堆或arch API接口实现的. 如果CPU没有cache则cache API应该通过宏配置去掉,即使没有去掉实际也会变成内核堆接口的调用. ...

  4. java二维数组 内存分配_java中二维数组内存分配

    区分三种初始化方式: 格式一: 数据类型[][] 数组名 = new 数据类型[m][n]; m:表示这个二维数组有多少个一维数组. n:表示每一个一维数组的元素有多少个. //例:int arr[] ...

  5. java调用sap接口_(二)通过JAVA调用SAP接口 (增加一二级参数)

    /*** Created by gang.xu01@hand-china.com on 2018/12/4*/ public classMultiFromSAP {/*** description: ...

  6. Vulkan系列教程—VMA教程(二)—选择内存类型

    文章目录 前言 一.选择内存类型 二.Usage类型 三.Required and Preferred标识符 四.Explicit memory types(精确的内存分配类型) 五.Dedicate ...

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

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

  8. erlang NIF部分接口实现(四)消息发送

    erlang中不能没有消息和异步过程,NIF也必须有此项能力,这个能力是通过enif_send实现的,它可以在NIF中向一个进程发送消息,但由于消息本身需要跨进程传递,消息的生命周期可能很长,而在er ...

  9. erlang nif 中文手册

    这是翻译erlang官方文档中的 erts-5.9.2的erl_nif部分.翻译完了.水平有限,我就把这个当作是我自己使用了,以后也会继续完善的. erlang nif 中文手册 概括 功能 初始化 ...

最新文章

  1. 2周修改了1000多个Bug后软件项目扭转了局面,未交付银行的现金管理系统健壮起来了...
  2. oracle 状态unknown,解决Oracle crs_stat状态为UNKNOWN有关问题
  3. Python 3.5 socket OSError: [Errno 101] Network is unreachable
  4. python中super的用法实例解析
  5. centos6.5 MySQL 服务器_启用CentOS6.5 64位安装时自带的MySQL数据库服务器
  6. C MySql封装类 高性能连接池_在vc中通过连接池操作mysql(api方式),附c++访问mysql的封装类...
  7. c语言谭浩强ppt课件,编程_C语言学习课件_谭浩强_PPT~1216F.ppt
  8. 计算机领域各个技术——汇总篇
  9. vlc_for_android(基于git-3.0.0)快速集成并播放电视节目直播
  10. 层次分析法(小白必看手机查看)
  11. 电池供电设备增加高精度库仑计
  12. Ubuntu16.04使用语义分割标注工具Semantic-Segmentation-Editor
  13. 一位尚德机构网课老师的一天:从容、热爱与“一键全连”
  14. 【ps功能精通】3.图层和选取
  15. Outlook-VBA-06-邮件另存为
  16. SSH公钥原理(密钥,秘钥,私钥)(看了还是懵逼啊!)
  17. matlab2018a安装(里面有matlab2018a_win64的下载路径以及详细步骤,自己亲自测试运行没有问题!)
  18. 汇编:JMP指令原理
  19. 从张小平这样登月人才的离职事件,看团队期望管理的重要性
  20. Windows | Tensorfow Softmax Regerssion

热门文章

  1. 利用Broadcast及相关组件实现简易音乐播放器功能
  2. OpenVpnCentos7脚本
  3. java Vue中小学图书馆管理系统ssm毕业设计源码maven介绍
  4. 速营社分享:一个人人可做,汇聚了全网不同层次的网赚项目!
  5. 【运行vue项目时遇到的错误解决方案】
  6. 基于GoKit(4)的物联网应用开发沙龙体验
  7. Ubuntu20重启后桌面图标变大了
  8. 番茄工作法——专治拖延症、精神涣散、再要五分钟综合症
  9. 【图像分割】分割网络概览
  10. ng-alain学习