erlang NIF部分接口实现(二)类型系统和内存分配接口
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))
/* 对于小于ERL_ONHEAP_BIN_LIMIT(64)字节的binary,可以直接分配在进程堆上 */
/* 对于大于ERL_ONHEAP_BIN_LIMIT(64)字节的binary,将通过ERTS_ALC_T_BINARY对应的ERTS_ALC_A_BINARY分配器进行分配, ERTS_ALC_A_BINARY也是利用alloc_util框架实现的内存分配器 */
/* 然后构建一个进程binary的结构保存刚刚分配的大额binary */
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;
}
}
对于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;
}
主要的类型系统接口已经分析完了,对于每个类型,都有一个get和make函数,binary类例外,get函数会取得类型的数据内容,make函数会为类型分配内存,并构造类型。
erlang NIF部分接口实现(二)类型系统和内存分配接口相关推荐
- 【朝花夕拾】Android性能篇之(二)Java内存分配
前言 原文:[朝花夕拾]Android性能篇之(二)Java内存分配 在内存方面,相比于C/C++程序员,咱们java系程序员算是比较幸运的,因为对于内存的分配和回收,都交给 ...
- cuda二维数组内存分配和数据拷贝
uda二维数组内存分配和数据拷贝 2016-04-20 10:54 138人阅读 评论(0) 收藏 举报 分类: 机器学习(11) 人工智能(9) 版权声明:本文为博主原创文章,允许转载. 因为cud ...
- SylixOS中的动态内存分配【14】--- cache API中的非缓存内存分配接口实现原理
实现原理 cache API中的非缓存内存分配接口是通过内核堆或arch API接口实现的. 如果CPU没有cache则cache API应该通过宏配置去掉,即使没有去掉实际也会变成内核堆接口的调用. ...
- java二维数组 内存分配_java中二维数组内存分配
区分三种初始化方式: 格式一: 数据类型[][] 数组名 = new 数据类型[m][n]; m:表示这个二维数组有多少个一维数组. n:表示每一个一维数组的元素有多少个. //例:int arr[] ...
- java调用sap接口_(二)通过JAVA调用SAP接口 (增加一二级参数)
/*** Created by gang.xu01@hand-china.com on 2018/12/4*/ public classMultiFromSAP {/*** description: ...
- Vulkan系列教程—VMA教程(二)—选择内存类型
文章目录 前言 一.选择内存类型 二.Usage类型 三.Required and Preferred标识符 四.Explicit memory types(精确的内存分配类型) 五.Dedicate ...
- erlang NIF部分接口实现(一)加载过程及编写框架
最近在项目中频繁用到erlang的NIF接口,以扩展erlang虚拟机的功能,同时又能提供较高的性能. NIF(native implemented functions)从R14B开始支持,其功能在于 ...
- erlang NIF部分接口实现(四)消息发送
erlang中不能没有消息和异步过程,NIF也必须有此项能力,这个能力是通过enif_send实现的,它可以在NIF中向一个进程发送消息,但由于消息本身需要跨进程传递,消息的生命周期可能很长,而在er ...
- erlang nif 中文手册
这是翻译erlang官方文档中的 erts-5.9.2的erl_nif部分.翻译完了.水平有限,我就把这个当作是我自己使用了,以后也会继续完善的. erlang nif 中文手册 概括 功能 初始化 ...
最新文章
- 2周修改了1000多个Bug后软件项目扭转了局面,未交付银行的现金管理系统健壮起来了...
- oracle 状态unknown,解决Oracle crs_stat状态为UNKNOWN有关问题
- Python 3.5 socket OSError: [Errno 101] Network is unreachable
- python中super的用法实例解析
- centos6.5 MySQL 服务器_启用CentOS6.5 64位安装时自带的MySQL数据库服务器
- C MySql封装类 高性能连接池_在vc中通过连接池操作mysql(api方式),附c++访问mysql的封装类...
- c语言谭浩强ppt课件,编程_C语言学习课件_谭浩强_PPT~1216F.ppt
- 计算机领域各个技术——汇总篇
- vlc_for_android(基于git-3.0.0)快速集成并播放电视节目直播
- 层次分析法(小白必看手机查看)
- 电池供电设备增加高精度库仑计
- Ubuntu16.04使用语义分割标注工具Semantic-Segmentation-Editor
- 一位尚德机构网课老师的一天:从容、热爱与“一键全连”
- 【ps功能精通】3.图层和选取
- Outlook-VBA-06-邮件另存为
- SSH公钥原理(密钥,秘钥,私钥)(看了还是懵逼啊!)
- matlab2018a安装(里面有matlab2018a_win64的下载路径以及详细步骤,自己亲自测试运行没有问题!)
- 汇编:JMP指令原理
- 从张小平这样登月人才的离职事件,看团队期望管理的重要性
- Windows | Tensorfow Softmax Regerssion