Lua 虛擬機器的初始化
轉自 https://blog.csdn.net/suhuaiqiang_janlay/article/details/56702381
一、Lua指令碼語言
1. 概述
Lua是一種指令碼程式語言,與一般指令碼語言不同,被稱為是嵌入式的指令碼語言。Lua最著名的應用是在暴雪公司的網路遊戲魔獸世界中。
Lua語言可以獨立進行程式設計,但這不是其主要的使用方式。Lua最典型的用法,是作為一個庫,嵌入到其他大型語言(稱為宿主語言)的應用程式之中,為應用程式提供引數配置或邏輯描述等功能,帶來前所未有的靈活性。
Lua常見的宿主語言有:C/C++、Java、.NET,甚至指令碼語言如PHP、Ruby。
2. Lua與相似解決方案的比較
Lua體積很小,往往使用靜態連結嵌入到程式內部,在釋出應用時不需要附帶任何的執行時支援。
3. 宿主語言中嵌入Lua的工作流程
(1)宿主語言建立Lua直譯器物件
(2)將宿主語言實現的Lua擴充套件,如函式等,註冊到Lua直譯器中,供其使用。
(3)讀入Lua源程式或預先編譯好的Lua程式。
(4)執行讀入的Lua程式。
二、Lua虛擬機器的初始化
Lua工作的核心是Lua虛擬機器,宿主語言在載入和執行Lua指令碼時,做的第一件事情就是建立並初始化Lua虛擬機器。
1. 建立Lua虛擬機器
lua_State *lua_newstate(lua_Alloc f, void *ud) API可以為我們建立一個新的獨立的Lua虛擬機器。
引數指定了虛擬機器中的記憶體分配策略,例如我們已經在自己的程式碼中實現了記憶體池,這時候只需要寫一個符合lua_Alloc原型的介面卡,然後指定為Lua的記憶體分配器就可以了,使得記憶體分配更加靈活。當然,如果不想自定義記憶體分配策略,也可以使用luaL_newstate,這樣Lua會幫你定義預設的記憶體分配策略。
我們可以先來看一下luaL_newstate的原始碼:
- // luaconf.h
- /*
- @@ LUA_EXTRASPACE defines the size of a raw memory area associated with
- ** a Lua state with very fast access.
- ** CHANGE it if you need a different size.
- */
- #define LUA_EXTRASPACE (sizeof(void *))
- // lstate.c
- /*
- ** thread state + extra space
- */
- typedefstruct LX {
- lu_byte extra_[LUA_EXTRASPACE];
- lua_State l;
- } LX;
- /*
- ** Main thread combines a thread state and the global state
- */
- typedefstruct LG {
- LX l;
- global_State g;
- } LG;
- // lstate.c
- LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
- int i;
- lua_State *L;
- global_State *g;
- LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG)));
- if (l == NULL) return NULL;
- L = &l->l.l;
- g = &l->g;
- ......
- return L;
- }
可見,通過luaL_newstate 建立Lua虛擬機器時,第一塊申請的記憶體將用來儲存global_State(全域性狀態機)和lua_State(主執行緒)例項。為了避免記憶體碎片的產生,同時減少記憶體分配和釋放的次數,Lua採用了一個小技巧:利用一個LG結構,把分配lua_State和global_State的行為關聯在一起。這個LG結構是在C檔案內部定義,而不存在公開的H檔案中,僅供該C程式碼檔案使用,因此這種依賴資料結構記憶體佈局的用法負作用不大。
2. 關於global_State和lua_State
在一個獨立的Lua虛擬機器中,global_State是一個全域性的結構, 而lua_State可以有多個。
global_State
global_State結構,我們可以稱之為Lua全域性狀態機。從Lua的使用者角度來看,global_State結構是完全感知不到的:我們無法用Lua公開的API獲取到它的指標、控制代碼或引用,而且實際上我們也並不需要引用到它。但是對於Lua的實現來說,global_State是十分重要的部分。
它管理著Lua中全域性唯一的資訊,主要是以下功能:
(1)記憶體分配策略及其引數
在呼叫lua_newstate的時候配置它們. 也可以通過lua_getallocf和lua_setallocf隨時獲取和修改它
(2)字串的hashtable
全域性的字串雜湊表,即儲存那些短字串,使得整個虛擬機器中短字串只有一份例項。具體參見 Lua字串處理
(3)垃圾回收相關的資訊,記憶體使用統計量
(4)panic, 當無保護呼叫發生時, 會呼叫該函式, 預設是null, 可以通過lua_atpanic配置.(用於異常處理)
(5)登錄檔, 登錄檔是一個全域性唯一的table
(6)記錄lua中元方法名稱 和 基本型別的元表
[注意, lua中table和userdata每個例項可以擁有自己的獨特的元表--記錄在table和userdata的mt欄位, 其他型別是每個型別共享一個元表--就是記錄在這裡].
(7)upvalue連結串列
(8)主lua_State, 一個lua虛擬機器中, 可以有多個lua_State, lua_newstate會創建出一個lua_State(稱為主執行緒), 並邦定到global_state的主lua_State上
lua_State
執行緒,這裡執行緒的概念區別於作業系統的執行緒,實際上也是Lua中定義的一種狀態機。lua_State主要是管理一個lua虛擬機器的執行環境, 一個lua虛擬機器可以有多個執行環境。
(1)要注意的是, 和nil, string, table一樣,lua_State也是lua中的一種基本型別, lua中的表示是TValue {value = lua_State, tt = LUA_TTHREAD}
(2)lua_State的成員和功能
a. 棧的管理, 包括管理整個棧和當前函式使用的棧的情況
b. CallInfo的管理, 包括管理整個CallInfo陣列和當前函式的CallInfo
c. hook相關的, 包括hookmask, hookcount, hook函式等
d. 全域性表l_gt, 注意這個變數的命名, 很好的表現了它其實只是在本lua_State範圍內是全域性唯一的的, 和登錄檔不同, 登錄檔是lua虛擬機器範圍內是全域性唯一的e. gc的一些管理和當前棧中upvalue的管理 f. 錯誤處理的支援(3)從lua_State的成員可以看出來, lua_State最主要的功能就是函式呼叫以及和c的通訊.3. lua_newstate函式的流程
(1)新建一個global_state和一個lua_State
(2)初始化, 包括給g_s建立登錄檔, g_s中各個型別的元表的預設值全部置為0
(3)給l_s建立全域性表, 預分配l_s的CallInfo和stack空間
(4)其中涉及到了記憶體分配統統使用lua_newstate傳進來的記憶體分配器分配