美文网首页
Lua的内存布局结构

Lua的内存布局结构

作者: 码上述Andy | 来源:发表于2022-05-14 21:40 被阅读0次

    Lua的内存布局结构

    0.Lua编译过程?
    1.Lua内存布局?
    2.内存中的状态?

    我们知道每种语言都有自己的内存布局的状态,比如C++,Lua也不例外(动态语言:即边解释边执行),只是没有 C++ 那么复杂,C++ 是面向对象语言静态语言,由于本身语言特性比较多,比如继承、虚函数等,所以对象的内存布局模型会复杂很多。下面我们结合一个简单的脚本示例及加载、执行脚本主干流程产生的数据结构对象来分析Lua中的内存布局是怎么样的。 Lua脚本通过编译成字节码中间状态才能在虚拟机上运行。这里主要分析脚本编译过程及加载到执行每个阶段内存中布局状态。各阶段涉及到创建的重要数据结构及加载执行中的内存布局。对应代码的流程为luaL_loadfile到lua_pcall的过程。

    Lua从加载到执行可以理解整个过程就是两个状态:静态(Proto)和动态(LCloure)。
    静态:编译器生成字节码后的中间产物:Proto(函数原型);
    动态:是虚拟机执行过程中产生的LClosure(函数闭包或者说是函数对象)以及此闭包可以访问到的变量(upvalue:理解为函数上面的外部局部变量或者理解函数作用域的值);
    动态与静态的产物的关系可以理解为:LClosure = Proto + upvalues;
    LClosure在Lua里中也为数据类型。

    1.脚本及ByteCode:

    test.lua源码:

    function f()
    print("hello thunder")
    end
    

    脚本编译ByteCode(luac -l -l luac.out):

    main <test.lua:0,0> (4 instructions at 0x7fbfdf4061b0)
    0+ params, 2 slots, 1 upvalue, 0 locals, 1 constant, 1 function
        1   [1] VARARGPREP  0
        2   [3] CLOSURE     0 0 ; 0x7fbfdf4062c0
        3   [1] SETTABUP    0 0 0   ; _ENV "f"
        4   [3] RETURN      0 1 1   ; 0 out
    constants (1) for 0x7fbfdf4061b0:
        0   S   "f"
    locals (0) for 0x7fbfdf4061b0:
    upvalues (1) for 0x7fbfdf4061b0:
        0   _ENV    1   0
    
    function <test.lua:1,3> (5 instructions at 0x7fbfdf4062c0)
    0+ params, 2 slots, 1 upvalue, 0 locals, 2 constants, 0 functions
        1   [1] VARARGPREP  0
        2   [2] GETTABUP    0 0 0   ; _ENV "print"
        3   [2] LOADK       1 1 ; "hello thunder"
        4   [2] CALL        0 2 1   ; 1 in 0 out
        5   [3] RETURN      0 1 1   ; 0 out
    constants (2) for 0x7fbfdf4062c0:
        0   S   "print"
        1   S   "hello thunder"
    locals (0) for 0x7fbfdf4062c0:
    upvalues (1) for 0x7fbfdf4062c0:
        0   _ENV    0   0
    

    2.脚本编译过程:

    image.png

    源码通过编译(luac)把所有的函数信息表示成Proto数据结构存储比如参数、常量、指令集、调试信息、本地变量及各数据size等等,保存编译的结果。最终通过虚拟机读取Proto指令数值中指令一个个来执行(lvm.c#luaV_execute)。执行过程中的数据单元为函数闭包(CLosure)。通过这个过程的了解,我们知道接下来要分析的是什么数据对象了。

    3.Lua脚本加载内存布局

    加载(luaL_loadfile)到内存后的内存布局:


    vm_mem_z.png

    在Lua中脚本加载通过luaL_loadfile生成一个函数闭包压入栈顶,然后luaY_parser词法、语法分析之后生成对应的数据存入到Proto中,字节码对应可详见函数部分Lua字节码文件结构及加载过程。如图主要为数据栈,函数调用栈以及全局状态中的数据及重要的栈指针。其中stack_last(最大stack位置,范围[stack,stack_last])、top(栈顶)、stack(栈底)指向虚拟栈中对应的位置。具体数据含义可以参照:虚拟机篇

    4.ByteCode VM上运行内存布局

    Lua ByteCode加载到虚拟机之后执行(lua_pcall)字节码的内存布局状态:


    vm_mem_zw.png

    Lua执行通过lua_pcall函数,这里会涉及到一个比较重要的数据结构CallInfo,到lua_pcallk函数中,首先构建一个CallInfo指针实例指向lua_State(CallInfo *ci)当前函数及当前函数index等。最终通过luaV_execute函数pc = ci->u.l.savedpc、i(指令index) = *(pc++)循环执行的。具体可以详见:虚拟机篇。执行过程中除加载阶段涉及到的数据结构外比较重要的是CallInfo,状态中为双向链表结构,可以理解成函数调用栈,通过luaL_State结构体ci、base_ci指向函数栈当前CallInfo和栈底CallInfo。

    5.总结

    脚本加载、执行过程中主要围绕这几个数据结构(lua_State、global_State、CallInfo、LCloure、Proto)展开来的就是Lua内存布局结构(编译时结构和运行时结构)。通过上面我们应该很清晰什么是内存布局了,其实就是内存中的数据结构。

    相关文章

      网友评论

          本文标题:Lua的内存布局结构

          本文链接:https://www.haomeiwen.com/subject/gfuturtx.html