加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

深入理解Lua的全局变量_G以及源码实现

发布时间:2020-12-14 21:57:56 所属栏目:大数据 来源:网络整理
导读:在Lua脚本层,Lua将所有的全局变量保存在一个常规的table中,这个table被称为全局环境,并且将这个table保存在一个全局变量_G中,也就是说在脚本中可以用_G获取这个全局table,并且有_G._G == _G,在默认情况,Lua在全局环境_G中添加了标准库比如math、函数

在Lua脚本层,Lua将所有的全局变量保存在一个常规的table中,这个table被称为全局环境,并且将这个table保存在一个全局变量_G中,也就是说在脚本中可以用_G获取这个全局table,并且有_G._G == _G,在默认情况,Lua在全局环境_G中添加了标准库比如math、函数比如pairs等。可以通过下面代码,可以递归打印_G中的所有信息:

[cpp]? view plain copy

在CODE上查看代码片

派生到我的代码片

  1. function?treaverse_global_env(curtable,level)??
  2. ????for?key,value?in?pairs(curtable?or?{})?do??
  3. ????local?prefix?=?string.rep("?",level*5)??
  4. ????print(string.format("%s%s(%s)",prefix,key,type(value)))??
  5. ??
  6. ????--注意死循环??
  7. ????if?(type(value)?==?"table"?)?and?key?~=?"_G"?and?(not?value.package)?then??
  8. ????????treaverse_global_env(value,level?+?1)??
  9. ????elseif?(type(value)?==?"table"?)?and?(value.package)?then??
  10. ????????print(string.format("%sSKIPTABLE:%s",key))??
  11. ????end???
  12. ????end???
  13. end??
  14. ??
  15. treaverse_global_env(_G,0)??
注意Lua虚拟机本身是不会使用_G这个变量的,在脚本中,可以任意改变这个变量_G的值,不会影响任何环境或副作用。比如下面代码:

派生到我的代码片

    local?cf?=?loadstring("?local?i=0??i=i+1?print(i)?")??
  1. --从后面两个输出我们可以看出,生成的函数的环境就是全局_G??
  2. print(cf,getfenv(cf),_G)??--?function:?0025AF58??????table:?00751C68?table:?00751C68??
  3. --改变_G的值??
  4. _G?=?{}??
  5. cf()??--1??
  6. --虽然改变了_G的值,但函数的的环境仍然是全局环境table地址仍然是00751C68??
  7. print(cf,_G)??--?function:?0075AF58??????table:?00751C68?table:?0075B468??
默认情况下,在Lua中当compiles a chunk时,都是以_G作为环境的,当然可以通过函数load或loadfile,改变compiles a chunk时的环境。在C中,可以使用lua_load(类似有luaL_load*作为前缀的辅助函数)
来load a lua chunk,load的后得到的函数,默认情况下,它的第一个upvalue就是_G,我们可以改变第一个upvalue,来改变得到的函数执行环境。

变量_G是在C中注册的(源码linit.c,lbaselib.c中),在C中,可以直接调用lua_pushglobaltable把这个全局环境压入栈中,在lua5.2 该函数实质就是从注册表中获取这个全局环境,即lua_pushglobaltable用下面宏定义的:

派生到我的代码片

    #define?lua_pushglobaltable(L)?lua_rawgeti(L,?LUA_REGISTRYINDEX,?LUA_RIDX_GLOBALS)??
这里的LUA_REGISTRYINDEX是Lua注册表(注册表是lua虚拟机范围内是全局唯一的)的伪索引,LUA_RIDX_GLOBALS是全局环境在注册表中的索引(也就说,全局环境_G是虚拟机范围内是全局唯一的)。
最后可以通过源码,来了解一下_G全局环境的变量的注册。在一个Lua虚拟机中,用一个全局结构global_State来管理多个lua_State。在调用luaL_newstate()时,在创建一个全局结构global_State和一个lua_State后,
luaL_newstate会调用f_luaopen,然后f_luaopen调用init_registry来初始化注册表,函数init_registry代码如下:

