erlang判断模块导出函数问题

erlang本身提供一个接口,可以用来检查模块是否有导出函数,这个接口是erlang:function_exported/3,但是很多时候这个接口无法正常使用。

下面重现一下这个问题:

1> erlang:function_exported(crypto,start,0).
false
2> crypto:start().
ok
3> erlang:function_exported(crypto,start,0).
true

注意:例子中并不是说一定要crypto:start()才能使用这个函数,只是说这个函数确实存在。

现在,来看下erlang对这个接口的说明:

Returns true if the module Module is loaded and contains an exported function Function/Arity; otherwise false.

Returns false for any BIF (functions implemented in C rather than in Erlang).

换句话说,如果一个模块还没有被加载,就无法使用erlang:function_exported/3函数。很多模块在erlang启动时都没有加载到系统,都是在使用到的时候才加载,所以这个检查导出函数的接口可能会出现错误的结果。

如果要判断模块是否有导出函数,那么,我们可以像下面这么写,就可以正常使用了。

-module(test).
-compile(export_all).

function_exported(Module, Function, Arity) ->
	case erlang:module_loaded(Module) of
		true ->
			next;
		_ ->
			code:load_file(Module)
	end,
	erlang:function_exported(Module, Function, Arity).

另外,如果模块被改成bif也无法判断了。不过这倒不用太过担心,我们自己写的函数都不会是bif

最后,讨论下erlang:function_exported/3为何只能检查已加载过的模块?

为了实现热更新,新旧代码替换,erlang维护了一个全局哈希表,用于描述模块导出函数信息。那么,只要加载过的模块就会被记录信息到这个哈希表,保存这些信息也要耗费一定的内存开销,而且,哈希算法本身存在冲突的可能性,元素越多,发生冲突的可能性越大,为了解决冲突,还会引入了bucket(哈希桶)或者链表。这样,如果要保证键值分散,就会浪费很多空间,不然又会影响查找效率。

所以,erlang只记录了加载过的模块信息,其他等到使用到的时候再加载。

erlang:function_exported/3是一个bif函数,实现函数可以在erts\emulator\beam\bif.c找到:

BIF_RETTYPE function_exported_3(BIF_ALIST_3)
{
    if (is_not_atom(BIF_ARG_1) || is_not_atom(BIF_ARG_2) ||  is_not_small(BIF_ARG_3)) {
        BIF_ERROR(BIF_P, BADARG);
    }
    if (erts_find_function(BIF_ARG_1, BIF_ARG_2, signed_val(BIF_ARG_3),
            erts_active_code_ix()) == NULL) {
        BIF_RET(am_false);
    }
    BIF_RET(am_true);
}

erts_find_function实现在erts\emulator\beam\export.c中:

/*
 * Find the export entry for a loaded function.
 * Returns a NULL pointer if the given function is not loaded, or
 * a pointer to the export entry.
 *
 * Note: This function never returns export entries for BIFs
 * or functions which are not yet loaded.  This makes it suitable
 * for use by the erlang:function_exported/3 BIF or whenever you
 * cannot depend on the error_handler.
 */

Export* erts_find_function(Eterm m, Eterm f, unsigned int a, ErtsCodeIndex code_ix)
{
    struct export_templ templ;
    struct export_entry* ee;

    ee = hash_get(&export_tables[code_ix].htable, init_template(&templ, m, f, a));
    if (ee == NULL ||	(ee->ep->addressv[code_ix] == ee->ep->code+3 &&
	 ee->ep->code[3] != (BeamInstr) BeamOp(op_i_generic_breakpoint))) {
        return NULL;
    }
    return ee->ep;
}

这里,export_tables是一个全局变量,还是在export.c,其中,code_ix被用来控制代码版本,有时间的话再讨论erlang热更新机制

static IndexTable export_tables[ERTS_NUM_CODE_IX];  /* Active not locked */

所以,erlang:function_exported/3只是去查找导出函数的哈希表,找到返回true,没有就false

参考:http://blog.csdn.net/mycwq/article/details/40663737

时间: 2024-10-16 01:55:53

erlang判断模块导出函数问题的相关文章

关于DLL模块导出函数

当然以前我知道有一个.def文件的,里面写的都是需要导出的函数,以为与__declspec(dllexport)作用是一样的.但是今天看公司项目源码的时候才知道,它们两个导出方法是有一定的区别的,编译导出来的符号名称是略有不同的,如果供其他语言调用dephin,等,就麻烦些. references: http://blog.csdn.net/zhuxiaoyang2000/article/details/6387247 http://baike.baidu.com/view/2779203.ht

