lua源码分析4(lua是怎么执行的)
2008-11-05 16:27
281 查看
第3章 lua中函数调用的方法
前面,以及分析了lua中定义一个函数的方法,现在总结如下:
1、 将函数名作为局部变量存在局部变量表里,并在栈上开辟一个寄存器空间,在运行期,将新建一个closure,并存在已保留的寄存器里;
2、 将在语法解析阶段新建的FuncState结构体保存在其父函数的局部函数定义数组里。
现在,就要分析,当lua进行函数调用的时候,是怎么调用的呢?
当分析到这里,对于lua生成中间码的过程就比较熟悉了。关键是生成的中间码必须要和lua虚拟机的执行联系在一起。所以,对于这里分析的函数调用,要结合lua虚拟机的执行一起来分析。
上篇文章对生成局部函数中间码做了简单的介绍。这里知道,当lua发现一个新定义的函数的时候,会生成OP_CLOSURE指令。那么,lua虚拟机执行到OP_CLOSURE后怎么执行呢?
在此之前,先说在lua解析代码完了以后,会做那些善后之事呢?
前面说过,lua会把一个代码文件当作是一个函数解析执行。在解析期间,它会率先生成一个FuncState的结构,作为最外面的函数。但这是解析时做的事情,运行期间,是不会有FuncState这个东西出现的。在运行期间,是由一个个叫CallInfo的数据结果的,它指的是当前运行的函数。
那么,在解析代码以后,是怎么转入运行的呢?
秘密就在lua做的一些善后工作。在f_parser()函数里,解析完了后,最外层的那个FuncState是有的,其实用的是FuncState里的函数头Proto。然后,lua会新建一个闭包(Closure),通过函数luaF_newLclosure(),并把刚才解析代码生成的Proto保存在这个闭包里,cl->l.p=tf,新建完了这个Closure,lua会把它压入栈。然后就转入运行期进行运行。也就是进入lua_pcall()函数执行。而lua_pcall()这个函数就是假设在当前栈顶有一个Closure,然后执行这个Closure。具体说来如下:
lua_pcall()经过一系列调用,最后通过调用luaD_precall()来执行。
这个luaD_precall()做什么呢?如果这个函数是C函数,那么就直接执行,如果是lua函数,这个函数便字如其人了,就是做执行前的准备。
首先,这个函数就会在L的ci数组里申请一项空槽。也就是说,L里有一个CallInfo数组,代表每个执行的函数。然后用现在的Closure初始化这个CallInfo。并且将这个函数的栈底标志在栈顶,也就是那个Closure压入栈的地方。
接着,就是转入lua的虚拟机的运行了。也就是函数luaV_execute()。
这时,如果遇到OP_CALL的时候,也就是在lua代码中有函数调用的时候,就会直接用luaD_precall(),将要执行的函数加载到L->ci中去,然后重新开始luaV_execute()虚拟机执行。
与调用函数相对应的,怎么从一个函数里返回呢?
也就是,当lua虚拟机遇到OP_RETUREN的时候,该如何操作呢?
lua会通过一个函数来实现从子函数到调用它的函数里的一个转变:luaD_poscall()。在这个函数里,lua主要做的事情就是,因为L->ci是个链,代表当前运行函数的嵌套调用链,调用过的函数会被直接丢弃。于是,只要L->ci--,也就是让当前ci指针退一格,指向前面调用它的函数,即可返回。另外,要将栈底位置和pc值恢复,这些恢复都很简单。
但是,最主要的一件事,就是如果这个函数里含有upvalue怎么办?这个留在明天再研究一下。
前面,以及分析了lua中定义一个函数的方法,现在总结如下:
1、 将函数名作为局部变量存在局部变量表里,并在栈上开辟一个寄存器空间,在运行期,将新建一个closure,并存在已保留的寄存器里;
2、 将在语法解析阶段新建的FuncState结构体保存在其父函数的局部函数定义数组里。
现在,就要分析,当lua进行函数调用的时候,是怎么调用的呢?
当分析到这里,对于lua生成中间码的过程就比较熟悉了。关键是生成的中间码必须要和lua虚拟机的执行联系在一起。所以,对于这里分析的函数调用,要结合lua虚拟机的执行一起来分析。
上篇文章对生成局部函数中间码做了简单的介绍。这里知道,当lua发现一个新定义的函数的时候,会生成OP_CLOSURE指令。那么,lua虚拟机执行到OP_CLOSURE后怎么执行呢?
在此之前,先说在lua解析代码完了以后,会做那些善后之事呢?
前面说过,lua会把一个代码文件当作是一个函数解析执行。在解析期间,它会率先生成一个FuncState的结构,作为最外面的函数。但这是解析时做的事情,运行期间,是不会有FuncState这个东西出现的。在运行期间,是由一个个叫CallInfo的数据结果的,它指的是当前运行的函数。
那么,在解析代码以后,是怎么转入运行的呢?
秘密就在lua做的一些善后工作。在f_parser()函数里,解析完了后,最外层的那个FuncState是有的,其实用的是FuncState里的函数头Proto。然后,lua会新建一个闭包(Closure),通过函数luaF_newLclosure(),并把刚才解析代码生成的Proto保存在这个闭包里,cl->l.p=tf,新建完了这个Closure,lua会把它压入栈。然后就转入运行期进行运行。也就是进入lua_pcall()函数执行。而lua_pcall()这个函数就是假设在当前栈顶有一个Closure,然后执行这个Closure。具体说来如下:
lua_pcall()经过一系列调用,最后通过调用luaD_precall()来执行。
这个luaD_precall()做什么呢?如果这个函数是C函数,那么就直接执行,如果是lua函数,这个函数便字如其人了,就是做执行前的准备。
首先,这个函数就会在L的ci数组里申请一项空槽。也就是说,L里有一个CallInfo数组,代表每个执行的函数。然后用现在的Closure初始化这个CallInfo。并且将这个函数的栈底标志在栈顶,也就是那个Closure压入栈的地方。
接着,就是转入lua的虚拟机的运行了。也就是函数luaV_execute()。
这时,如果遇到OP_CALL的时候,也就是在lua代码中有函数调用的时候,就会直接用luaD_precall(),将要执行的函数加载到L->ci中去,然后重新开始luaV_execute()虚拟机执行。
与调用函数相对应的,怎么从一个函数里返回呢?
也就是,当lua虚拟机遇到OP_RETUREN的时候,该如何操作呢?
lua会通过一个函数来实现从子函数到调用它的函数里的一个转变:luaD_poscall()。在这个函数里,lua主要做的事情就是,因为L->ci是个链,代表当前运行函数的嵌套调用链,调用过的函数会被直接丢弃。于是,只要L->ci--,也就是让当前ci指针退一格,指向前面调用它的函数,即可返回。另外,要将栈底位置和pc值恢复,这些恢复都很简单。
但是,最主要的一件事,就是如果这个函数里含有upvalue怎么办?这个留在明天再研究一下。
相关文章推荐
- lua开源测试框架busted源码学习(二)--代码框架分析和用例执行流程
- Lua 5.3 源码分析(十)线程的执行与中断
- Lua - 决定开始分析Lua的源码
- hive原理与源码分析-UDxF、优化器及执行引擎(五)
- Mybatis3源码分析(16)-Sql解析执行-结果集映射(ResultSetHandler)
- 关于云风在 Lua 中实现面向对象的源码分析
- Windows源码分析 - 1.初始化内核与执行体子系统
- 细谈struts2中action执行流程和源码分析
- Zookeeper源码分析(4)- Follower执行流程
- Yii2.0源码分析之——控制器文件分析(Controller.php)创建动作、执行动作
- 比特币源码分析(10) - 可执行程序 - Bitcoind
- Struts中ActionServlet源码深入分析执行过程
- lua源码分析之性能分析1(赋值研究)
- Docker源码分析(二):Docker Client创建与命令执行
- LUA源码分析三:table分析(1)
- JSP运行原理以及执行过程源码分析
- twitter storm源码走读之6 -- Trident Topology执行过程分析
- Struts2 源码分析——调结者(Dispatcher)之执行action
- Hadoop作业提交与执行源码分析
- suricata 3.1 源码分析24 (数据包解码模块执行)