Erlang数据类型的内部实现
来源:互联网 发布:婚纱摄影的网络邀约 编辑:程序博客网 时间:2024/05/22 13:40
Erlang中有8种基础数据类型(integer、 float、 atom、 reference、 fun、 port、 pid、 binary)和两种复合结构(tuple、list)。在erl编程时,对这些数据类的内存分配及引用都是透明的,了解erl数据类型的内部实现,可以让我们更优更合理的运用数据类型,更好地评估程序性能。
假设erl中有这么一组数据: 1、2、3、"a"、"b"、"c",
在erl虚拟机(vm)中将会这样描述erl数据:1F、2F、3F、3214B、4B6CB、1EDCB,
从上面可以看出,在vm内部,erl的数据类型是以后缀来识别的,比如小整数的后缀都为F,封装方法可用C表示为 (var << 4) | 0xF。
同时可以看出字符串的后缀都为B,但却不同与小整数的封装方法,数字串以及更多的数据类型究竟是如何封装的呢?我们继续往下看。
下面是一些erl数据 对应 在vm中的描述的示例。
以上看来,有些类型似乎不好找规律了,只好翻阅erlang源码,一探究竟。
erl_term.h中可以看到如下代码:
#define _TAG_PRIMARY_SIZE2#define _TAG_PRIMARY_MASK0x3#define TAG_PRIMARY_HEADER0x0#define TAG_PRIMARY_LIST0x1#define TAG_PRIMARY_BOXED0x2#define TAG_PRIMARY_IMMED10x3#define _TAG_IMMED1_SIZE4#define _TAG_IMMED1_MASK0xF#define _TAG_IMMED1_PID((0x0 << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_IMMED1)#define _TAG_IMMED1_PORT((0x1 << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_IMMED1)#define _TAG_IMMED1_IMMED2((0x2 << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_IMMED1)#define _TAG_IMMED1_SMALL((0x3 << _TAG_PRIMARY_SIZE) | TAG_PRIMARY_IMMED1)#define _TAG_IMMED2_SIZE6#define _TAG_IMMED2_MASK0x3F#define _TAG_IMMED2_ATOM((0x0 << _TAG_IMMED1_SIZE) | _TAG_IMMED1_IMMED2)#define _TAG_IMMED2_CATCH((0x1 << _TAG_IMMED1_SIZE) | _TAG_IMMED1_IMMED2)#define _TAG_IMMED2_NIL((0x3 << _TAG_IMMED1_SIZE) | _TAG_IMMED1_IMMED2)
我们来简化一下代码,并将16进制数改为用2进制来表示,如下:
#define _TAG_PRIMARY_SIZE2#define _TAG_PRIMARY_MASK11B#define TAG_PRIMARY_HEADER00B#define TAG_PRIMARY_LIST01B#define TAG_PRIMARY_BOXED10B#define TAG_PRIMARY_IMMED111B#define _TAG_IMMED1_SIZE4#define _TAG_IMMED1_MASK1111B#define _TAG_IMMED1_PID0011B#define _TAG_IMMED1_PORT0111B#define _TAG_IMMED1_IMMED21011B#define _TAG_IMMED1_SMALL1111B#define _TAG_IMMED2_SIZE6#define _TAG_IMMED2_MASK111111B#define _TAG_IMMED2_ATOM001011B#define _TAG_IMMED2_CATCH011011B#define _TAG_IMMED2_NIL111011B
由上可看出,对这些类型进行了分层,每2位就一个层次,分别为primary、immed1、immed2。
知道了这些定义,对于erl数据类型的内部实现就已清楚了大半,以下举例说明。
例1:判断Erlang数据是否为list的方法
如果要判断一个erl数据是否为list,只需要判断最后两位是否为01,可描述为:(var & 11B) == 01B现在我们来验证一下表1中的一组的数据。
-----------------
[1,2] -> 0x1B0BDE9
[3,4] -> 0x1B0BEA5
-----------------
1B0BDE9和1B0BEA5的最后一位用2进制来表示,分别为:1001B,0101B
可见最后两位都为01,即为TAG_PRIMARY_LIST的值。
例2:检测Erlang数据类型的方法
假设vm中收到一个值为0xFFFFFFFB的erl变量,要想知道这是一个什么数据,可做如下分析。0xFFFFFFFB的最后一个字节(0xFB)用2进制表示为:0x11111011B
首先判断TAG_PRIMARY,也就是最后两位,值为11B,对应了上面的定义TAG_PRIMARY_IMMED1。
目前只知道它是immed1类型, 要想进一步了解它是什么东西,只能继续判断前面两位(10B)。
现在知道最后四位为1011B,对应定义_TAG_IMMED1_IMMED2,即为immed2,仍然不明确它的类型,
只能再取前面两位(11B), 现在知道最后六位为111011B,对应_TAG_IMMED2_NIL,
说明它是一个nil数据,即为一个空的list(或者是list的末端,具体内容在以后文章中进一步阐述)。
例3:Erlang中的整型处理
在表1中:-----------------
1 -> 0x1F
2 -> 0x2F
-----------------
整数1和2封装成erl数据分别为1F和2F,从这里我们很容易知道它们的封装和解封取值的过程,
将1F解封取出整数值,只需要右移四位(1F >> 4)即可。
但是对于大整数,这方法就不可取了,如:
-----------------------------------
1234567890 -> 0x1B0A37A
1234567891 -> 0x1B0A3A2
-----------------------------------
以例2的方法,可知0x1B0A37A和0x1B0A3A2是BOXED类型,0x1B0A37A和0x1B0A3A2去掉tag的值后,其实就是一个erl内部的数据指针。
可见erl内部将整数分为小整型和大整型, 但对于erl程编来说,它是透明的。
例4:Erlang中的字符串实现方式
------------------------"ab" -> 0x1B0A19D
"cb" -> 0x1B0A1CD
------------------------
以例1的方法,可以知道0x1B0A19D和0x1B0A1CD均是list类型,说明erl中的字符串是用list实现的。
和例3中的大整数类似,0x1B0A19D和0x1B0A1CD去掉tag值以后,就是指向字符串的指针值。
在源码中有一段关于header的注释:
/* * HEADER representation: * *aaaaaaaaaaaaaaaaaaaaaaaaaatttt00arity:26, tag:4 * * HEADER tags: * *0000ARITYVAL * 0001 BINARY_AGGREGATE | *001xBIGNUM with sign bit| *0100REF| *0101FUN| THINGS *0110FLONUM| * 0111 EXPORT | *1000REFC_BINARY|| *1001HEAP_BINARY| BINARIES| *1010SUB_BINARY|| * 1011 Not used * 1100 EXTERNAL_PID | | * 1101 EXTERNAL_PORT | EXTERNAL THINGS | * 1110 EXTERNAL_REF | | * 1111 Not used * * COMMENTS: * * - The tag is zero for arityval and non-zero for thing headers。 * - A single bit differentiates between positive and negative bignums。 * - If more tags are needed, the REF and and EXTERNAL_REF tags could probably * be combined to one tag。 * * XXX: globally replace XXX_SUBTAG with TAG_HEADER_XXX */
在前面例子中我们可以知道,在vm中,像大整数、tuple等类似的数据在传递时,只是传递了一个被封装过的类型为boxed的指针值,要想进一步了解指针所指的内容结构,
首先要取出内容中的开头的4个字节,即为header值。
现在我们假设有一个header值为0xC0(11000000B),从上面注释中可以得知,尾部为000000B的header对应的header tag为arityval,
关于header更具体的说明,还要参阅以下代码及注释。
#define ARITYVAL_SUBTAG(0x0 << _TAG_PRIMARY_SIZE) /* TUPLE */#define BIN_MATCHSTATE_SUBTAG(0x1 << _TAG_PRIMARY_SIZE) #define POS_BIG_SUBTAG(0x2 << _TAG_PRIMARY_SIZE) /* BIG: tags 2&3 */#define NEG_BIG_SUBTAG(0x3 << _TAG_PRIMARY_SIZE) /* BIG: tags 2&3 */#define _BIG_SIGN_BIT(0x1 << _TAG_PRIMARY_SIZE)#define REF_SUBTAG(0x4 << _TAG_PRIMARY_SIZE) /* REF */#define FUN_SUBTAG(0x5 << _TAG_PRIMARY_SIZE) /* FUN */#define FLOAT_SUBTAG(0x6 << _TAG_PRIMARY_SIZE) /* FLOAT */#define EXPORT_SUBTAG(0x7 << _TAG_PRIMARY_SIZE) /* FLOAT */#define _BINARY_XXX_MASK(0x3 << _TAG_PRIMARY_SIZE)#define REFC_BINARY_SUBTAG(0x8 << _TAG_PRIMARY_SIZE) /* BINARY */#define HEAP_BINARY_SUBTAG(0x9 << _TAG_PRIMARY_SIZE) /* BINARY */#define SUB_BINARY_SUBTAG(0xA << _TAG_PRIMARY_SIZE) /* BINARY */#define EXTERNAL_PID_SUBTAG(0xC << _TAG_PRIMARY_SIZE) /* EXTERNAL_PID */#define EXTERNAL_PORT_SUBTAG(0xD << _TAG_PRIMARY_SIZE) /* EXTERNAL_PORT */#define EXTERNAL_REF_SUBTAG(0xE << _TAG_PRIMARY_SIZE) /* EXTERNAL_REF */#define _TAG_HEADER_ARITYVAL(TAG_PRIMARY_HEADER|ARITYVAL_SUBTAG)#define _TAG_HEADER_FUN(TAG_PRIMARY_HEADER|FUN_SUBTAG)#define _TAG_HEADER_POS_BIG(TAG_PRIMARY_HEADER|POS_BIG_SUBTAG)#define _TAG_HEADER_NEG_BIG(TAG_PRIMARY_HEADER|NEG_BIG_SUBTAG)#define _TAG_HEADER_FLOAT(TAG_PRIMARY_HEADER|FLOAT_SUBTAG)#define _TAG_HEADER_EXPORT(TAG_PRIMARY_HEADER|EXPORT_SUBTAG)#define _TAG_HEADER_REF(TAG_PRIMARY_HEADER|REF_SUBTAG)#define _TAG_HEADER_REFC_BIN(TAG_PRIMARY_HEADER|REFC_BINARY_SUBTAG)#define _TAG_HEADER_HEAP_BIN(TAG_PRIMARY_HEADER|HEAP_BINARY_SUBTAG)#define _TAG_HEADER_SUB_BIN(TAG_PRIMARY_HEADER|SUB_BINARY_SUBTAG)#define _TAG_HEADER_EXTERNAL_PID (TAG_PRIMARY_HEADER|EXTERNAL_PID_SUBTAG)#define _TAG_HEADER_EXTERNAL_PORT (TAG_PRIMARY_HEADER|EXTERNAL_PORT_SUBTAG)#define _TAG_HEADER_EXTERNAL_REF (TAG_PRIMARY_HEADER|EXTERNAL_REF_SUBTAG)#define _TAG_HEADER_BIN_MATCHSTATE (TAG_PRIMARY_HEADER|BIN_MATCHSTATE_SUBTAG)#define _TAG_HEADER_MASK0x3F#define _HEADER_SUBTAG_MASK0x3C/* 4 bits for subtag */#define _HEADER_ARITY_OFFS6
从上面代码的第一行可以得知,header值为11000000B的数据是一个tuple,它的长度为11000000B >> 6 == 11B,即是一个长度为3的tuple。
END,此文可作为阅读Erlang C源码的入门知识,对于erl编程和nif的开发及理解也有一定帮助,关于其它数据类型及数据结构的具体实现,有兴趣的同学可以参阅$ERL_TOP/erts/emulator/beam/erl_term.h
Erlang中list和tuple的构建及转换的内部实现
- Erlang数据类型的内部实现
- Erlang数据类型的内部实现
- [Erlang 0032] Erlang Binary的内部实现
- erlang catch的内部实现
- erlang catch的内部实现(初稿)
- Erlang原子(atom)的内部实现及应用
- erlang的简单数据类型
- PHP弱数据类型的内部实现
- Erlang中list和tuple的构建及转换的内部实现
- Erlang如何查看gen_server内部的状态
- erlang 数据类型
- Erlang 数据类型
- MapReduce的Erlang实现
- Erlang虚拟机提供的伪数据类型Record
- erlang 各种数据类型占用的内存大小
- 高手写的erlang的一些内部机制分析
- 查看Erlang运行期内部状态的方法(基于R13B04)
- Erlang二进制创建的内部机制和优化(一)
- 关于direct3d vs2012配置与初始化
- 源码编译安装 MySQL 5.5.x 实践
- 正则表达式
- GPT分区数据格式分析(图已补上)
- 几种任务调度的 Java 实现方法与比较
- Erlang数据类型的内部实现
- Nutch 入门
- Linux 信号signal处理机制
- android弹出窗口的实现(PopupWindow)
- 【2013Esri全球用户大会精彩看点】ArcGIS 10.2移动产品新特性
- JS收集(二)
- 分治算法---乒乓球比赛日程安排
- 导入u8存货档案的另一个办法
- C++字节对齐容易被忽略的两个问题