【Lua进阶系列】lua_Stack

    大家好,我是Lampard~~

    欢迎来到Lua进阶系列的博客

前文再续,书接上一回。今天和大家讲解一下lua_Stack。

    还记得之前我们曾经写过一篇【lua基础系列】之C/C++与lua的交互方式 ,这篇文章简要提到了lua和c之间的交互过程以及TValue的数据结构,大家没看过的可以先点进去瞅一眼打个底。

    示意图如下:

(一)lua_Stack究竟由什么组成?

   c与lua之间交互离不开lua堆栈,那么lua堆栈究竟是什么东西,由什么组成?

   对于这个问题,我感觉没有比看源代码更有说服力的答案了(以下是lua5.4最新版代码)。

   直接定位到lua_State定义的文件“lstate.h”中,我们发现了以下结构体还有一堆英文注释,我们现在来一个个分析着看。

struct lua_State {CommonHeader;unsigned short nci;  /* 存储一共多少个CallInfo number of items in 'ci' list */lu_byte status;StkId top;  /* 指向栈的顶部,压入数据,都通过移动栈顶指针来实现。 first free slot in the stack */global_State *l_G;CallInfo *ci;  /* 当前运行函数信息 call info for current function */const Instruction *oldpc;  /* last pc traced */StkId stack_last;  /* 指向栈的底部,但是会预留空间作宝物处理 last free slot in the stack */StkId stack;  /* 指向栈的底部 stack base */UpVal *openupval;  /* list of open upvalues in this stack */GCObject *gclist;struct lua_State *twups;  /* list of threads with open upvalues */struct lua_longjmp *errorJmp;  /* current error recover point */CallInfo base_ci;  /* 调用栈的头部指针 CallInfo for first level (C calling Lua) */volatile lua_Hook hook;ptrdiff_t errfunc;  /* current error handling function (stack index) */int stacksize;    /* 栈的大小 */int basehookcount;int hookcount;unsigned short nny;  /* number of non-yieldable calls in stack */unsigned short nCcalls;  /* number of nested C calls */l_signalT hookmask;lu_byte allowhook;
};

(1) CommonHeader -- GC的通用头

#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked

CommonHeader 是使用引用计数机制进行垃圾回收的通用头。如果可以正确的遵从gc的使用规则,也就是说你可以正确无误的使用智能指针,那么理论上bai说,就不可能存在内存泄漏。

(2) nci -- 记录调用栈item个数的变量

nci是16位unsigned short类型的一个变量,用于记录有多少个item在调用栈(ci)中。

(3) status -- 表示当前这个lua_Stack线程的状态

注意这里的线程类型不要与操作系统线程混淆,Lua的线程类型是Lua虚拟机实现一种数据类型,简单来说也就是代表这个lua_Stack的状态。

我们看看lua线程的所有状态(存放在“lua.h”文件中):

LUA_OK -- 正常运行,LUA_YIELD  -- 挂起, LUA_ERRRUN -- 运行时错误,LUA_ERRSYNTAX -- 编译错误 ,LUA_ERRMEM -- 内存分配错误,LUA_ERRGCMM  -- GC内存回收错误,LUA_ERRERR --在运行错误处理函数时发生的错误

/* thread status */
#define LUA_OK      0
#define LUA_YIELD   1
#define LUA_ERRRUN  2
#define LUA_ERRSYNTAX   3
#define LUA_ERRMEM  4
#define LUA_ERRGCMM 5
#define LUA_ERRERR  6

 (4) l_G -- 全局状态机,维护全局字符串表、内存管理函数、gc等信息

  在5.4之前l_G并不是global_State全局状态机类型,它是一个把lua_Stack和global_State关联起来的一个结构体变量,不过很明显5.4之后lua底层直接把这个global_State暴露出来了,不过变量名还没有改(还是l_G)。

  我们不是在讲lua_State吗?为啥又来一个global_State呢?

  简而言之lua_state是是暴露给用户的数据类型(线程)用户通过它来调用C_API,global_State维护全局字符串表、内存管理函数、gc等信息。

关于具体的global_State我们可以看一下我写的这篇博客:【Lua进阶系列】全局状态机global_State

  两者大体上的区别如下:

执行状态机 -- lua_state(暴露给用户调用)
        lua_state 是暴露给用户的数据类型,既表示一个 lua 程序的执行状态,也指代 lua 的一个线程(在官方文档中)。
        每个线程拥有独立的数据栈以及函数调用栈,还有独立的调试钩子和错误处理设置。
        lua_state是一个lua 线程的执行状态。所有的lua C API 都是围绕这个状态机:
        lua_State是围绕程序如何执行来设计的,数据栈和调用栈都在其中。

