Lua的环境与热更

news2025/1/14 9:55:40

一、global_State,lua_State与G表

Lua支持多线程环境,使用 lua_State 结构来表示一个独立的 Lua 线程(或协程)。每个线程都需要一个独立的全局环境。而lua_State 中的l_G指针,指向一个global_State结构,这个就是我们常说的G表,也就是一个全局变量和虚拟机全局资源的集合。

struct lua_State {
  CommonHeader;
  lu_byte status;
  StkId top;  /* first free slot in the stack */
  StkId base;  /* base of current function */
  global_State *l_G;
  CallInfo *ci;  /* call info for current function */
  const Instruction *savedpc;  /* `savedpc' of current function */
  StkId stack_last;  /* last free slot in the stack */
  StkId stack;  /* stack base */
  CallInfo *end_ci;  /* points after end of ci array*/
  CallInfo *base_ci;  /* array of CallInfo's */
  int stacksize;
  int size_ci;  /* size of array `base_ci' */
  unsigned short nCcalls;  /* number of nested C calls */
  unsigned short baseCcalls;  /* nested C calls when resuming coroutine */
  lu_byte hookmask;
  lu_byte allowhook;
  int basehookcount;
  int hookcount;
  lua_Hook hook;
  TValue l_gt;  /* table of globals */
  TValue env;  /* temporary place for environments */
  GCObject *openupval;  /* list of open upvalues in this stack */
  GCObject *gclist;
  struct lua_longjmp *errorJmp;  /* current error recover point */
  ptrdiff_t errfunc;  /* current error handling function (stack index) */
};

下面看看global_State,它是 Lua 虚拟机全局的状态结构体,存储了与整个 Lua 虚拟机相关的全局数据和资源。它是为整个 Lua 实例共享的数据结构,而非单个线程或协程。

typedef struct global_State {
  stringtable strt;  /* hash table for strings */
  lua_Alloc frealloc;  /* function to reallocate memory */
  void *ud;         /* auxiliary data to `frealloc' */
  lu_byte currentwhite;
  lu_byte gcstate;  /* state of garbage collector */
  int sweepstrgc;  /* position of sweep in `strt' */
  GCObject *rootgc;  /* list of all collectable objects */
  GCObject **sweepgc;  /* position of sweep in `rootgc' */
  GCObject *gray;  /* list of gray objects */
  GCObject *grayagain;  /* list of objects to be traversed atomically */
  GCObject *weak;  /* list of weak tables (to be cleared) */
  GCObject *tmudata;  /* last element of list of userdata to be GC */
  Mbuffer buff;  /* temporary buffer for string concatentation */
  lu_mem GCthreshold;
  lu_mem totalbytes;  /* number of bytes currently allocated */
  lu_mem estimate;  /* an estimate of number of bytes actually in use */
  lu_mem gcdept;  /* how much GC is `behind schedule' */
  int gcpause;  /* size of pause between successive GCs */
  int gcstepmul;  /* GC `granularity' */
  lua_CFunction panic;  /* to be called in unprotected errors */
  TValue l_registry;
  struct lua_State *mainthread;
  UpVal uvhead;  /* head of double-linked list of all open upvalues */
  struct Table *mt[NUM_TAGS];  /* metatables for basic types */
  TString *tmname[TM_N];  /* array with tag-method names */
} global_State;

再进一步讲,lua_state 是暴露给用户的数据类型,既表示一个 lua 程序的执行状态,也指代 lua 的一个线程(在官方文档中)。每个线程拥有独立的数据栈以及函数调用栈,还有独立的调试钩子和错误处理设置。所以我们不应当简单的把lua_state 看成一个静态的数据集,它是一个lua 线程的执行状态。所有的lua C API 都是围绕这个状态机:
或把数据压入堆栈,或取出,或执行栈顶的函数,或继续上次被中断的执行过程。

从 lua 的使用者的角度看,global_state 是不可见的。我们无法用公开的 api 取到它的指针,也不需要引用它。但分析lua 的实现就不能绕开这个部分。
global_state  里面有对主线程的引用,有注册表管理所有全局数据,有全局字符串表,有内存管理函数,有GC 需要的把所有对象串联起来的相关信息,以及一切 lua 在工作时需要的工作内存。

二、_ENV 是什么

_ENV其实代表的是当前函数的环境,一个环境就是一个表,该函数被限定为只能访问该表中的域,或在函数体内自己定义的变量。

在 Lua 中,会为每个代码段增加一个预定义上值,即 _ENV 。他是一个外部局部变量,会将代码段中使用的 “全局变量” 存在这个 _ENV 中。

