Erlang Erlang 的 NIF 资源释放

DavidAlphaFox · 发布于 2017年09月23日 · 259 次阅读
84794b

为什么要用NIF

Erlang在数学运算和字符串处理方面是比较弱,很多时候需要借助C的nif来加速。不过我们今天不讨论这个,因为今天这事情的起因是我要将Redis的AOF嵌入到Erlang程序中。

大家都知道Redis的AOF是要打开一个文件的,但是如果我们的Erlang进程因为意外退出,NIF打开的文件该怎么处理呢?因为NIF并不像Erlang的Driver那样可以监测Erlang进程的死活。

解决方法

这个时候,我想起了一个伟大的工程eleveldb。leveldb也是需要打开句柄,并且相应的进程也使用这个句柄。所以就去翻看了eleveldb的代码。发现几个非常重要的函数

ErlNifResourceType *enif_open_resource_type(ErlNifEnv* env, const char* module_str, const char* name, ErlNifResourceDtor* dtor, ErlNifResourceFlags flags, ErlNifResourceFlags* tried);

void *enif_alloc_resource(ErlNifResourceType* type, unsigned size);

ERL_NIF_TERM enif_make_resource(ErlNifEnv* env, void* obj);

void enif_release_resource(void* obj);

这几个函数是怎么解决资源释放问题的呢?

我们可以看到eleveldb在on_load的时候,使用enif_open_resource_type创建了一个资源类型。之后在每次请求打开DB的时候,用enif_alloc_resource分配出一个资源,然后用enif_make_resource和enif_release_resource将资源的控制权交给了调用的进程

Erts内部实现

根据阅读Erts中NIF相关的代码,可以看到enif_alloc_resource在分配资源的时候,分配的资源引用计数为1,并且该资源不属于任何一个Erlang的进程。

当我们调用enif_make_resource的时候,相当于在调用者的堆栈上分配了一个指向资源的指针,并将资源的引用计数加1。

接着我们再使用enif_release_resource将资源的引用计数减少1,这样就相当将资源的控制权交给了调用的Erlang进程。

那么资源是怎么安全释放的呢?这个时候我们可以看到资源的引用计数为1,并且这个引用者是Erlang进程堆。我们都知道Erlang进程崩溃后,Erts会清理Erlang进程堆和栈,释放资源。

那么当Erlang进程崩溃后,就会调用enif_open_resource_type被调用的时候所传入的析构函数,而这个析构函数的参数就是前面所分配的资源。

暂无回复。
需要 登录/注册 后方可回复, 如果你还没有账号请点击这里 注册