全局状态机 -- 同一虚拟机中的所有执行线程(实际的虚拟机,一个全局状态机的数据多个lua_Stack共享)
        global_state  里面有对主线程的引用,有注册表管理所有全局数据,有全局字符串表
        有内存管理函数,有GC 需要的把所有对象串联起来的相关信息,以及一切 lua 在工作时需要的工作内存。
        通过 lua_newstate 创建一个新的 lua 虚拟机时,第一块申请的内存将用来保存主线程和这个全局状态机。

结构示意图:

 (5) StkId -- 数据栈

StkId top;  /* first free slot in the stack */
StkId stack_last;  /* last free slot in the stack */
StkId stack;  /* stack base */

前文提及在ua和C/C++是通过这个lua_State进行交互的,而lua_State就是利用StkId这个数据栈对数据进行暂存的。

下面我们看看这个StkId的代码定义:

我们可以看出,StkId其实是TValue的数组,那么TValue又是什么结构呢?

typedef union Value {GCObject *gc;    /* collectable objects */void *p;         /* light userdata */int b;           /* booleans */lua_CFunction f; /* light C functions */lua_Integer i;   /* integer numbers */lua_Number n;    /* float numbers */
} Value;#define TValuefields    Value value_; int tt_typedef struct lua_TValue {TValuefields;
} TValue;

通过上述代码关系我们可以看出,实际存储数据的数据结构是Value,而TValue是为了区分联合中存放的数据类型(使用tt字段),再额外绑定一个类型字段。

lua 中的数据可以这样分为两类:值类型和引用类型。值类型可以被任意复制,而引用类型共享一份数据。

Value存放了gc和几个属性值,属性值分别对应了lua的值类型数据,而gc则管理lua中的引用数据的生命周期。

    从下面的图可以的得出如下结论:

        1. lua中, number, boolean, nil, light userdata四种类型的值是直接存在栈上元素里的, 和垃圾回收无关.

        2. lua中, string, table, closure, userdata, thread存在栈上元素里的只是指针, 他们都会在生命周期结束后被垃圾回收.

  3.lua_state 的数据栈,就是一个 TValue 的数组。代码中用 StkId 类型来指代对 TValue 的引用。

 (6) CallInfo  -- 调用栈

CallInfo base_ci;  /* CallInfo for first level (C calling Lua) */
CallInfo *ci;  /* call info for current function */
  • 主要由一个CallInfo的结构组成。CallInfo是一个双向链表结构。通过双向链表结构来管理每一个Lua的函数调用栈信息。
  • Lua一共有三种类型的函数:C语言闭包函数(例如pmain)、Lua的C函数库(例如str字符串函数)和LUA语言
  • 每一个函数的调用,都会新生产一个CallInfo的调用栈结构,用于管理函数调用的栈指针信息。当一个函数调用结束后,会返回CallInfo链表的前一个调用栈,直到所有的调用栈结束回到L->base_ci。
  • 调用栈最终都会指向数据栈上,通过一个个调用栈,用于管理不同的函数调用。
typedef struct CallInfo {StkId func;  /* ci->func:指向正在调用操作的栈底位置。 function index in the stack */StkId   top;  /* 指向调用栈的栈顶部分 top for this function */struct CallInfo *previous, *next; /* previous和next是双向链表指针,用于连接各个调用栈。当执行完一个函数,通过previous回滚到上一个调用栈CI dynamic call link */union {struct {  /* only for Lua functions */StkId base;  /* base for this function */const Instruction *savedpc;} l;struct {  /* only for C functions */lua_KFunction k;  /* continuation in case of yields */ptrdiff_t old_errfunc;lua_KContext ctx;  /* context info. in case of yields */} c;} u;ptrdiff_t extra;short nresults;  /* expected number of results from this function */unsigned short callstatus;
} CallInfo;

这里可以举一个栗子,就是函数A调用函数B,函数b也调用函数C,那么此时base_ci的next就是函数A的callinfo,ci就是函数c的callinfo

实际上,遍历 L 中的 base_ci域指向的 CallInfo双向链表可以获得完整的 lua 调用栈。而每一级的 CallInfo 中,都可以进一步的通过 func 域取得所在函数的更详细信息。
当 func 为一个 lua 函数时,根据它的函数原型可以获得源文件名、行号等诸多调试信息。

 (7) HOOK 相关-- 服务于debug模块

  int basehookcount;int hookcount;volatile lua_Hook hook;l_signalT hookmask;lu_byte allowhook;

  volatile lua_Hook hook 存放了debug调用的钩子函数

struct lua_Debug {int event;const char *name;    /* (n) */const char *namewhat;  /* (n) 'global', 'local', 'field', 'method' */const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */const char *source; /* (S) */int currentline;   /* (l) */int linedefined;   /* (S) */int lastlinedefined;   /* (S) */unsigned char nups;    /* (u) number of upvalues */unsigned char nparams;/* (u) number of parameters */char isvararg;        /* (u) */char istailcall; /* (t) */char short_src[LUA_IDSIZE]; /* (S) *//* private part */struct CallInfo *i_ci;  /* active function */
};

