zoukankan      html  css  js  c++  java
  • 关于erlang的binary

    引自:http://cryolite.iteye.com/blog/1547252

    1. binary数据是可以在不同进程间共享的

    当然这些进程都在同一Erlang节点上。

    这与普通term不同,后者作为消息在进程间传递时是要在接收进程中做拷贝的(当然atom数据例外,它们也不会做拷贝)。摘一段原文在这里:
    All data in messages between Erlang processes is copied, with the exception of refc binaries on the same Erlang node.

    bintest是一个察看binary内存地址的小程序(附后),用它验证一下:

    Eshell代码  收藏代码
    1. 1> Bin = <<1,2,3,4,5,6,7,8>>.  
    2. <<1,2,3,4,5,6,7,8>>  
    3. 2> bintest:get_bin_address(B).  
    4. "bin: size=8, ptr=0x7ff2e131bc03"  
    5. 3> P = spawn(fun() -> receive BinMsg -> BinInfo = bintest:get_bin_address(BinMsg), io:format("~s~n", [BinInfo]) end end).  
    6. 4> P ! B.  
    7. bin: size=8, ptr=0x7ff2e131bc03  
    8. <<1,2,3,4,5,6,7,8>>  



    binary在进程间共享带来的问题就是垃圾回收时的麻烦,(可以用引用计数的方式处理?),这是erlang虚拟机实现者的麻烦,有时候也给我们应用开发者带来麻烦。

    2. 模式匹配得到的binary,实际上是匹配目标字节流的一个片断(sub-binary)

    Eshell代码  收藏代码
    1. 1> Bin = <<1,2,3,4,5,6,7,8>>.  
    2. <<1,2,3,4,5,6,7,8>>  
    3. 2> <<_:3/binary, B:3/binary, _/binary>> = Bin.  
    4. <<1,2,3,4,5,6,7,8>>  
    5. 3> B.  
    6. <<4,5,6>>  
    7. 4> binary:referenced_byte_size(Bin).  
    8. 8  
    9. 5> binary:referenced_byte_size(B).  
    10. 8  



    我们可能有一个很大的Bin,然后对它进行匹配查找,找到其中一小段B,实际上这两个变量都指向同一个binary(的不同位置和大小)。可以通 过binary:referenced_byte_size/1函数察看变量引用背后的二进制数据的实际大小,如上面例子所示。所以除非这两个变量都释 放,它们实际引用的那个大binary就不会被垃圾回收。

    binary:referenced_byte_size/1得到binary变量所引用的原始binary数据的大小。所以模式匹配出来的binary变量还是引用到了原始的数据,没有任何拷贝操作。可以进一步看看binary变量引用的字节流地址:

    Eshell代码  收藏代码
    1. 6> bintest:get_bin_address(Bin).  
    2. "bin: size=8, ptr=0x7f9556578c00"  
    3. 7> bintest:get_bin_address(B).  
    4. "bin: size=3, ptr=0x7f9556578c03"  
    5. 8> C = binary:copy(B).  
    6. <<4,5,6>>  
    7. 9> niftest:get_bin_address(C).  
    8. "bin: size=3, ptr=0x7f9556239f78"  



    有可能出现这种情况:在查找完后原来那个大binary不再有用,有用的是那些查找到的结果,这种情况下那个大binary占着大块内存又用不 着,由于有小对象引用都指向它,所以也无法垃圾回收。浪费内存实在可耻。这种情况可以考虑使用binary模块的copy函数,我们用 binary:copy/1把那些找到的每个小binary都拷贝一份出来,这样就不再引用到原来的大binary对象了,没有引用的binary就可以 被垃圾回收了。


    3. binary与字符串

    在erlang中,binary也用做高效的string,但是上述内存共享办法会给nif之间binary字符串的传递带来问题。要处理的字符 串可能来自于一个大binary中的一个片段,我们是无法直接将它作为C语言的字符串处理的,因为C字符串需要一个作为字符串的结尾。好在我们知道这 个字符串的长度。通过复制添的方式可以转换成C能处理的字符串。

    这本质上是两种语言内部对binary字符串的表达方式的不同造成的麻烦:一种以标志字符串,另一种以长度。

    enif_make_string_len可用来处理非的C字符串给elang,只要知道长度就行。这其实是erlang的binary字符串处理方式了,这样没有结尾的binary字符串也能当成成字符串给erlang用了。

    而enif_make_sub_binary是用来在C中模仿erlang的字符串binary操作的。

    enif_get_string将erlang字符串(实际上是list,跟binary无关了)转换成结尾的C字符串。

    而enif_inspect_iolist_as_binary,则是将一个iolist的erlang字符串转换成一个erlang的binary字符串
    enif_inspect_iolist_as_binary使用的陷阱是要注意binary字符串是以长度而不是标识的。在C中使用使要做转换:

    C代码  收藏代码
    1. ErlNifBinary tilefilenameBin;  
    2. if (!enif_inspect_iolist_as_binary(env, argv[1], &tilefilenameBin) || (tilefilenameBin.size >= 64)) {  
    3.     return enif_make_badarg(env);  
    4. }  
    5.   
    6. char tilefilename[64] = "";  
    7. memcpy(tilefilename, tilefilenameBin.data, tilefilenameBin.size);  





    bintest是一个察看binary内存地址的小程序,利用了nif的ErlNifBinary结构

    C代码  收藏代码
    1. typedef struct {  
    2.     unsigned size;  
    3.     unsigned char* data;  
    4. } ErlNifBinary;  



    Bintest.erl代码  收藏代码
    1. -module(bintest).  
    2. -export([get_bin_address/1]).  
    3.   
    4. -on_load(init/0).  
    5.   
    6. init() ->  
    7.     erlang:load_nif("./bintest", 0).   
    8.   
    9. get_bin_address(_Bin) ->  
    10.     erlang:error({"NIF not implemented in nif_test at line", ?LINE}).  



    对应的nif C:

    Bintest.c代码  收藏代码
    1. #include "erl_nif.h"  
    2. #include <stdio.h>  
    3.   
    4. static ERL_NIF_TERM get_bin_address(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {  
    5.     ErlNifBinary bin;  
    6.     enif_inspect_binary(env, argv[0], &bin);  
    7.     char buf[256];  
    8.     sprintf(buf, "bin: size=%zu, ptr=%p", bin.size, bin.data);  
    9.     return enif_make_string(env, buf, ERL_NIF_LATIN1);  
    10. }  
    11. static ErlNifFunc nif_funcs[] =  
    12. {  
    13.     {"get_bin_address", 1, get_bin_address}  
    14. };  
    15.   
    16. ERL_NIF_INIT(bintest,nif_funcs,NULL,NULL,NULL,NULL);  



    linux下的编译命令:

    Shell代码  收藏代码
      1. gcc -std=c99 -fPIC -shared -o bintest.so bintest.c -I/usr/local/lib/erlang/usr/include 
  • 相关阅读:
    mysql BETWEEN操作符 语法
    mysql IN操作符 语法
    mysql LIKE通配符 语法
    mysql TOP语句 语法
    mysql DELETE语句 语法
    mysql Update语句 语法
    mysql INSERT语句 语法
    mysql ORDER BY语句 语法
    mysql OR运算符 语法
    mysql AND运算符 语法
  • 原文地址:https://www.cnblogs.com/yanwei-wang/p/4382999.html
Copyright © 2011-2022 走看看