driver: linux2.6 内核模块导出函数实例(EXPORT_SYMBOL) 【转】

转自:http://blog.chinaunix.net/uid-23381466-id-3837650.html 内核版本:2.6.38-11-generic 内核自己都大量利用内核符号表导出函数,那么应该导出呢,ldd3上面说只需要EXPORT_SYMBOL一类的宏导出即可,结果试了很久都不行,最后查看文档,算是明白一点了. 对于导出符号表,内核文档给出了三种解决方案,见尾部,现在忽略. 现在有两个模块,a模块导出函数myprint,b模块使用该函数,想象一下如果a模块 EXPORT_SYM

C#调用C++ dll导出函数提示找不到指定模块

在X64系统上,用VS2013编写了一个C++动态链接库,里面提供了一个导出函数SGFYS. 编译为DLL之后,我们用C#对其动态链接库进行调用,调用代码如下: 此时会被提示,试图加载不正确的格式.我们对该.NET项目属性进行配置,右键该项目--属性--生成--目标平台修改为(x86) 之后再次调用,已经可以被正确调用. 此时我们将该程序放到XP下运行,会报错“找不到指定模块”. 我们用 Dependency walker加载该DLL,发现缺少依赖MSVCR120D.DLL 导致上述问题的原因是

DLL中__declspec(dllexport)和.def(模块定义文件)定义导出函数的一点区别

原想是不在DLL中使用.def文件的,直接在需要导出的函数前加__declspec(dllexport)修饰.但在是要导出STDAPI __declspec(dllexport) DllGetClassObject的时候,就提示"warning C4518: “__declspec(dllexport ) ” : 此处遇到意外的存储类或类型说明符:被忽略"(我用的是vc.net 2003). STDAPI这个宏扩展出来是extern "C" HRESULT __st

第二节 模块与函数(上)

模块 模块是erlang的基本代码单元.模块保存在扩展名为.erl的文件里,而且必须先编译才能运行模块里面的代码.编译后的模块以.beam作为扩展名. 我们创建一个geometry.erl的文件 1 -module (geometry). 2 -export ([area/1]). 3 4 area({rectangle,Width,Height}) -> Width * Height; 5 area({square, Side}) -> Side * Side. 然后在shell中编译这个模

再说说erlang的模块热更新

前面的文章有讲过erlang热更新,只是大概介绍,现在再深入一点讲erlang的模块热更新.erlang的热更新是模块级别的,就是一个模块一个模块更新的. 热更新是什么,就是在不停止系统的情况下对运行的代码进行替换. 如何进行热更新? c(Mod) -> compile:file(Mod), code:purge(Mod), code:load_file(Mod). 以上就是shell c(Mod) 的主要代码,3个步骤:编译新的代码,清除旧代码,加载新代码 同样, l(Mod) 的主要代码如下

DLL的导出函数重定向机制

曾经,调试时跟进HeapAlloc,结果发现直接进入到ntdll的RtlAllocateHeap中,感到很有趣,就使用Dependency Walker查看kernel32.dll的导出函数,结果发现HeapAlloc的地址直接显示的就是NTDLL.RtlAllocateHeap.于是反汇编查看kernel32.dll文件,发现本以为是汇编代码的HeapAlloc的函数体就是字符串NTDLL.RtlAllocateHeap. 想想以前也曾经自己实现过GetProcAddress,就是直接从导出表

C++ DLL导出函数的两种方法(导出序号那种方法,别人看不到函数名)

第一种就直接导出函数名如下代码: #ifdef__cplusplus #define TEXPORT extern "c" _declspec(dllexport) #dlse #define TEXPORT _declspec(dllexport) TEXPORT BOOL FUN();//这就是要导出函数 这种方法查看DLL时能看到函数名. 第二种是就导出序号如下代码: bool _stdcall fun(); 在工程右键添加新项目点模块定义文件.DEF, 在在DEF文件里写 LI

(备忘)vs2010编写动态链接库时导出函数的函数名问题及加载方式

在vs2010中使用.def文件导出函数时,仅仅添加.def文件是不够的,还要在 项目属性 -> 链接器 -> 输入 -> 模块定义文件 中添加自定义的.def文件名. (前提:导入导出都在头文件和源文件中定义好了) ##:静态加载动态链接库 将链接库的 头文件..lib文件 和 .dll 文件拷贝到工程目录下 然后#include 头文件,#pragma comment(lib,"**.lib") 最后直接在需要使用dll函数的地方使用函数就行 ##:动态加载动态链