lua 环境揭秘
什么是环境?
http://www.lua.org/manual/5.1/manual.html#2.9
Besides metatables, objects of types thread, function, and userdata have another table associated with them, called their environment. Like metatables, environments are regular tables and multiple objects can share the same environment.
线程、函数和用户自定义数据, 一张表与之关联,即环境表, 环境表也是章普通表, 多个对象可以共享同一个环境表。
Threads are created sharing the environment of the creating thread. Userdata and C functions are created sharing the environment of the creating C function. Non-nested Lua functions (created by
loadfile
,loadstring
orload
) are created sharing the environment of the creating thread. Nested Lua functions are created sharing the environment of the creating Lua function.
被创建的线程共享创建线程的环境。
被创建的用户数据和C函数共享 创建者(C函数)的环境。
Non-nested Lua 函数(由loadfile和loastring和load)被创建后, 共享创建者(线程)的环境。
Nested Lua函数被创建,共享创建者(Lua函数)的环境。
Environments associated with threads are called global environments. They are used as the default environment for threads and non-nested Lua functions created by the thread and can be directly accessed by C code (see §3.3).
关联线程的环境, 被称作全局环境。 线程和Non-nested函数被线程创建后, 默认的环境就是全局环境, 这些环境可以被C代码直接访问。
The environment associated with a C function can be directly accessed by C code (see §3.3). It is used as the default environment for other C functions and userdata created by the function.
C函数关联的环境,可以直接被C代码访问。
C函数创建的C函数和用户数据, 则将这个环境被作为默认环境。
Environments associated with Lua functions are used to resolve all accesses to global variables within the function (see §2.3). They are used as the default environment for nested Lua functions created by the function.
lua函数关联的环境,被用作解析所有的全局函数的访问, 在此函数中。
这个环境被用作如下情况的默认环境, lua函数创建的了nested lua函数, 则nested lua函数,则集成了 lua函数的环境。
You can change the environment of a Lua function or the running thread by calling
setfenv
. You can get the environment of a Lua function or the running thread by callinggetfenv
. To manipulate the environment of other objects (userdata, C functions, other threads) you must use the C API.
使用setfenv改变lua函数或者线程的环境。
使用getfenv获取lua函数或者线程的环境。
操作其它对象(userdata c函数 其它线程)的环境, 需要使用C API.
LUA thread
The type thread represents independent threads of execution and it is used to implement coroutines (see §2.11). Do not confuse Lua threads with operating-system threads. Lua supports coroutines on all systems, even those that do not support threads.
线程表达了一个独立的执行线索,它用于实现协程。
不要把lua的线程的操作系统的线程混淆。
lua支持协程在所有的系统上, 即使在那些不支持线程的系统上。
协程
Lua supports coroutines, also called collaborative multithreading. A coroutine in Lua represents an independent thread of execution. Unlike threads in multithread systems, however, a coroutine only suspends its execution by explicitly calling a yield function.
http://www.lua.org/manual/5.1/manual.html#2.11
http://www.cnblogs.com/chenny7/p/3634529.html
协同程序(coroutine)与多线程情况下的线程比较类似:有自己的堆栈、局部变量、指令指针,但与其它协程共享全局变量等很多信息。
协程类似一种多线程,但与多线程还有很多区别:
1. 协程并非os线程,所以创建、切换开销比线程相对要小。
2. 协程与线程一样有自己的栈、局部变量等,但是协程的栈是在用户进程空间模拟的,所以创建、切换开销很小。
3. 多线程程序是多个线程并发执行,也就是说在一瞬间有多个控制流在执行。而协程强调的是一种多个协程间协作的关系,只有当一个协程主动放弃执行权,另一个协程才能获得执行权,所以在某一瞬间,多个协程间只有一个在运行。
4. 由于多个协程时只有一个在运行,所以对于临界区的访问不需要加锁,而多线程的情况则必须加锁。
5. 多线程程序由于有多个控制流,所以程序的行为不可控,而多个协程的执行是由开发者定义的所以是可控的。Lua的协程是不对称的(asymmetric coroutines),是指“挂起一个正在执行的协同函数” 和 “使一个被挂起的协程再次执行的函数”是不同的。
有些语言使用对称协同(symmetric coroutines),即使用同一个函数负责“执行与挂起间的状态切换”。
Non-nested lua function
Non-nested Lua functions (created by
loadfile
,loadstring
orload
) are created sharing the environment of the creating thread.
Non-nested lua functions 没有具体例子说明,到底是什么样的函数, 只说是由 loadfile loadstring 和 load产生的, 可以理解为脚本文件对应的一个匿名函数。
这些函数继承了 创建者线程的 环境。
Environments associated with threads are called global environments. They are used as the default environment for threads and non-nested Lua functions created by the thread and can be directly accessed by C code (see §3.3).
线程相关的环境被称为全局环境, 当线程创建了 线程或者 non-nested lua函数 时候, 被创建者的默认环境, 就继承了创建者线程的环境。
所以, Non-nested lua functions 也具有全局环境。
对于 require语句, 调用一个脚本情况, require实际上相当于 local f=loadfile(filename); f()
整个脚本对应的匿名函数, 就具有创建者线程相同的环境, 创建者线程通过 require方法加载, 查询loader,找到lua脚本的加载器:
static int loader_Lua (lua_State *L) {
const char *filename;
const char *name = luaL_checkstring(L, 1);
filename = findfile(L, name, "path");
if (filename == NULL) return 1; /* library not found in this path */
if (luaL_loadfile(L, filename) != 0)
loaderror(L, filename);
return 1; /* library loaded successfully */
}
require函数的环境为 _G
require函数的实现简写为, 则filename脚本被创造为 chunk对应的 匿名函数 f, f默认继承了 require的环境 _G
function require(filename)
if package.loaded[filename] then
return package.loaded[filename]
end
local f = assert(loadfile(filename))
return f()
end
当然如果 filename 脚本中有 module语句, 则会改写 匿名函数的环境。
可以通过本文的给出的脚本进行验证。
main.lua
local module_mediator = require("module_mediator")
var_caller = 55
local function printTable(tbl)
for k,v in pairs(tbl) do
print("kay="..tostring(k) .. " value="..tostring(v))
endend
print("----------- module_mediator ------------------")
printTable(module_mediator)
print("----------- _G ------------------")
printTable(_G)
module_mediator.lua
local require = require
local setmetatable = setmetatable
local setfenv = setfenv
local getfenv = getfenv
local tostring = tostring
local print = printmodule("module_mediator", package.seeall)
var_mediator = 77
--[[
local require_env = {var_mediator=33}
setmetatable(require_env, {__index=_G})setfenv(require, require_env)
]]local require_env = getfenv(require)
local function printTable(tbl)
for k,v in pairs(tbl) do
print("kay="..tostring(k) .. " value="..tostring(v))
endend
print(" require_env ="..tostring(require_env))
print("----------- before require env ------------------")
printTable(require_env)
print("----------- after require env ------------------")
local module_test = require("module_test")
module_test.lua (将module 添加和去除, 本脚本中的 getfenv(1) 和 getfenv(2)结果进行比较):
去掉module, 打印:
getfenv(1) =table: 005D2658 // module_test.lua匿名函数 环境
getfenv(1) metatable=nil
getfenv(2) =table: 005D2658 // require 的环境, _Gkay=_G value=table: 005D2658
添加module, 打印:
getfenv(1) =table: 00773C68 // module_test.lua匿名函数 环境
getfenv(1) metatable=nil
getfenv(2) =table: 00252658 // require 的环境, _Gkay=_G value=table: 00252658
--module(..., package.seeall)
local print = print
local getfenv = getfenv
local tostring = tostring
local getmetatable = getmetatable
local pairs = pairsmodule("module_test")
local function printTable(tbl)
for k,v in pairs(tbl) do
print("kay="..tostring(k) .. " value="..tostring(v))
endend
varone = 1
local strkey = "vartwo"
-- 本模块环境
local env = getfenv(1)env[strkey] = 2
print("vartwo="..vartwo)
print("getfenv(1) ="..tostring( ( getfenv(1) ) ) )
print("getfenv(1) metatable="..tostring( getmetatable( getfenv(1) ) ) )
if getmetatable( getfenv(1) ) then
print("--------------- before getmetatable( getfenv(1) ) -------------")
printTable( getmetatable( getfenv(1) ) )
print("--------------- after getmetatable( getfenv(1) ) -------------")
end-- 调用此模块的环境
local env_caller = getfenv(2)env["env_caller"] = env_caller
print("getfenv(2) ="..tostring(getfenv(2)))
print("--------------- before env_caller -------------")
printTable( env_caller )
print("--------------- after env_caller -------------")
脚本文件与chunk与匿名函数
http://www.lua.org/manual/5.1/manual.html#2.11
The unit of execution of Lua is called a chunk. A chunk is simply a sequence of statements, which are executed sequentially. Each statement can be optionally followed by a semicolon:
chunk ::= {stat [`;´]}There are no empty statements and thus '
;;
' is not legal.
lua的执行单元是chunk, chunk是有一系列的语句组成。
Lua handles a chunk as the body of an anonymous function with a variable number of arguments (see §2.5.9). As such, chunks can define local variables, receive arguments, and return values.
lua将一个chunk作为一个匿名函数对待, 带有一系列的入参。
所以,chunk可以定义局部变量, 接受参数, 返回值。
A chunk can be stored in a file or in a string inside the host program. To execute a chunk, Lua first pre-compiles the chunk into instructions for a virtual machine, and then it executes the compiled code with an interpreter for the virtual machine.
chunk存在两种形式, 文件 或者 字符串。
执行chunk需要先将chunk编译为指令,这个是虚拟机指令, 然后执行编译后的代码, 使用虚拟机解释器。
Chunks can also be pre-compiled into binary form; see program
luac
for details. Programs in source and compiled forms are interchangeable; Lua automatically detects the file type and acts accordingly.
chunk可以被预先编译为二进制形式, 详情参与 luac。
源码和编译形式是可以互换的。lua会自动探测, 文件类型, 并执行响应动作。
如果是编译后的文件, 则lua不会再编译,直接执行, 如果未编译, lua会先编译后执行。
lua require dofile loadfile区别
http://blog.163.com/hbu_lijian/blog/static/126129153201422902256778/
1.dofile与loadfile
dofile当作Lua运行代码的chunk的一种原始的操作。dofile实际上是一个辅助的函数。真正完成功能的函数是loadfile;与dofile不同的是loadfile编译代码成中间码并且返回编译后的chunk作为一个函数,而不执行代码;另外loadfile不会抛出错误信息而是返回错误代。我们可以这样定义dofile:
function dofile (filename)
local f = assert(loadfile(filename))
return f()
end
3.require与dofile
。粗略的说require和dofile完成同样的功能但有两点不同:
1. require会搜索目录加载文件
2. require会判断是否文件已经加载避免重复加载同一文件。由于上述特征,require在Lua中是加载库的更好的函数。
Nested lua luafunction
Nested Lua functions are created sharing the environment of the creating Lua function.
嵌套lua函数共享了 创建者的环境。
什么是嵌套的lua函数呢? 没有官方定义。
http://lua-users.org/wiki/LuaScopingDiscussion
这个有个关于嵌套函数作用域的讨论, 函数curry化的用法, 被嵌套的是 sum函数:
function addn(x)
function sum(y)
return x+y
end
return sum
end
print((addn(3))(1))
通过样例解释嵌套函数环境
getfenvtest.lua
local getfenv = getfenv
local print = printmodule("getfenvtest")
var_test = 1
function test()
local env = getfenv(1)
print("var_test="..env["var_test"])local env_caller = getfenv(2)
print("env_caller[var_test]="..env_caller["var_test"])
endtest()
getfenv_main.lua
var_test = 100
local testmodule = require("getfenvtest")
local function printTable(tbl)
for k,v in pairs(tbl) do
print("kay="..tostring(k) .. " value="..tostring(v))
endend
print("-------- before test func env ------")
printTable(getfenv(testmodule.test))
print("-------- after test func env ------")function mediator()
testmodule.test()
end
local env = {}
env.testmodule = testmodule
env.mediatorflag = true
env.var_test = 55setfenv(mediator, env)
mediator()
打印:
var_test=1 // getfenv_test.lua中 调用test打印, 说明test函数继承了 此脚本的匿名函数环境
env_caller[var_test]=1 // getfenv_test.lua中 调用test打印, 此脚本的匿名函数调用了 此函数, 故此函数中getfenv(2) 返回的是 此脚本匿名函数的环境-------- before test func env ------ // 打印标明 虽然在 被调用脚本中使用, 但是test函数的环境,还是其创建时候的环境, 即getenvtest.lua中 module环境。
kay=_NAME value=getfenvtest
kay=_PACKAGE value=
kay=test value=function: 00539898
kay=_M value=table: 00539168
kay=var_test value=1
-------- after test func env ------
var_test=1 // 虽然在被调用脚本中使用, getfenv获得的环境还是 getfenvtest.lua中module的环境, 跟调用点没有关系。
env_caller[var_test]=55 // 在被调用脚本中使用, getfenv(2)获得的是调用脚本的环境。
>Exit code: 0
一个有趣的环境改变实验
话说一个聪明人, 能够根据环境讲不同的话语, 见到美国人, 说美国是个伟大的国家, 见到中国人, 中国是个伟大的国家, 背地里没有外国人的时候, 说MyCountry是个伟大的国家。
首先定义 聪明人为 wiseman.lua 使用module构造。
其中有一个关键函数是 makeEnvDependFunc, 将一个普通的函数,转换为, 判断会判断环境的函数。
实现原理为 改造此函数的依赖的 环境 setfenv, 当判断被调用的场景getfenv(2), 如果存在, 则重写环境为 {}
并将此{}的metatable设置为 __index=function()end, 函数体中会先寻找 getfenv(2), 然后在找getfenv(1)。
module("wiseman", package.seeall)
country = "My country"
local function makeEnvDependFunc(f)
local oldfenv = getfenv(f)-- 缂撳瓨鎵€鏈夌殑env
local envTbl = {}table.insert( envTbl, oldfenv )
-- environment, and isprior
local addNewEnv = function( env, isprior )
if isprior then
table.insert(envTbl, 1, env)
else
table.insert(envTbl, env)
end
endlocal deleteNewEnv = function(targetEnv)
for i,env in ipairs(envTbl) do
if targetEnv == env then
table.remove(envTbl, i)
end
end
endlocal indexEnvTbl = function(env, key)
for i,env in ipairs(envTbl) do
local val = env[key]
if val then
return val
end
endreturn nil
endlocal envMetaTable = {__index=indexEnvTbl}
local newfenv = {}
setmetatable(newfenv, envMetaTable)local fwrapper = function()
addNewEnv(getfenv(2), true)setfenv(f, newfenv)
local ret = f()
deleteNewEnv(getfenv(2))
setfenv(f, oldfenv)
return ret
endreturn fwrapper
end--[[
function SayHello()
local env = getfenv(1)local country_senario = country
local env_caller = getfenv(2)
if env_caller and env_caller.country then
country_senario = env_caller.country
endprint(country_senario .." is a great country!")
end
]]-- 姝ゆ椂 country 杩樻槸鎸囨湰module锛?My Country
function SayHello_Naive()
print(country .." is a great country!")
endSayHello = makeEnvDependFunc(SayHello_Naive)
china.lua
module("china")
country = "china"
function CallPerson( wiseman )
wiseman.SayHello()
end
american.lua
module("american")
country = "american"
function CallPerson( wiseman )
wiseman.SayHello()
end
主调脚本actionbycountry.lua
local wiseman = require("wiseman")
local chinese = require("china")
chinese.CallPerson(wiseman)local american = require("american")
american.CallPerson(wiseman)wiseman.SayHello()
打印:
>lua -e "io.stdout:setvbuf 'no'" "actionbycountry.lua"
china is a great country!
american is a great country!
My country is a great country!
>Exit code: 0
实验脚本如下:
https://github.com/fanqingsong/code-snippet/tree/master/lua/envtest
lua 环境揭秘的更多相关文章
- 【Lua学习笔记之:Lua环境搭建 Windows 不用 visual studio】
Lua 环境搭建 Windows 不用 visual studio 系统环境:Win7 64bit 联系方式:yexiaopeng1992@126.com 前言: 最近需要学习Unity3d游戏中的热 ...
- lua 学习 (一 )Mac搭建lua环境和ide
今天开始学习lua 首先是搭建环境 和 我的hello world 首先 交代一下我用的是 Mac 系统 所以在Mac上安装lua 环境很方便的 lua Mointain Lion - Lua 5 ...
- VS2012+LUA环境搭建
1 .启动VS2012,选择C++下的"win32"项目类型中的"Win2控制台应用程序" 2.工具——选项——项目和解决方案——VC++目录——可执行程序(C ...
- Nginx+lua环境搭建
其实有点类似WampServer一站式安装包 wget http://openresty.org/download/ngx_openresty-1.7.10.1.tar.gz tar -zxvf ng ...
- VS2012配置Lua环境
1.VS2012配置BabeLua插件 2.VS2012配置Lua 1.VS2012配置BabeLua插件 BabeLua插件简介: 安装方法: 关闭VS2012后直接安装BabeLua插件. 下载地 ...
- [Lua]Mac系统上安装Lua环境
1.下载 Lua语言的官方网站 http://www.lua.org/ 下载最新版本的Lua环境 2.安装 解压下载包lua-5.3.1.tar.gz 打开终端Terminal 使用cd命令进入该目录 ...
- Mac 端配置 Lua 环境
一.设计目的 Lua 是一种轻量级的脚本语言,其目的是为了嵌入到程序中,从而为程序提供灵活的扩展和定制功能. 二.特性 轻量级:编译后仅仅 100 余K,可以很方便的嵌入到程序中. 可扩展:Lua 提 ...
- 将C注册到lua环境中使用
注册到lua的方式有两种,一种是lua解释器,如果支持动态链接,使用动态链接机制,将函数接口编译成动态链接库,然后将动态链接库放到lua的C路径(LUA_CPATH)中,然后在lua文件中直接使用 r ...
- ubuntu配置lua环境,并进行c与lua的相互调用
1.安装lua环境 先查看一下apt可获取的lua版本 我们选择lua5.1版本进行安装 sudo apt install lua5.1 安装完之后测试一下是否安装成功,如果可以正常使用,则lua环境 ...
随机推荐
- 利用百度云盘API上传文件至百度云盘
一.获取Access Token示例 1. 请您将以下HTTP请求直接粘贴到浏览器地址栏内,并按下回车键. https://openapi.baidu.com/oauth/2.0/authorize? ...
- [Leetcode] Permutation Sequence
The set [1,2,3,…,n] contains a total of n! unique permutations. By listing and labeling all of the p ...
- 【wikioi】2822 爱在心中
题目链接 算法:Tarjan+dfs(最短路的都行,判连通而已) 先了解一下什么是Tarjan Tarjan算法用于求出图中所有的强连通分量. 转自NOCOW:点击打开链接 ============= ...
- linux ps指令
ps axjf <==連同部分程序樹狀態
- Delphi中对BCD码的直接支持 (转)
最近在Delphi下写软件,需要将数据转换为BCD码和将BCD码转换为其它数据类型,从网上搜索了一下,没有发现好的函数,于是就想自定义函数来完成BCD与其它格式的数据转换功能.但最终没有动手写,先查查 ...
- 使用C语言在windows下一口气打开一批网页
作者:郝峰波 mail : fengbohello@qq.com 本博客地址:http://www.cnblogs.com/fengbohello/p/4374450.html 1.核心函数说明 核心 ...
- SQL 联合索引 与 单一列的索引 比较
背景: 公司业务迅速扩展,很多网站.接口都因为大流量的数据,发生服务器习惯性死机:一条sql查询语句只能适用于一定的网络环境,没有优化的查询当遇上大数据时就不适用了. 本文主旨: 讨论什么情况下能利用 ...
- java web(四)文件上传与下载
一.文件上传原理 1.在TCP/IP中,最早出现的文件上传机制是FTP ,它是将文件由客户端发送到服务器的标准机制:但是在jsp使用过程中不能使用FTP方法上传文件,这是由jsp运行机制所决定. 通 ...
- 关于mysql(或MariaDB)中的用户账号格式
之前在修改数据库本地root用户密码时,发现我远程连接的root用户的密码并没有改变,之后查了一下,发现原来这两个root不是同一个用户(汗..) 于是联想到之前配置数据库每次给用户赋予远程连接权限时 ...
- html中标签的含义及作用
链接:http://www.w3chtml.com/html/tag/div.html