Lua 编译器会将以下代码段进行转换,全局变量都会被带上 _ENV

local i = 10
j = 100
k = j + i
print(i, j, k)

会被转换为

local i = 10
_ENV.j = 100
_ENV.k = _ENV.j + i
print(i, _ENV.j, _ENV.k)

 对于上面的代码段, _ENV 的存在点,可以理解为如下

local _ENV = 初始化
return function (...)
    local i = 10
    _ENV.j = 100
    _ENV.k = _ENV.j + i
    print(i, _ENV.j, _ENV.k)
end

这里可以注意到 _ENV 是一个代码段外部的局部变量 ,但是他会进行初始化,理论上他可以是任意的表,但是为了维护全局的概念,所以这里会使用 _G 进行初始化,这样就让我们无感知的使用到了全局。

我们可以将 _ENV 和 _G 进行打印,在不进行修改的情况下,_ENV 和 _G 两者其实都是同一个表

do
    print(_ENV, _G)     --> table: 0x6000007d4200	table: 0x6000007d4200
end
那么我们通常去查找一个全局变量时,其实是在当前函数环境,也就是当前函数的env表中查找(准确来说是到函数对应的Closure(闭包)指针中的env表去查询数据)。这里提到的“当前函数环境”,指的不一定是某一个具体的函数,也可能是一个 Lua文件。
早期的Lua 提供了几个 API 来读取当前函数的环境,分别是 getfenv和 set env
因此,如果执行以下代码:
setfenv(1,{})
print(a)
实际上找不到 Lua 标准库提供的 print 函数,并且会提示报错 attempt to call global ’print’(a  nil
value )。 原因就是首先使用 setfenv将当前函数的env表置为一个空表 , env 表中查找不到这个名字的函数。
下面来看看函数的 env 表是如何创建的 在创建一个 Closure 对象时,都会调用 getcurrenv
函数来获取当前的环境表:
(lapi.c) 
79 static Table *getcurrenv (lua_State *L) { 
80     if (L- >ci == L- >base_ci) /* no enclosing function? */ 
81         return hvalue(gt(L)); /* use global table as environment */ 
82     else { 
83         Closure unc = curr_func(L); 
84         return func ->c.env; 
85     } 
86 }
它将区分如下两种情况。
1.如果该函数不是内嵌函数,那么直接返回G表
2.否则,如果是内嵌函数,就返回其母函数的 env表
在创建一个新的Closure(闭包) 时,会调用这个函数返回的结果,对新的 Closure 的环境进行赋值
这里可以看出, env 表会逐层继承。

三、Lua的热更

理解了ENV,热更部分可以先参考Lua 的代码加载和热更新方式

未完待续...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2251695.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【UVM】phase机制

Phase的种类 funcion phase 不消耗仿真时间 八种(图中白色背景) task phase 消耗仿真时间 一种(图中灰色背景),run_phase又可以细分为十二种 Phase的功能 bulid_phase:uvm_component类的实例化&…

《装甲车内气体检测“神器”:上海松柏 K-5S 电化学传感器模组详解》

