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

来源:互联网 发布:淘宝宝贝标题关键词 编辑:程序博客网 时间:2024/05/16 06:39
我们先来重温一下《Erlang数据类型的内部实现》一文。 

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

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

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



/* * HEADER representation: * *aaaaaaaaaaaaaaaaaaaaaaaaaatttt00arity:26, tag:4 * * HEADER tags: * *  0001    BINARY_AGGREGATE        | *1000REFC_BINARY|    | *1001HEAP_BINARY| BINARIES| *1010SUB_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 SUBTAGrefc binaries1000REFC_BINARYREFC_BINARY_SUBTAGheap binaries1001HEAP_BINARYHEAP_BINARY_SUBTAGsub binary1010SUB_BINARYSUB_BINARY_SUBTAGmatch 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二进制数据中某部分的描述,或者说是引用。