派生到我的代码片

    /*?
  1. **?创建注册表和表中预定义的值?
  2. */??
  3. static?void?init_registry?(lua_State?*L,?global_State?*g)?{??
  4. ??TValue?mt;???
  5. ??/*创建注册表,初始化注册表数组部分大小为LUA_RIDX_LAST*/??
  6. ??Table?*registry?=?luaH_new(L);??
  7. ??sethvalue(L,?&g->l_registry,?registry);??
  8. ??luaH_resize(L,?registry,?LUA_RIDX_LAST,?0);???
  9. ??/*把这个注册表的数组部分的第一个元素赋值为主线程的状态机L(这里所说的线程并非是os的线程,而是lua的状态机概念)*/??
  10. /*即?registry[LUA_RIDX_MAINTHREAD]?=?L?*/??
  11. ??setthvalue(L,?&mt,?L);???
  12. ??luaH_setint(L,?LUA_RIDX_MAINTHREAD,?&mt);??
  13. /*把注册表的数组部分的第二个元素赋值为全局表,即registry[LUA_RIDX_GLOBALS]?=?table?of?globals?*/??
  14. ??sethvalue(L,?luaH_new(L));??
  15. 通过init_registry函数的实现可以看出,在创建注册表的同时,创建了全局环境,并把这个全局表赋值给注册表数组部分的第二个元素。通过下面代码,把上面创建的全局表注册到脚本中的,这样在脚本就可以使用变量_G来获取全局表了,代码如下:

    派生到我的代码片

      const?luaL_Reg?loadedlibs[]?=?{???
    1. ??{"_G",?luaopen_base},??
    2. ??{LUA_LOADLIBNAME,?luaopen_package},??
    3. /*?
    4. ??**省略了一些代码?
    5. ?????????*/??
    6. ??{LUA_DBLIBNAME,?luaopen_debug},248)"> ??{NULL,?NULL}??
    7. };??
    8. LUALIB_API?void?luaL_openlibs?(lua_State?*L)?{??
    9. ??const?luaL_Reg?*lib;??
    10. /*?从'loadedlibs'中调用函数,并把调用的结果res除了package.loaded[modname]=res?*/??
    11. /*同时设置到全局变量modname中,供脚本层调用*/??
    12. ??for?(lib?=?loadedlibs;?lib->func;?lib++)?{??
    13. ????luaL_requiref(L,?lib->name,?lib->func,?1);???
    14. ????lua_pop(L,?1);??/*?remove?lib?*/??
    15. ??}??
    16. ??**省略了一些代码?
    17. ?????????*/??
    18. }??
    在函数luaopen_base中会把脚本用到的函数注册到全局表, 代码如下:

    派生到我的代码片

      const?luaL_Reg?base_funcs[]?=?{???
    1. ??{"assert",?luaB_assert},108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> ??{"collectgarbage",?luaB_collectgarbage},??
    2. ??{"xpcall",?luaB_xpcall},248)"> ??????
    3. LUAMOD_API?int?luaopen_base?(lua_State?*L)?{??
    4. /*?设置_G._G?=?_G*/??
    5. ??lua_pushglobaltable(L);??
    6. ??lua_pushglobaltable(L);??
    7. ??lua_setfield(L,?-2,?"_G");??
    8. /*在全局表中添加脚本中用到的全局函数*/??
    9. ??luaL_setfuncs(L,?base_funcs,248)"> ??lua_pushliteral(L,?LUA_VERSION);??
    10. "_VERSION");??/*?set?global?_VERSION?*/??
    11. return?1;???


    参考资料:

    http://blog.csdn.net/ball32109/article/details/11402727
    http://blog.codingnow.com/2011/12/lua_52_env.html

    http://www.cnblogs.com/ringofthec/archive/2010/11/09/lua_State.html


    上文来自:http://blog.csdn.net/maximuszhou/article/details/24105673?utm_source=tuicool&utm_medium=referral

    (编辑:李大同)

    【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读