lua虚拟机的整体结构

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;

版权声明:本文为huzilinitachi原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。