作者:羅日健
前面各種Lua的數據類型基本都說得差不多了,剩下最后一個數據類型:lua_State,我們可以認為是”腳本上下文”,主要是包括當前腳本環境的運行狀態信息,還會有gc相關的信息。
Lua這門語言考慮了多線程的情況,在腳本空間中能夠開多個線程相關腳本上下文,而大家會共用一個全局腳本狀態數據,如下:
全局數據global_state的數據結構如下:
global_state主要是用於GC的數據鏈表,下面簡要說明幾個:
- stringtable strt:這個是在TString那章說到的全局字符串哈希表
- TValue lregistry:對應LUAREGISTRYINDEX的全局table.
- TString *tmname[TM_N]:元方法的名稱字符串。
- Table *mt[NUM_TAGS]:基本類型的元表,這是Lua5.0的特性。
mt成員在作者介紹文章中說到:
在上面代碼中,我們看到a支持一個tostring的方法,a是數值類型,我們可以為數值類型添加任意的方法。Lua文章中說到一個用途,就是對於unicode和gbk的字符串的len方法能自己實現。
其它成員就不一一介紹了,下面來介紹與線程相關的腳本上下文lua_State:
我們看到,luaState也帶有CommonHeader頭,在第一章中也提到了GCObject中有luaState th這個成員,由此可見lua_State也會是被回收的對象之一。
考慮回一個線程中的腳本上下文,我們再來逐個分析每個成員:
- lu_byte status:線程腳本的狀態,線程可選狀態如下:
- StkId top:指向當前線程棧的棧頂指針,typedef TValue *StkId
- StkId base:指向當前函數運行的相對基位置,具體可參考第四章的閉包
- globalState *lG:指向全局狀態的指針
- CallInfo *ci:當前線程運行的函數調用信息
- const Instruction *savedpc:函數調用前,記錄上一個函數的pc位置
- StkId stack_last:棧的實際最后一個位置(棧的長度是動態增長的)
- StkId stack:棧底
- CallInfo *end_ci:指向函數調用棧的棧頂
- CallInfo *base_ci:指向函數調用棧的棧底
- int stacksize:棧的大小
- int size_ci:函數調用棧的大小
- unsigned short nCcalls:當前C函數的調用的深度
- unsigned short baseCcalls:用於記錄每個線程狀態的C函數調用深度的輔助成員
- lu_byte hookmask:支持哪些hook能力,有下列可選的
- lu_byte allowhook:是否允許hook
- int basehookcount:用戶設置的執行指令數(LUA_MASKCOUNT下有效)
- int hookcount:運行時,跑了多少條指令(LUA_MASKCOUNT下有效)
- lua_Hook:用戶注冊的hook回調函數
- TValue l_gt:當前線程的全局的環境表
- TValue env:當前運行的環境表
- GCObject *openupval、gclist:用於gc,詳細將會在GC一章細說
- struct lua_longjmp *errorJmp:發生錯誤的長跳轉位置,用於記錄當函數發生錯誤時跳轉出去的位置。
本系列總結:
整個系列文章回答了我們對Lua中最基本的一個問題:“一個Lua變量究竟是什么?”。由此我們深入並引申出各種知識,在腳本中我們覺得弱類型變量用起來很痛快,而其實它的內部實現其實是如此的復雜。
對於實現一門腳本語言,必須實現的是解釋器、虛擬機、上下文數據3大部分:
上下文數據這一層是腳本最基礎,最底層的東西,它決定了這門腳本究竟能做什么。拋開解釋器和虛擬機,我們依然可以單純地通過C接口,在C++這一層就能操作腳本的上下文數據。
有空再研究一下Lua的GC,解釋器等等。