您现在的位置是:亿华云 > IT科技类资讯
一篇学会好玩的Lua
亿华云2025-10-04 00:26:18【IT科技类资讯】2人已围观
简介本文转载自微信公众号「编程杂技」,作者 theanarkh 。转载本文请联系编程杂技公众号。最近体验了一下Openresty,了解到Openresty里使用lua语言来增强了Nginx的能力,所以又去
本文转载自微信公众号「编程杂技」,篇学作者 theanarkh 。篇学转载本文请联系编程杂技公众号。篇学
最近体验了一下Openresty,篇学了解到Openresty里使用lua语言来增强了Nginx的篇学能力,所以又去了解了一下lua,篇学lua语言小而精悍,篇学lua引擎也值得学习。篇学周末看了一下lua引擎的篇学一些实现,也体验了一下lua语言的篇学一些东西,本文简单介绍一下,篇学后续有时间的篇学话再写文章分析引擎的实现。
1 在c语言中嵌入lua引擎
lua引擎本身是篇学一个库,类似V8一样,篇学我们可以把它嵌入到其他项目中,篇学我们首先安装相关文档安装lua(我安装的是5.1.5)。然后写个demo体验一下。
#include <lua.h> #include <lualib.h> #include <lauxlib.h> #include<stdio.h> int echo(lua_State *L) { printf("world"); } int main(int argc, char *argv[]) { int s = 0; lua_State *L = lua_open(); // 注册个自定义的函数 lua_register(L,"echo", echo); luaL_openlibs(L); // 执行lua脚本 luaL_dofile(L, "hello.lua"); lua_close(L); return 1; }编译上面的代码
gcc hello.c -llua -lm -ldl然后写个hello.lua脚本。
print("hello"); echo();执行./a.out,我们看到输出了hello world。这个是个简单的体验demo,和直接使用lua提供的亿华云计算命令行工具类似,只不过我们这里还拓展了一个自定义的echo函数给lua脚本调用。如果我们想动态地执行一段脚本,而不是执行一个lua文件,也是可以的。
#include <lua.h> #include <lualib.h> #include <lauxlib.h> const char * script = "print(hi);"; int main(int argc, char *argv[]) { lua_State *L = lua_open(); luaL_openlibs(L); luaL_dostring(L, script); lua_close(L); return 1; }编译执行以上代码我们会看到输出hi。以上这些似乎没什么大的作用,因为我们执行简单地使用lua语言提供的能力。而lua的能力绝不止于此,lua称为胶水语言,除了可以嵌入其他语言中,还支持拓展。下面我们看如果拓展lua的能力。
2 基于lua的demo运行时
虽然这里只是简单地拓展lua,但是这里称之为运行时是因为类似Node.js基于V8一样,我们也可以通过拓展lua来实现一个基于lua的运行时。下面我们看看怎么拓展(也就是怎么调用其他语言的代码,这里是高防服务器c)。新建一个test.c文件。
#include <lua.h> #include <lualib.h >#include <lauxlib.h> static int test(lua_State* L){ //取栈第一个参数 const char *a = luaL_checkstring(L, 1); //返回值入栈 lua_pushstring(L, (const char *)"hi"); return 1; } static const struct luaL_Reg reg_test[] = { { "test", test}, { NULL, NULL} }; int luaopen_test(lua_State *L) { const char* libName = "test"; luaL_register(L, libName, reg_test); return 1; }lua和c是通过一个栈进行通信的,lua调用c函数的时候,c函数可以从栈中获取lua的参数,也可也从栈中返回执行结果给lua。我们把以上代码编译成一个动态库。
gcc test.c -fPIC -shared -o test.so然后写个测试lua demo。
local test = require "test" a = test.test("hello world!") print(a)我们可以看到在lua中成功调用了test模块的test函数,并输出hi。当我们require"test"的时候,lua会去当前目录找test.o,并且执行其中的luaopen_test函数。luaopen_前缀是约定,test则是模块名称。当前去哪里找需要加载的模块这个我们可以设置。我们分析一下c文件的代码,看看拓展lua时的一些内容。首先看luaL_register。
LUALIB_API void (luaL_register) (lua_State *L, const char *libname, const luaL_Reg *l) { luaI_openlib(L, libname, l, 0); }我们主要关注luaL_register的第二第三个参数libname和luaL_Reg。源码库因为知道这个参数的格式,我们才知道怎么写c代码。其中name是库名称,也就是我们require时传的字符串。luaL_Reg的定义如下
typedef int (*lua_CFunction) (lua_State *L); typedef struct luaL_Reg { const char *name; lua_CFunction func; } luaL_Reg;luaL_Reg是封装了kv的一个结构体,。name是导出的函数名称,即在lua中可以调用的函数。func则是对应的函数,当在lua执行name函数时就会执行func的代码。
3 lua变量存储的设计
lua是动态类型的语言,意味着一个变量的值的类型是可以改变的,下面看一下lua中是如何设计底层的存储的。lua所有变量都使用TValue结构体来表示。
#define TValuefields Value value; int tt typedef struct lua_TValue { TValuefields; } TValue;里面只有两个字段。tt是表示变量类型。lua的类型比较简单。如下
#define LUA_TNIL 0 #define LUA_TBOOLEAN 1 #define LUA_TLIGHTUSERDATA 2 #define LUA_TNUMBER 3 #define LUA_TSTRING 4 // 数组和对象都使用一种类型 #define LUA_TTABLE 5 #define LUA_TFUNCTION 6 #define LUA_TUSERDATA 7 #define LUA_TTHREAD 8接下来我们看看Value的定义。
typedef union { GCObject *gc; void *p; lua_Number n; int b; } Value;Value里分为两种类型,一种是不需要gc的,比如数字,一种是需要gc的,比如数组,lua是带gc的语言。我们继续看GCObject。
union GCObject { GCheader gch; union TString ts; union Udata u; union Closure cl; struct Table h; struct Proto p; struct UpVal uv; struct lua_State th; /* thread */ };我们看到GCObject是一个联合体,可以存储不同类型的变量。我们再看看TString的定义。
typedef union TString { L_Umaxalign dummy; /* 内存对齐,性能优化 */ struct { CommonHeader; lu_byte reserved; unsigned int hash; size_t len; } tsv; } TString;字符串结构体里面主要的字段时len和hash,len就是字符串的长度,hash类似一个索引,lua中的字符串不是存储在结构体本身的,而是统一管理起来,主要是为了复用,比如有两个变量的值都是同一个字符串,那么lua中,只会存储一个字符串值,而这两个变量都会通过hash指向这个字符串的值。我们可以看一下一段代码大概了解一下。
// 新建字符串,如果存在则直接复用 TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { GCObject *o; unsigned int h = cast(unsigned int, l); /* seed */ size_t step = (l>>5)+1; size_t l1; // 计算字符串的哈希值 for (l1=l; l1>=step; l1-=step) /* compute hash */ h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1])); // 判断是否有一样的字符串存在了,是则共享,直接返回,否则新建 for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)]; o != NULL; o = o->gch.next) { TString *ts = rawgco2ts(o); if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) { if (isdead(G(L), o)) changewhite(o); return ts; } } // 找不到则新建 return newlstr(L, str, l, h); /* not found */ }我们看到lua的变量存储设计中是一种树状结构,通过上层的变量类型,再进行不同的存取操作。从而我们也可以了解到动态语言在变量存储中的一些设计思想。
后记:这是周末学习lua的一些内容,后续有时间会继续更新,lua是一个非常有意思的项目。
很赞哦!(125)
相关文章
- 5. 四种状态过后,域名管理机构释放域名给公众注册。
- 太全了!用Python操作MySQL的使用教程集锦!
- 聊聊 Spring 事务控制策略以及 @Transactional 失效问题避坑
- 分布式链路追踪Jaeger + 微服务Pig在Rainbond上的实践分享
- 顶级域名可以增加企业品牌的价值。随着经济的快速发展,域名已不再是企业在网络中的独立地位。顶级域名的服务范围、企业产品、综合形象体现等,对于企业单位来说,顶级域名的重要性不言而喻。
- 一篇让你学会哈希表(散列)
- 接口流量突增,如何做好性能优化?
- 都在推荐 TS,有必要学习吗?该怎么学?
- 2. 不要花大价钱买域名,新手鉴别能力不足,容易投资失误。
- 代码简洁之道:统一返回格式