檢視原始碼 編譯與程式碼載入
程式碼如何編譯和載入並非語言本身的問題,而是與系統相關。本節描述 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 執行檔。它可以理解許多 flag,可用於定義巨集、為 include 檔案新增搜尋路徑等等。
% erlc <flags> File1.erl...FileN.erl
程式碼載入
目的碼必須載入到 Erlang 執行期系統中。這由程式碼伺服器處理,請參閱 Kernel 應用程式中的 code
模組。
程式碼伺服器根據程式碼載入策略載入程式碼,該策略可以是互動式(預設)或嵌入式。在互動模式中,程式碼會在程式碼路徑中搜尋,並在首次被引用時載入。在嵌入式模式中,程式碼會在啟動時根據啟動腳本載入。這在 系統原理 中有描述。
程式碼替換
Erlang 支援在執行中的系統中變更程式碼。程式碼替換在模組層級完成。
一個模組的程式碼在系統中可以存在兩種變體:current 和 old。當一個模組第一次載入系統時,該程式碼會變成 'current'。如果之後載入該模組的新執行個體,則前一個執行個體的程式碼會變成 'old',而新的執行個體會變成 'current'。
old 和 current 程式碼都有效,並且可以同時評估。完全限定的函數呼叫總是參考 current 程式碼。由於處理程序可能停留在 old 程式碼中,因此 old 程式碼仍然可以被評估。
如果載入模組的第三個執行個體,程式碼伺服器會移除 (清除) old 程式碼,並且任何停留在其中的處理程序都會終止。然後第三個執行個體會變成 'current',而先前 current 的程式碼會變成 'old'。
若要從 old 程式碼變更為 current 程式碼,處理程序必須進行完全限定的函數呼叫。
範例
-module(m).
-export([loop/0]).
loop() ->
receive
code_switch ->
m:loop();
Msg ->
...
loop()
end.
若要使處理程序變更程式碼,請將訊息 code_switch
傳送給它。然後處理程序會對 m:loop()
進行完全限定的呼叫,並變更為 current 程式碼。請注意,m:loop/0
必須被匯出。
若要使 fun 的程式碼替換生效,請使用語法 fun Module:FunctionName/Arity
。
模組載入時執行函數
-on_load()
指令會指定在模組載入時自動執行的函數。
其語法如下
-on_load(Name/0).
不需要匯出該函數。它會在新產生的處理程序中呼叫(該處理程序會在函數返回後立即終止)。
如果模組要成為模組新的 current 程式碼並可被呼叫,則函數必須返回 ok
。
返回任何其他值或產生例外狀況都會導致新程式碼被卸載。如果傳回值不是原子,則會將警告錯誤報告傳送到錯誤記錄器。
如果該模組已存在 current 程式碼,則該程式碼將保持 current,並且可以呼叫,直到 on_load
函數返回。如果 on_load
函數失敗,則 current 程式碼(如果有的話)將保持 current。如果模組沒有 current 程式碼,則任何在 on_load
函數完成之前對模組進行外部呼叫的處理程序都將被暫停,直到 on_load
函數完成。
變更
在 Erlang/OTP 19 之前,如果
on_load
函數失敗,任何先前的 current 程式碼都會變成 old,這實際上會使系統沒有模組的任何可用的且可存取的執行個體。
在嵌入式模式中,首先會載入所有模組。然後會呼叫所有 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
失敗,則會卸載模組,並將警告報告傳送到錯誤載入器。