《装甲车内气体检测“神器”:上海松柏 K-5S 电化学传感器模组详解》 一、引言二、K-5S 电化学传感器模组概述(一)产品简介(二)产品特点(三)产品适用场景 三、电化学传感器原理及优点(一&#xf…

【Linux课程学习】:文件第二弹---理解一切皆文件,缓存区

🎁个人主页:我们的五年 🔍系列专栏:Linux课程学习 🌷追光的人,终会万丈光芒 🎉欢迎大家点赞👍评论📝收藏⭐文章 Linux学习笔记: https://blog.csdn.net/d…

汽车控制软件下载移动管家手机控车一键启动app

移动管家手机控制汽车系统是一款实现车辆远程智能控制的应用程序‌。通过下载并安装特定的APP,用户可以轻松实现以下功能:‌远程启动与熄火‌:无论身处何地,只要有网络,即可远程启动或熄火车辆,提前预冷或预…

匿名发帖/匿名论坛功能设计与实现(编辑发帖部分)

前言 还是之前的音乐系统,首页一直是没想好写些什么,想写一个基于数据分析筛选的歌曲推荐功能,但是目前技术选型没太有考究等以后再实现吧,昨天突然想到可以把首页设计成前40%页面是歌曲推荐后面接下来就是一段匿名论坛功能&…

微信小程序 城市点击后跳转 并首页显示被点击城市

在微信小程序中,渲染出城市列表后,如何点击城市,就跳转回到首页,并在首页显示所点击的城市呢? 目录 一、定义点击城市的事件 二、首页的处理 首页:点击成都市会跳转到城市列表 城市列表:点击…

DOM,事件监听和VUE入门

四个事件、 JS模块化 VUE入门 Ajax VUE总结

Python学习笔记(5)Python的创建型设计模式

创建型设计模式(Creational Design Patterns),主要关注对象的创建机制。这类模式可以使得系统更加独立于如何创建、组合和表示其对象。通过将这些职责分离出来,创建型设计模式有助于提高代码的灵活性和复用性。 本书的范例代码已经…

云备份实战项目

文章目录 前言一、整体项目简介二、服务端环境及功能简介三、 客户端环境及功能简介四、服务端文件管理类的实现1. 获取文件大小,最后一次修改时间,最后一次访问时间,文件名称,以及文件内容的读写等功能2. 判断文件是否存在&#…

Java有关数组的相关问题

Java中的栈和堆的含义 栈 存储局部变量:栈主要用于存储方法中的局部变量,包括基本数据类型(int、double、boolean等)和对象的引用(不包含对象本身)。 遵循后进先出原则:当一个方法被调用时&…

使用Dify与BGE-M3搭建RAG(检索增强生成)应用-改进一,使用工作流代替Agnet

文章目录 前言Agent vs 工作流编写工作流 前言 在上一篇中,我们实现了一个基本的基于Dify的RAG的示范。 使用Dify与BGE-M3搭建RAG(检索增强生成)应用 这个效果确实很差。 我们一起来看看,该怎么改进。 今天我们就尝试一下&…

Python语法基础(四)

🌈个人主页:羽晨同学 💫个人格言:“成为自己未来的主人~” 高阶函数之map 高阶函数就是说,A函数作为B函数的参数,B函数就是高阶函数 map:映射 map(func,iterable) 这个是map的基本语法,…

Python毕业设计选题:基于django+vue的智慧社区可视化平台的设计与实现+spider

开发语言:Python框架:djangoPython版本:python3.7.7数据库:mysql 5.7数据库工具:Navicat11开发软件:PyCharm 系统展示 管理员登录 管理员功能界面 养老机构管理 业主管理 社区安防管理 社区设施管理 车位…

Ubuntu环境中RocketMQ安装教程

参考教程 https://blog.csdn.net/weixin_56219549/article/details/126143231 1、安装JDK,并配置环境变量(略) 2、下载RocketMQ安装包 RocketMQ下载地址,选择二进制包下载 unzip rocketmq-all-5.0.0-ALPHA-bin-release.zip 使…

【SSM】mybatis的增删改查

目录 代理Dao方式的增删改查 1. 创建项目 $$1. 在sql.xml里增加日志代码以及user的mapper资源。 $$ 2. 在usermapper里引入接口。 $$3. 在测试类中引入以下代码,并修改其中名字。 $$ 4. 实例对象User.java里属性要与表中列严格对应。 2. 查询 1>. 查询所有 …

【C++习题】23.二分查找算法_寻找旋转排序数组中的最小值

文章目录 题目链接:题目描述:解法C 算法代码:图解 题目链接: 153. 寻找旋转排序数组中的最小值 题目描述: 解法 暴力解法:O(n) 从前往后找数组中的最小值。 二分算法:O(logn) 先找二段性 因为这…

Linux笔记---进程:进程终止

1. 进程终止概念与分类 进程终止是指一个正在运行的进程结束其执行的操作。以下是一些常见的导致进程终止的情况: 一、正常终止 完成任务当进程完成了它被设计要执行的任务后,就会正常终止。收到特定信号在操作系统中,进程可能会收到来自操作…

立创庐山派 K230 RTSP 推流

立创庐山派使用的是K230芯片,按照教程刷了canmv固件,下载canmv ide,使用嘉楠社区的rtsp和wlan例程,修改成连接wifi以及RTSP推流例程 # Description: This example demonstrates how to stream video and audio to the network us…

Oracle数据恢复—Oracle数据库sysaux文件损坏的数据恢复案例

Oracle数据库故障&分析: 一台Oracle数据库打开报错,报错信息: “system01.dbf需要更多的恢复来保持一致性,数据库无法打开”。管理员联系我们数据恢复中心寻求帮助,并提供了Oracle_Home目录的所有文件。用户方要求…

【kafka04】消息队列与微服务之Kafka 图形工具

Kafka 在 ZooKeeper 里面的存储结构 topic 结构 /brokers/topics/[topic] partition结构 /brokers/topics/[topic]/partitions/[partitionId]/state broker信息 /brokers/ids/[o...N] 控制器 /controller 存储center controller中央控制器所在kafka broker的信息 消费者 /c…