一篇学会好玩的Lua

休闲2025-11-05 12:47:2457565

本文转载自微信公众号「编程杂技」,篇学作者 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的运行时。下面我们看看怎么拓展(也就是怎么调用其他语言的代码,这里是云南idc服务商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。IT技术网因为知道这个参数的格式,我们才知道怎么写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是一个非常有意思的项目。

本文地址:http://bhae.cn/html/975d29098734.html
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

全站热门

inux/Ubuntu系统用的时间久了,经常会有一些系统更新,除了一些系统补丁、工具补丁、工具升级之外,内核也经常对一些问题进行修改升级,这样就会产生一些新的内核,我们更新了新的内核后,就会自动帮我们添加到开机启动菜单选项中,那些旧的内核启动项,对于我们来说已经没有用了,可以删除,删除后可以让我们在开机的时候能快速的选择要启动的菜单项,同时也能清理出一些磁盘空间出来,小编下面就来分享如何删除多余的内核启动菜单项注意:小编的这个建议只适合于10.10之前版本的ubuntu系统,后面的11.04,11.10。。。14.04系统因为采用的默认桌面不同,所以本经验不适用,若要使用,那么请安装GNOME桌面  1、打开“应用程序”,然后在弹出菜单中点击“附件”,选择并打开“终端”,打开终端命令窗口2、在终端命令窗口中输入并执行下面的命令:uname -a,查看我们当前使用的内核,不要删除错误了,或者当我们使用的是旧内核进入系统,想删除这个内核启动项,这样也是不能删除的。只能在开机时选择别的内核启动项进入系统才能删除3、输入下面的命令,查看当前我们系统中有哪些内核启动项,同时也可以方便我们复制的操作,具体命令是:dpkg --get-selections | grep linux4、下面就开始删除内核启动项,先选择内核启动项,然后复制,这样方便操作,然后输入下面的命令:sudo apt-get remove linux-image-2.6.32.21-generic5、执行过程中会警告我们确实要执行这个操作,输入字母y,然后回车,就开始卸载旧内核了6、之后就是一些具体的卸载过程的提示,已经更新系统启动项的信息7、我们再次使用命令:dpkg --get-selections | grep linux 来查看当前的内核情况:在 linux-image-2.6.32.21-generic 后面显示deinstall,表示已经卸载了linux-headers-2.6.32.21和 linux-headers-2.6.32.21-generic是之前我们卸载的内核相关联的头文件,现在对于我们也没有用,也可以卸载了,卸载了可以帮我们清理出不少磁盘空间,具体命令是:sudo apt-get remove linux-headers-2.6.32-21sudo apt-get remove linux-headers-2.6.32-21-generic8、删除完后,再用命令:dpkg --get-selections | grep linux 查看当前内核启动选项相关信息:linux-headers-2.6.32-21 已经没有了,被删除了linux-headers-2.6.32-21-generic已经没有了,被删除了linux-image-2.6.32-21-generic 显示为deinstall,表示被卸载删除了9、 虽然在卸载旧内核的时候提示信息说已经帮我们更新了系统启动项,但为了保险起见我们执行sudo update-grub更新开机启动控制文件。

电子商务如何在2024年重新定义购物体验?

TIOBE 12 月榜单:PHP 跌出前十,C# 或将成年度语言

域名注册能选择哪些后缀?

特斯拉Model3(颠覆传统,引领未来)

面试官:重写 equals 时为什么一定要重写 hashCode?

通用顶级域名和新通用顶级域名的区别

购买域名后可以不使用嘛?

友情链接

滇ICP备2023000592号-9