Erlang Binary的内部结构和分类介绍

我们先来重温一下《 Erlang数据类型的内部实现》一文。

Erlang中封装binary数据指针时,尾部都会被贴上boxed标签(即最后2位为10B),

指针所指向的内容首4字节(确切的说是sizeof(Eterm)字节)是一个header,

从《Erlang数据类型的内部实现》一文中关于header的一段注释中可以看到:



/*
 * HEADER representation:
 *
 *	aaaaaaaaaaaaaaaaaaaaaaaaaatttt00	arity:26, tag:4
 *
 * HEADER tags:
 *
 *  0001    BINARY_AGGREGATE        |
 *	1000	REFC_BINARY	|		    |
 *	1001	HEAP_BINARY	| BINARIES	|
 *	1010	SUB_BINARY	|		    |
 *
 */


#define BIN_MATCHSTATE_SUBTAG   (0x1 << _TAG_PRIMARY_SIZE)
#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 */  


从上面header可得知二进制数据在底层可以分为四类。

表1:Erlang Binary的四种类型

Type NameHeader Tag (B)Tag Name#define SUBTAG
refc binaries1000REFC_BINARYREFC_BINARY_SUBTAG
heap binaries1001HEAP_BINARYHEAP_BINARY_SUBTAG
sub binary1010SUB_BINARYSUB_BINARY_SUBTAG
match context0001BINARY_AGGREGATEBIN_MATCHSTATE_SUBTAG


refc binaries (reference-counted binaries) 引用计数二进制



引用计数二进制可由以下两个结构体组合起来描述:

typedef struct proc_bin {
    Eterm thing_word;		/* Subtag REFC_BINARY_SUBTAG. */
    Uint size;			/* Binary size in bytes. */
    struct erl_off_heap_header *next;
    Binary *val;		/* 指向Binary结构 */
    byte *bytes;		/* Pointer to the actual data bytes. */
    Uint flags;			/* Flag word. */
} ProcBin;


typedef struct binary {
    UWord flags;							
    erts_refc_t refc;   /* 引用计数 */
    ERTS_BINARY_STRUCT_ALIGNMENT
    SWord orig_size;
    char orig_bytes[1]; /* to be continued */
    // 作为二进制数据的容器1,这里会被动态扩展后继的内存空间
} Binary;


引用计数二进制包含两部分:
  1. ProcBin,它被保存在进程堆里,ProcBin->val指向Binary。所有的ProcBin都是同一个链表的一部分,GC可以跟踪它们,当ProcBin移除时Binary->refc引用计数会减一。
  2. Binary,它徘徊在所有的进程堆之外,可被N个ProcBin引用,Binary->refc保存了引用计数,当引用计数为0时它将被抛弃。

heap binary 堆二进制

typedef struct erl_heap_bin {
    Eterm thing_word;		/* Subtag HEAP_BINARY_SUBTAG. */
    Uint size;			/* Binary size in bytes. */
    Eterm data[1];		/* The data in the binary. */
    // 作为二进制数据的容器2,这里会被动态扩展后继的内存空间
} ErlHeapBin;


堆二进制是最大为64字节的小二进制,它被直接存储在进程堆里。对于堆二进制,当GC或被作为消息发送时,它会被复制,在GC方面不需要做特别的处理。


sub binary 子二进制

顾名思义,它就是用来描述现有二制进的某一部分的。


typedef struct erl_sub_bin {
    Eterm thing_word;		/* Subtag SUB_BINARY_SUBTAG. */
    Uint size;			/* Binary size in bytes. */
    Uint offs;			/* Offset into original binary. */
    byte bitsize; 
    byte bitoffs; 
    byte is_writable;		/* The underlying binary is writable */
    Eterm orig;			/* Original binary (REFC or HEAP binary). */
} ErlSubBin;


子二进制是在使用函数split_binary/2或进行二进制匹配时产生的。它可以引用现有的refc binary或heap binary的一部分。充分使用子二制这一特性,可以提升二进制匹配操作的性能,因为匹配结果不用从原二进制中拷贝。


match context

对于这个官方名字,有点纠结,想不到合适的中文名来描述它。在源码中没有看到这个名字的出现,在表1中可以看到与此名字对应的其它描述。


// This structure represents a binary to be matched.
typedef struct erl_bin_match_buffer {
    Eterm orig;			/* 指向原始二进制数据包(ProcBin或ErlHeapBin) */
    byte* base;			/* 直接指向二进制数据(ProcBin->bytes或ErlHeapBin->data) */
    Uint offset;		/* Offset in bits. */
    size_t size;		/* Size of binary in bits. */
} ErlBinMatchBuffer;


typedef struct erl_bin_match_struct{
    // header(thing_word)的求值过程为:
    // (size << 6) | (TAG_PRIMARY_HEADER | BIN_MATCHSTATE_SUBTAG)
    Eterm thing_word;
    ErlBinMatchBuffer mb;		/* Present match buffer */
    Eterm save_offset[1];		/* Saved offsets */
} ErlBinMatchState;


match context和子二进制类似。为了匹配操作可以更快速,对比于子二进制,它做了一些优化,增加了直接指向二进制数据的指针(ErlBinMatchState->mb->base),当从二进制数据中匹配出字段值后直接移动指针即可。


小结

refc binaries和heap binaries是装二进制数据的容器。
sub binary和match context是对refc或heap二进制数据中某部分的描述,或者说是引用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值