lua的注册表,_ENV,_G

本文探讨了Lua中的全局表_G和_ENV,指出两者在lua52中具有相同的效果,展示了_LUA_RIDX_GLOBALS如何在注册表中初始化全局表,并详细解释了luaopen_base函数如何将基础函数注册到_G。同时,文章揭示了_ENV的设置过程,通过lua_load函数关联注册表、全局表与_ENV的关系。
部署运行你感兴趣的模型镜像

之前读书的时候,比较喜欢玩python,对于lua,就知道专门用来做游戏脚本的,一直没机会接触到。

有幸的是,今年进入了一个新项目,用到了lua.我做事不喜欢不明不白,所以在使用lua过程中遇到不明一般都喜欢看源码探其原由。

lua51和lua52都有一个叫全局表_G,遍历这个表就可以知道,这个表保存了lua所有的全局函数和全局变量。

而到了lua52,有个叫_ENV的玩意,遍历这个表也知道,也得到跟_G一样的结果.

先看看这个_G是如何产生的。

在lua中,多个lua state会共享一个global state,在通过luaL_newstate()产生lua state而后初始化的过程中,在f_luaopen这个函数会调用init_registry初始化一个叫注册表的玩意.

如下,调用栈:

 	test.exe!init_registry(lua_State * L, global_State * g)  行 168	C
 	test.exe!f_luaopen(lua_State * L, void * ud)  行 187 + 0xd 字节	C
 	test.exe!luaD_rawrunprotected(lua_State * L, void (lua_State *, void *)* f, void * ud)  行 133 + 0x1f 字节	C
 	test.exe!lua_newstate(void * (void *, void *, unsigned int, unsigned int)* f, void * ud)  行 304 + 0x10 字节	C
 	test.exe!luaL_newstate()  行 938 + 0xc 字节	C
	test.exe!main()  行 26 + 0x5 字节	C++

init_registry源码:

static void init_registry (lua_State *L, global_State *g) {
  TValue mt;
  /* create registry */
  Table *registry = luaH_new(L);
  sethvalue(L, &g->l_registry, registry);
  luaH_resize(L, registry, LUA_RIDX_LAST, 0);
  /* registry[LUA_RIDX_MAINTHREAD] = L */
  setthvalue(L, &mt, L);
  luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &mt);
  /* registry[LUA_RIDX_GLOBALS] = table of globals */
  sethvalue(L, &mt, luaH_new(L));
  luaH_setint(L, registry, LUA_RIDX_GLOBALS, &mt);
}

看源码的时候,没必面一开始就每一个函数,每一个宏都要搞清楚,通过其函数名大概知道其用意就行了,等对整体有了解再慢慢细化。

这个函数中,先产生一个lua的table,叫注册表,然后初始化其数组部分的大小为LUA_RIDX_LAST(2)(lua的table有数组跟hash两部分),然后把数组部分的LUA_RIDX_MAINTHREAD(1)赋值为L,也就是把这个注册表的数组部分的第一个元素赋值为主线程的状态机L(这里所说的线程并非是os的线程,而是lua的状态机概念)。通过luaL_newstate产生的lua state都会同时创建一个global state,很自然的就是,这个lua state就是主线程。

接下来,luaH_new创建一个table,再通过luaH_setint(L, registry, LUA_RIDX_GLOBALS, &mt)把刚创建的空table赋到LUA_RIDX_GLOBALS(2),也就是注册表的数组2指向的是一个全局表。

接下来看luaopen_base这个函数.

LUAMOD_API int luaopen_base (lua_State *L) {
  /* set global _G */
  lua_pushglobaltable(L);
  lua_pushglobaltable(L);
  lua_setfield(L, -2, "_G");
  /* open lib into global table */
  luaL_setfuncs(L, base_funcs, 0);
  lua_pushliteral(L, LUA_VERSION);
  lua_setfield(L, -2, "_VERSION");  /* set global _VERSION */
  return 1;
}

创建一个key为_G,指向注册表自已。然后通过luaL_setfuncs把基础的函数base_funcs注册到注册表中。

于是注册表就成了这样子。。



再看看_ENV是啥玩意,看lua_load函数是啥玩意:


LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
                      const char *chunkname, const char *mode) {
  ZIO z;
  int status;
  lua_lock(L);
  if (!chunkname) chunkname = "?";
  luaZ_init(L, &z, reader, data);
  status = luaD_protectedparser(L, &z, chunkname, mode);
  if (status == LUA_OK) {  /* no errors? */
    LClosure *f = clLvalue(L->top - 1);  /* get newly created function */
    if (f->nupvalues == 1) {  /* does it have one upvalue? */
      /* get global table from registry */
      Table *reg = hvalue(&G(L)->l_registry);
      const TValue *gt = luaH_getint(reg, LUA_RIDX_GLOBALS);
      /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */
      setobj(L, f->upvals[0]->v, gt);
      luaC_barrier(L, f->upvals[0], gt);
    }
  }
  lua_unlock(L);
  return status;
}

parser完后并没有错误会看到,会创建一个LClosure,lua的闭包,也就是每个lua文件load完后是一个lua闭包,是闭包的话自然就会有upvalue,所以这个闭包的第一个upvalue 就是regitry[LUA_RIDX_GLOBALS] 。

也就是注册表registry[2] = globaltable,_ENV = globaltable,globaltable["_G"] = globaltable.


您可能感兴趣的与本文相关的镜像

Qwen-Image

Qwen-Image

图片生成
Qwen

Qwen-Image是阿里云通义千问团队于2025年8月发布的亿参数图像生成基础模型,其最大亮点是强大的复杂文本渲染和精确图像编辑能力,能够生成包含多行、段落级中英文文本的高保真图像

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值