#
ETermErts做为Erlang的执行时环境最重要的一件事情就是执行BEAM的OP代码。所有的代码都无法排除两个东西函数和数据,换句话说就是BIF和ETerm。
##内存编程模型
不同模型下的指针和int的区别
默认编译器倾向LP32,LP64
Type ILP64 LP64 LLP64
int 64 32 32
long 64 64 32
pointer 64 64 64
long long 64 64 64
在不同的编译器上和不同的内存模型上指针和int的长度是不一定相同。Erts在编译的时候会通过多种探测手段确认编译器所使用的内存模型。
##内存分配模型 在x86或ARM处理器中,基本C数据类型通常并不存储于内存中的随机字节地址。 实际情况是,除char外,所有其他类型都有“对齐要求”: char可起始于任意字节地址,2字节的short必须从偶数字节地址开始,4字节的int或flot必须从能被4整除的地址开始,8字节的long和double必须从能被8整除的地址开始。无论signed(有符号)还是unsigned(无符号)都不受影响。x86和ARM上的基本C类型是“自对齐(self-aligned)”的。 关于指针,无论32位(4字节)还是64位(8字节)也都是自对齐的。简单的说就是内存分配的指针一定是偶数。
##ETerm ETerm是一个和指针的等长的无符号整数。它采用这个整数的二进制最后2位作为标签(tag)。 ETerm的标签主要有(掩码长度为2位,掩码0x3):
0x0 标签头 0x1 链表 0x2 装箱数据 0x3 立即数
标签头的Eterm表示
+---------------------------------------------------------------+
| 标签头 0|0|
+---------------------------------------------------------------+
链表的Eterm表示
+---------------------------------------------------------------+
| 指针二进制表示除去最后2位 0|1|
+---------------------------------------------------------------+
装箱数据的Eterm表示
+---------------------------------------------------------------+
| 指针二进制表示除去最后2位 1|0|
+---------------------------------------------------------------+
立即数表示
+---------------------------------------------------------------+
| 立即数 1|1|
+---------------------------------------------------------------+
这里面链表和装箱数据能正常用Eterm能正常表示和内存分配模型是非常相关的。在32bit上指针是以4字节对齐的,所以要能保证被4整除,所以在二进制表示下任何指针的最后两位一定是00。
###ETerm的立即数 ETerm将立即数又进一步的进行了分类,分为IMMD1和IMMD2这两类
IMMD1的标签有(掩码长度为4位,掩码0xF):
0x3 内部PID 0x7 内部Port 0xF 小整数 0xB IMMD2
PID的Eterm表示
+---------------------------------------------------------------+
| PID 0|0|1|1|
+---------------------------------------------------------------+
Port的Eterm表示
+---------------------------------------------------------------+
| Port 0|1|1|1|
+---------------------------------------------------------------+
小整数的Eterm表示
+---------------------------------------------------------------+
| 小整数 1|1|1|1|
+---------------------------------------------------------------+
IMMD2的Eterm表示
+---------------------------------------------------------------+
| IMMD2 1|0|1|1|
+---------------------------------------------------------------+
IMMD2的标签有(掩码长度为6位,掩码0x3F):
0xB ATOM 0x1B catch 0x3B NIL
ATOM的Eterm表示
+---------------------------------------------------------------+
| ATOM 0|0|1|0|1|1|
+---------------------------------------------------------------+
catch的Eterm表示
+---------------------------------------------------------------+
| catch 0|1|1|0|1|1|
+---------------------------------------------------------------+
NIL的Eterm表示
+---------------------------------------------------------------+
| 小整数 1|1|1|0|1|1|
+---------------------------------------------------------------+
###ETerm的装箱数据和标签头
装箱数据单独使用是没有意义的,需要和标签头一起使用才具有意义。装箱数据的内容是一个指针这样完全和链表是重复的,但是意义不同的是装箱数据的指针指向的是一个标签头。而标签头又分成3部分,大小(24bit),标签(4bit),主标签(2bit,总为00),在一个标签头后面连续跟着n个ETerm。
+---------------------------------------------------------------+
| 指针二进制表示除去最后2位 1|0|
+---------------------------------------------------------------+
|
\|/
+---------------------------------------------------------------+
| n |x|x|x|x|0|0|
+---------------------------------------------------------------+
+---------------------------------------------------------------+
| ETerm 1 |
+---------------------------------------------------------------+
.........
.........
+---------------------------------------------------------------+
| ETerm n |
+---------------------------------------------------------------+
标签头的标签有(掩码长度为6位,掩码0x3F):
0x0 元组 0x4 Binary聚合 0x8 正大整数 0xC 负大整数 0x10 函数或Things 0x18 浮点数 0x1C 导出(export) 0x20 REFC_BINARY(暂时不知道是什么) 0x24 HEAP_BINARY或BINARIES(暂时不知道是什么) 0x28 SUB_BINARY(暂时不知道是什么) 0x2C 没有使用 0x30 外部PID 0x34 外部Port或Things 0x38 外部的Ref 0x3C MAP类型(R17引入)
其中0x10和0x34当标签头大小部分全部为0的时候分别表示函数和外部Port,当不为0的时候表示Erlang的Things。
参考文件 emulator/beam/sys.h emulator/beam/erl_term.h