我打算使用NIFs为我计划在Erlang中编码的应用程序操作二进制文件。下面给出了指向 NIF 的 cpp 文件和 erl 文件的要点链接。
[Erl Gist 链接] https://gist.github.com/abhijitiitr/3a5bc97184d6dd32f97b
[C++要点链接] https://gist.github.com/abhijitiitr/24d2b780f2cdacebfb07
基本上我正在尝试做一个简单的测试。在 NIF 调用之间共享二进制文件,并通过连续的 NIF 调用成功操作它们。
如果您通过以下方式测试erlang REPL中的代码
c(binary_test).
Ref=binary_test:open(<<1>>).
binary_test:increment(Ref,<<3>>).
二进制文件在 NIF 调用之间存储更改。第三个命令的 REPL 输出为
1
3
60
60
<<"?">>
我在初始化阶段通过了<<1>>
。为什么改为<<60>>
?我无法弄清楚这里发生了什么。有人可以指出错误吗?
C++编译指令
clang++ -std=c++11 -stdlib=libc++ -undefined dynamic_lookup -O3 -dynamiclib binary_test.cpp -o binary_test.so -I /usr/local/Cellar/erlang/17.0/lib/erlang/erts-6.0/include/
在我的 Mac 上。
我还想询问有关在 NIF 中操作共享资源的并发进程。这是否可能,或者有一条规则,即必须在单个 Erlang 进程中访问 NIF。
您遇到了问题,因为您非法访问内存。在您的BinaryStore
构造函数中,您尝试从传递给binary_test:open/1
的参数列表中保存二进制文件,但这不起作用,因为一旦 NIF 调用完成这些参数,这些参数就会被释放。您需要保存参数的副本以供以后使用。为此,请先向BinaryStore
类添加新成员:
ErlNifEnv* term_env;
接下来,修改构造函数以分配term_env
,然后使用它来复制传入的术语:
BinaryStore(ERL_NIF_TERM binary)
{
term_env = enif_alloc_env();
binary_term = enif_make_copy(term_env, binary);
}
这会在term_env
环境中分配binary_term
,然后将传入的术语复制到其中。您还需要一个析构函数来释放term_env
:
~BinaryStore()
{
enif_free_env(term_env);
}
最后,在increment_binary
函数中检查binary_term
时,您需要传递term_env
而不是env
:
nifpp::get_throws(term_env, binary_term, ibin);
通过这些修改,我从运行代码中得到以下结果:
1> Ref=binary_test:open(<<1>>).
Reading symbols for shared libraries . done
<<>>
2> binary_test:increment(Ref,<<3>>).
1
3
1
1
<<4>>
(顺便说一下,从 Erlang 模拟器内部打印时,您应该使用"rn"
行结尾,而不仅仅是"n"
,以便换行符始终返回到最左侧的列。
仍然有一个问题,那就是您泄漏了分配给new_bin2
的内存。
我对学习 NIF 细节的建议是一开始避免使用 nifpp
等包,这样您就可以了解 NIF API 以及有关内存所有权、资源分配和释放以及参数转换的所有详细信息。一旦你理解了它们,使用像nifpp
这样的包就会变得更容易和更有成效。
ERL_NIF_TERM
s必须与ErlNifEnv
相关联,并且传递给NIF函数的环境仅在该函数调用期间有效。 当您将术语存储到 BinaryStore
对象中,然后从另一个 nif 调用中使用它时,您违反了此规则。 您的选择:
-
为您的二进制存储创建一个新的 ErlNifEnv,并将术语从 nif 调用复制到这个新环境中。
-
使用C++数据结构(如
std::vector<unsigned char>
(来存储二进制数据。 我认为这对您的情况来说会更简单。