关于erlang的binary

来源:互联网 发布:美空网怎么约 知乎 编辑:程序博客网 时间:2024/06/04 19:21

转载:http://cryolite.iteye.com/blog/1547252

(牛人的blog)


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字符串需要一个\0作为字符串的结尾。好在我们知道这个字符串的长度。通过复制添\0的方式可以转换成C能处理的字符串。 

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

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

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

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

而enif_inspect_iolist_as_binary,则是将一个iolist的erlang字符串转换成一个erlang的binary字符串 
enif_inspect_iolist_as_binary使用的陷阱是要注意binary字符串是以长度而不是\0标识的。在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  


原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 智能表采集失败怎么办 电脑没有蓝牙功能怎么办 判决后无力偿还怎么办 dz47-63c63跳闸怎么办 租房合同丢了怎么办 北京土地承包合同丢失怎么办 租赁合同丢失了怎么办 房屋租赁合同丢失怎么办 学校没发学生证怎么办 学校银行卡丢了怎么办 报税名字忘了怎么办 地税零申报漏报怎么办 欠中联重科施工电梯钱怎么办 大型船舶起锚正横后怎么办 老师不会教孩子怎么办 个体工商营业证怎么办 广州市住房公积怎么办 苹果账号禁用了怎么办 换单位后公积金怎么办 青岛不发工资怎么办 上北影不会唱歌怎么办 黑驾校不退钱怎么办 驾驶证被扣半年怎么办 买个二手车落户怎么办 名下有公司贷款怎么办 厦门车辆过户路桥费怎么办 香港oocl被收购怎么办 船公司倒闭货物怎么办 没有找到crc值怎么办 hdl-c偏低该怎么办 oocl的海员未来怎么办 加固插件版本低怎么办 干燥剂用手摸了怎么办 玩游戏出手汗怎么办 玩游戏手心出汗怎么办 儿童误服干燥剂怎么办 幼儿故事保护剂怎么办 幼儿误食颗粒干燥剂怎么办 干燥剂遇水碰到怎么办 宝宝吃了花露水怎么办 把干燥剂吃了怎么办