查看源代码 编译和代码加载

代码如何编译和加载不是一个语言问题,而是依赖于系统的。本节描述 Erlang/OTP 中的编译和代码加载,并引用文档的相关部分。

编译

Erlang 程序必须编译为目标代码。编译器可以生成一个包含目标代码的新文件。当前运行目标代码的抽象机器称为 BEAM,因此目标文件的后缀为 .beam。编译器也可以生成可以直接加载的二进制文件。

编译器位于 Compiler 中的 compile 模块中。

compile:file(Module)
compile:file(Module, Options)

Erlang shell 理解命令 c(Module),它既编译又加载 Module

还有一个模块 make,它提供了一组类似于 UNIX 类型 Make 函数的功能,请参阅 Tools 中的 make 模块。

也可以使用 ERTS 中的 erl 可执行文件从操作系统提示符访问编译器。

% erl -compile Module1...ModuleN
% erl -make

erlc 程序提供了一种从操作系统 shell 编译模块的方法,请参阅 ERTS 中的 erlc 可执行文件。它理解许多标志,这些标志可以用于定义宏、为包含文件添加搜索路径等等。

% erlc <flags> File1.erl...FileN.erl

代码加载

目标代码必须加载到 Erlang 运行时系统中。这由代码服务器处理,请参阅 Kernel 中的 code 模块。

代码服务器根据代码加载策略加载代码,该策略可以是交互式(默认)或嵌入式。在交互模式下,在代码路径中搜索代码,并在首次引用时加载。在嵌入模式下,代码在启动时根据引导脚本加载。这在 系统原理 中进行了描述。

代码替换

Erlang 支持在运行系统中更改代码。代码替换是在模块级别完成的。

一个模块的代码在系统中可以存在两种变体:当前。当一个模块第一次加载到系统中时,代码变为“当前”。如果随后加载了该模块的新实例,则先前实例的代码变为“旧”,新实例变为“当前”。

旧代码和当前代码都有效,并且可以并发评估。完全限定的函数调用始终引用当前代码。由于进程在旧代码中徘徊,因此旧代码仍然可以被评估。

如果加载了该模块的第三个实例,则代码服务器会删除(清除)旧代码,并且其中徘徊的任何进程都会终止。然后,第三个实例变为“当前”,而之前当前的代码变为“旧”。

要从旧代码更改为当前代码,进程必须进行完全限定的函数调用。

示例

-module(m).
-export([loop/0]).

loop() ->
    receive
        code_switch ->
            m:loop();
        Msg ->
            ...
            loop()
    end.

要使进程更改代码,请向其发送消息 code_switch。然后,该进程对 m:loop() 进行完全限定的调用,并更改为当前代码。请注意,必须导出 m:loop/0

为了使 fun 的代码替换工作,请使用语法 fun Module:FunctionName/Arity

加载模块时运行函数

-on_load() 指令指定一个在模块加载时自动运行的函数。

其语法如下

-on_load(Name/0).

没有必要导出该函数。它在一个新生成的进程中被调用(该进程在函数返回后立即终止)。

如果模块要成为该模块新的当前代码并可调用,则该函数必须返回 ok

返回任何其他值或生成异常会导致新代码被卸载。如果返回值不是原子,则会向错误记录器发送警告错误报告。

如果模块已经存在当前代码,则该代码将保持当前状态,并且可以调用,直到 on_load 函数返回。如果 on_load 函数失败,则当前代码(如果有)将保持当前状态。如果模块没有当前代码,则任何在 on_load 函数完成之前对该模块进行外部调用的进程都将被挂起,直到 on_load 函数完成。

更改

在 Erlang/OTP 19 之前,如果 on_load 函数失败,则任何先前的当前代码都将变为旧代码,实际上使系统没有任何工作和可访问的模块实例。

在嵌入模式下,首先加载所有模块。然后调用所有 on_load 函数。除非所有 on_load 函数都返回 ok,否则系统将终止。

示例

-module(m).
-on_load(load_my_nifs/0).

load_my_nifs() ->
    NifPath = ...,    %Set up the path to the NIF library.
    Info = ...,       %Initialize the Info term
    erlang:load_nif(NifPath, Info).

如果对 erlang:load_nif/2 的调用失败,则会卸载该模块,并向错误加载器发送警告报告。