lua虚拟机的实现(虚拟机的整体结构)
1.文本加载器
文本加载器包含加载脚本及脚本语义解析的功能,lua的文本加载器由lua lexstate进行处理。
lua语法支持语义关键字为:and, break, do, else, elseif, end, false, for, function,
goto, if, in, local, nil, not, or, repeat, return, then, true, until, while。
lua特殊语义子符为://, …, …, ==, >=, <=, ~=,<<, >>, ::, , , , , 。
语义信息的抽象结构为
typedef union {
lua_Number r; //浮点值
lua_Integer i; //整型值
TString *ts; //字符型(对象和字符串等)
}
语义与含义绑定结构(Token)
typedef struct Token {
int token;
SemInfo seminfo;
} Token
lua语义解析状态机- LexState
typedef struct LexState {
int current;
int linenumber;
int lastline;
Token t;
Token lookahead;
struct FuncState *fs;
struct lua_State *L;
ZIO *z;
Mbuffer *buff;
Table *h;
struct Dyndata *dyd;
TString *source;
TString *envn;
} LexState
2.文本解析器
虚拟机的脚本解析器有lparser进行处理,lparser负责加载和读取解析脚本, 对脚本中的语法错误进行处理。对于正确的脚本,lparser会生成相应的字节码用于脚本功能的执行。
lparser将脚本抽象为表达式,局部变量,跳转,动态结构和lua函数作为脚本功能的组合。
这几种组合包含了所有可能的脚本处理内容。下面是这几种结构和功能的解释。
表达式
表达式类型约定
typedef enum {
VVOID, //空
VNIL, //nil
VTRUE, //常量true
VFALSE, //常量false
VK, //
VKFLT, //浮点数
VKINT, //整型
VNONRELOC, //确定的值
VLOCAL, //局部变量
VUPVAL, //upvalue变量
VINDEXED, //可索引的变量
VJMP, //用于指令跳转
VRELOCABLE, //可保存至任意的register中的变量
VCALL, //用于函数调用
VVARARG //可变变量表达式
} expkind;
表达式描述结构
typedef struct expdesc {
expkind k; //表达式所属类型
union {
lua_Integer ival; //整型值
lua_Integer nval; //浮点值
int info; //
struct { //VINDEXED(可索引的变量使用)
short idx; //索引值
lu_byte t; //table表(register或者upvalue)
lu_byte vt; //是局部变量还是upvalue
} ind;
} u;
int t; //为true判定退出(exit)的patch list
int f; //为false判定退出(exit)的patch list
} expdesc;
局部变量
跳转指令(goto或者label语句)
lua中的跳转指令存在goto和label两种,并且能解析多个这种指令,指令结构的描述如下
typedef struct Labeldesc {
TString *name; //label标记名称
int pc; //代码执行位置
int line; //代码所在脚本的行数
lu_byte nactvar; //当前块的局部层次
} Labeldesc;
动态的结构
typedef struct Dyndata {
struct {
Vardesc *arr;
int n;
int size;
} actvar; //"活跃状态"的局部变量表
Labellist gt; //待处理的goto表
Labellist label; //"活跃状态"的label表
} Dyndata;
代码块结构描述
typedef struct BlockCnt {
struct BlockCnt *previous; //
int firstlabel; //在块中第一个label的索引
int firstgoto; //在块中第一个goto的索引
lu_byte nactvar; //在块外部的活跃变量数
lu_byte upval; //在快中是否存在upvalue
lu_byte isloop; //块是否为一个循环
} BlockCnt;
lua函数
lua函数解析部分由FuncState处理
typedef struct FuncState {
Proto *f; //函数原型
struct FuncState *prev; //闭包函数
struct LexState *ls; //语义解析状态机
struct BlockCnt *bl; //代码块
int pc; //程序计数器
int lasttarget; //上一个跳转label
int jpc; //待处理的跳转的位置
int nk; //在k中的元素数量
int np; //在p中的元素数量
int firstlocal; //在动态数组中第一个局部变量的索引
short nlocvars; //在f->locvars中的元素数量
lu_byte nactvar; //活跃的局部变量数量
lu_byte nups; //upvalue的数量
lu_byte freereg; //第一个空闲的register
} FuncState;
3.字节码解释器
4.gc功能支持
lua虚拟机中可被gc收集处理的对象类型(TString, UData, Closure, Table, Proto, lua_State)。
TString为字符串,UData由外部调用传入的数据结构,Closure为闭包结构,Table是虚拟机中的表,
Proto为lua中的原型结构,lua_State为单独执行的结构(lua thread)。
可被gc收集处理的结构表达如下
union GCUnion {
GCObject gc;
struct TString ts;
struct UData u;
union Closure cl; //包含c的闭包和lua的闭包
struct Table h; //hash表
struct Proto p;
struct lua_State th; //(lua thread)
}
lua的gc处理由gclist(GCObject组成的链表)进行管理。lua gc存在以下几种状态
#define LUA_GCSTOP 0 //停止gc
#define LUA_GCRESTART 1 //重新进行gc
#define LUA_GCCOLLECT 2 //进行一个完整的gc
#define LUA_GCCOUNT 3
#define LUA_GCCOUNTB 4
#define LUA_GCSTEP 5
#define LUA_GCSTOPAUSE 6
#define LUA_GCSETSTEPMUL 7
#define LUA_GCISRUNNING 9
gc对象以三种颜色进行标记(white, grey, black)。white没有进行gc标记;grey表示已被标记,但是存在引用;black表示被标记同时引用不存在,即可以被清理。
lua gc收集器存在的几种状态
gc功能对于支持清理速度的变更设置
#define GCSWEEPCOST ((sizeof(TString) + 4) / 4) //清理对象的单元划分
#define GCSWEEPMAX (cast_int((GCSTEPSIZE / GCSWEEPCOST) / 4)) //单步清理最大清理单元数量
#define GCSpropagate 0 //gc收集器增加
#define GCSatomic 1 //原子式收集
#define GCSswapallgc 2
#define GCSswapfinobj 3
#define GCSswaptobefnz 4
#define GCSswpend 5 //gc清理完成
#define GCScallfin 6
#define GCSpause 7 //gc收集器暂停
lua gc收集器状态迁移图如下:
5.运行时环境支持
lua虚拟机的"thread"处理结构(lua_State)
struct lua_State {
CommonHeader;
unsigned short nci;
lu_byte status;
StkId top; //thread的栈顶
global_State *l_G; //全局状态结构引用
CallInfo *ci; //当前函数的调用信息
const Instruction *oldpc; //最近一次的pc(程序计数器)
StkId stack_last;
StkId stack;
UpVal *openupval;
GCObject *gclist;
struct lua_State *twups;
struct lua_longjmp *errJmp; //当前的错误跳转点(用作错误跳转)
CallInfo base_ci; //
volatile lu_Hook hook;
ptrdiff_t errfunc; //当前错误处理函数(由stack index索引得到)
int stacksize; //stack堆栈大小
int basehookcount;
int hookcount;
unsigned short nny; //非yeild类型的调用数量
unsigned short nCCalls; //嵌套的c调用数量
l_signalT hookmask;
lu_byte allowhook; //hook功能开关
}
lua虚拟机全局状态结构(被lua所有的线程共享)
typedef struct global_State {
lua_Alloc frealloc;
void *ud;
l_mem totalbytes;
l_mem GCdebt;
lu_mem GCmemtrav;
lu_mem GCestimate;
stringtable strt;
TValue l_registry; //全局注册的库
unsigned int seed; //hash所用的随机seed值
lu_byte currentwhite;
lu_byte gcstate;
lu_byte gckind; //运行的gc类型
lu_byte gcrunning; //是否正在gc
GCObject *allgc; //所有collectable状态的对象链
GCObject **sweppgc; //gc链中清理的当前位置
GCObject *finobj; //finalizer中的collectable对象链表
GCObject *grayobject; //存放gray对象的链表
GCObject *weak; //存放weak值得hash表
GCObject *ephemeron; //存放weak键的hash表
GCObject *allweak;s //all-weak的hash表
GCObject *tobefnz; //待gc的用户数据(userdata)链表
GCObject *fixedgc; //不会被gc收集的对象链表
struct lua_State *twups; //存放threads(线程)中的open状态的upvalue
unsigned int gcfinnum; //在每一步的GC中finalizer的调用数量
int gcpause; // 成功的GC调用的pause大小间隔
int gcstepmul; //gc粒度
lua_CFunction panic; //未保护的错误的处理函数
struct lua_State *mainthread; //mainthread
const lua_Number *version; //版本号
TString *memerrmsg; //内存错误信息
TString *tmnames[TM_N]; //tag标记method名字(数组)
struct Table *mt[LUA_NUMTAGS]; //基础类型的meta table
TString *strcache[STRCACHE_N][STRCACHE_M]; //API函数中使用的字符串的缓存结构
} global_State;