关于debug库的一些介绍可以看一下我的这篇博客:【Lua进阶系列】之Debug库

 (8) GC 垃圾回收

GCObject *gclist;

【Lua进阶系列】lua_Stack相关推荐

  1. 【Lua进阶系列】实例lua调用capi

                             [Lua进阶系列]实例lua调用capi     大家好,我是Lampard~~     欢迎来到Lua进阶系列的博客     首先祝大家2021新年 ...

  2. 【Lua进阶系列】lua元方法

                        [Lua进阶系列]之Lua元方法案例+字段     大家好,我是Lampard~~     欢迎来到Lua进阶系列的博客 前文再续,书接上一回.今天和大家讲解一 ...

  3. malloc开辟的空间在哪一个区间_C++进阶系列之STL(2)SGI版本空间配置器

    1.STL中的空间配置器在STL中,空间配置器分了2组,分别为一级空间配置器和二级空间配置器,但是它们都有自己各自运用的场合:一般说来,一级空间配置器一般分配的空间大于128B,二级空间配置器的分配空 ...

  4. C#进阶系列——WebApi 身份认证解决方案:Basic基础认证

    阅读目录 一.为什么需要身份认证 二.Basic基础认证的原理解析 1.常见的认证方式 2.Basic基础认证原理 三.Basic基础认证的代码示例 1.登录过程 2./Home/Index主界面 3 ...

  5. C#进阶系列——WebApi 接口参数不再困惑:传参详解

    看这边文章时的疑惑是:WebApi中的参数加了[FromBody],不知所以然,就百度了下,看到了以下文章,和大家分享下: 原文链接:http://www.cnblogs.com/landeanfen ...

  6. C#进阶系列——WebApi 跨域问题解决方案:CORS

    C#进阶系列--WebApi 跨域问题解决方案:CORS 参考文章: (1)C#进阶系列--WebApi 跨域问题解决方案:CORS (2)https://www.cnblogs.com/landea ...

  7. C#进阶系列——DDD领域驱动设计初探(五):AutoMapper使用

    前言:前篇搭建了下WCF的代码,就提到了DTO的概念,对于为什么要有这么一个DTO的对象,上章可能对于这点不太详尽,在此不厌其烦再来提提它的作用: 从安全上面考虑,领域Model都带有领域业务,让Cl ...

  8. JavaScript进阶系列01,函数的声明,函数参数,函数闭包

    本篇主要体验JavaScript函数的声明.函数参数以及函数闭包. □ 函数的声明 ※ 声明全局函数 通常这样声明函数: function doSth() { alert("可以在任何时候调 ...

  9. HTML5 进阶系列:indexedDB 数据库

    前言 在 HTML5 的本地存储中,有一种叫 indexedDB 的数据库,该数据库是一种存储在客户端本地的 NoSQL 数据库,它可以存储大量的数据.从上篇:HTML5 进阶系列:web Stora ...

最新文章

  1. maven 一个模块生成多个jar包
  2. 第一百三十四期:MySQL分页查询方法及优化
  3. 阶段3 2.Spring_06.Spring的新注解_3 AnnotationConfigApplicationContext的使用
  4. Oracle pmon是什么,oracle 11g pmon工作内容系列二
  5. 一万八的M1 iPad Pro ,怎么就成了“期货”
  6. Java项目校园兼职平台(含代码)
  7. 凸包算法Graham扫描法
  8. 4G浏览器 随机遇而生
  9. Linux: Top命令查询结果参数详解
  10. python坑爹的黑店_曝光米兰爱马仕黑店极其坑爹配货经验!大家不要再白白上当了!...
  11. 城市智商的提出,基于互联网云脑的智慧城市发展水平评测研究
  12. 2.1 安装 go-gtk
  13. 快速梳理23种常用的设计模式
  14. C++语言——求圆柱表面积
  15. web框架详解之 tornado 四 模板引擎、session、验证码、xss
  16. Jmeter批量导入数据联想
  17. 如何从微信跳到外部浏览器进行apk文件(app)下载如何解决
  18. 阿尔法围棋击败人类是计算机在那方面的应用,三问人机战! 阿尔法围棋攻陷人类智慧最后堡垒?...
  19. 微信 10 年,张小龙的 7 个逻辑和一个选择
  20. c语言如何让窗口无法关闭,无法关闭窗口的程序

热门文章

  1. div 随着内容撑开
  2. iBATIS与Hibernate数据库映射框架
  3. Android OTA升级方式分析
  4. 2021寒假赋能!Python网络爬虫与文本分析直播课
  5. 关于数据库在不同服务器之间导入导出时出现的时间同步问题
  6. /稿件更正 -- 腾讯企鹅号/
  7. SecurityError Error 2148 SWF 不能访问本地资源
  8. html5视频播放器video player 选择
  9. idea2022版本下载安装配置与卸载详细步骤(包含运行第一个java程序教程)
  10. 搭建个人网站,用虚拟主机还是云服务器好呢?