当前位置:首页 > 通信资讯 > 正文

关于好玩的作文(有没有什么好玩好学的)

关于好玩的作文(有没有什么好玩好学的)

最近体验了一下Openresty,了解到Openresty里使用lua语言来增强了Nginx的能力,所以又去了解了一下lua,lua语言小而精悍,lua引擎也值得学习。周末看了一下lua引擎的一些实现,也体验了一下lua语言的一些东西,本文简单介绍一下,后续有时间的话再写文章分析引擎的实现。

1 在c语言中嵌入lua引擎

lua引擎本身是一个库,类似V8一样,我们可以把它嵌入到其他项目中,我们首先安装相关文档安装lua(我安装的是5.1.5)。然后写个demo体验一下。

  1. #include<lua.h>
  2. #include<lualib.h>
  3. #include<lauxlib.h>
  4. #include<stdio.h>
  5. intecho(lua_State*L){
  6. printf("world");
  7. }
  8. intmain(intargc,char*argv[]){
  9. ints=0;
  10. lua_State*L=lua_open();
  11. //注册个自定义的函数
  12. lua_register(L,"echo",echo);
  13. luaL_openlibs(L);
  14. //执行lua脚本
  15. luaL_dofile(L,"hello.lua");
  16. lua_close(L);
  17. return1;
  18. }

编译上面的代码

  1. gcchello.c-llua-lm-ldl

然后写个hello.lua脚本。

  1. print("hello");
  2. echo();

执行./a.out,我们看到输出了hello world。这个是个简单的体验demo,和直接使用lua提供的命令行工具类似,只不过我们这里还拓展了一个自定义的echo函数给lua脚本调用。如果我们想动态地执行一段脚本,而不是执行一个lua文件,也是可以的。

  1. #include<lua.h>
  2. #include<lualib.h>
  3. #include<lauxlib.h>
  4. constchar*script="print('hi');";
  5. intmain(intargc,char*argv[]){
  6. lua_State*L=lua_open();
  7. luaL_openlibs(L);
  8. luaL_dostring(L,script);
  9. lua_close(L);
  10. return1;
  11. }

编译执行以上代码我们会看到输出hi。以上这些似乎没什么大的作用,因为我们执行简单地使用lua语言提供的能力。而lua的能力绝不止于此,lua称为胶水语言,除了可以嵌入其他语言中,还支持拓展。下面我们看如果拓展lua的能力。

2 基于lua的demo运行时

虽然这里只是简单地拓展lua,但是这里称之为运行时是因为类似Node.js基于V8一样,我们也可以通过拓展lua来实现一个基于lua的运行时。下面我们看看怎么拓展(也就是怎么调用其他语言的代码,这里是c)。新建一个test.c文件。

  1. #include<lua.h>
  2. #include<lualib.h
  3. >#include<lauxlib.h>
  4. staticinttest(lua_State*L){
  5. //取栈第一个参数
  6. constchar*a=luaL_checkstring(L,1);
  7. //返回值入栈
  8. lua_pushstring(L,(constchar*)"hi");
  9. return1;
  10. }
  11. staticconststructluaL_Regreg_test[]={
  12. {"test",test},
  13. {NULL,NULL}
  14. };
  15. intluaopen_test(lua_State*L){
  16. constchar*libName="test";
  17. luaL_register(L,libName,reg_test);
  18. return1;
  19. }

lua和c是通过一个栈进行通信的,lua调用c函数的时候,c函数可以从栈中获取lua的参数,也可也从栈中返回执行结果给lua。我们把以上代码编译成一个动态库。

  1. gcctest.c-fPIC-shared-otest.so

然后写个测试lua demo。

  1. localtest=require"test"
  2. a=test.test("helloworld!")
  3. print(a)

我们可以看到在lua中成功调用了test模块的test函数,并输出hi。当我们require"test"的时候,lua会去当前目录找test.o,并且执行其中的luaopen_test函数。luaopen_前缀是约定,test则是模块名称。当前去哪里找需要加载的模块这个我们可以设置。我们分析一下c文件的代码,看看拓展lua时的一些内容。首先看luaL_register。

  1. LUALIB_APIvoid(luaL_register)(lua_State*L,constchar*libname,constluaL_Reg*l){
  2. luaI_openlib(L,libname,l,0);
  3. }

我们主要关注luaL_register的第二第三个参数libname和luaL_Reg。因为知道这个参数的格式,我们才知道怎么写c代码。其中name是库名称,也就是我们require时传的字符串。luaL_Reg的定义如下

  1. typedefint(*lua_CFunction)(lua_State*L);
  2. typedefstructluaL_Reg{
  3. constchar*name;
  4. lua_CFunctionfunc;
  5. }luaL_Reg;

luaL_Reg是封装了kv的一个结构体,。name是导出的函数名称,即在lua中可以调用的函数。func则是对应的函数,当在lua执行name函数时就会执行func的代码。

3 lua变量存储的设计

lua是动态类型的语言,意味着一个变量的值的类型是可以改变的,下面看一下lua中是如何设计底层的存储的。lua所有变量都使用TValue结构体来表示。

  1. #defineTValuefieldsValuevalue;inttt
  2. typedefstructlua_TValue{
  3. TValuefields;
  4. }TValue;

里面只有两个字段。tt是表示变量类型。lua的类型比较简单。如下

  1. #defineLUA_TNIL0
  2. #defineLUA_TBOOLEAN1
  3. #defineLUA_TLIGHTUSERDATA2
  4. #defineLUA_TNUMBER3
  5. #defineLUA_TSTRING4
  6. //数组和对象都使用一种类型
  7. #defineLUA_TTABLE5
  8. #defineLUA_TFUNCTION6
  9. #defineLUA_TUSERDATA7
  10. #defineLUA_TTHREAD8

接下来我们看看Value的定义。

  1. typedefunion{
  2. GCObject*gc;
  3. void*p;
  4. lua_Numbern;
  5. intb;
  6. }Value;

Value里分为两种类型,一种是不需要gc的,比如数字,一种是需要gc的,比如数组,lua是带gc的语言。我们继续看GCObject。

  1. unionGCObject{
  2. GCheadergch;
  3. unionTStringts;
  4. unionUdatau;
  5. unionClosurecl;
  6. structTableh;
  7. structProtop;
  8. structUpValuv;
  9. structlua_Stateth;/*thread*/
  10. };

我们看到GCObject是一个联合体,可以存储不同类型的变量。我们再看看TString的定义。

  1. typedefunionTString{
  2. L_Umaxaligndummy;/*内存对齐,性能优化*/
  3. struct{
  4. CommonHeader;
  5. lu_bytereserved;
  6. unsignedinthash;
  7. size_tlen;
  8. }tsv;
  9. }TString;

字符串结构体里面主要的字段时len和hash,len就是字符串的长度,hash类似一个索引,lua中的字符串不是存储在结构体本身的,而是统一管理起来,主要是为了复用,比如有两个变量的值都是同一个字符串,那么lua中,只会存储一个字符串值,而这两个变量都会通过hash指向这个字符串的值。我们可以看一下一段代码大概了解一下。

  1. //新建字符串,如果存在则直接复用
  2. TString*luaS_newlstr(lua_State*L,constchar*str,size_tl){
  3. GCObject*o;
  4. unsignedinth=cast(unsignedint,l);/*seed*/
  5. size_tstep=(l>>5)+1;
  6. size_tl1;
  7. //计算字符串的哈希值
  8. for(l1=l;l1>=step;l1-=step)/*computehash*/
  9. h=h^((h<<5)+(h>>2)+cast(unsignedchar,str[l1-1]));
  10. //判断是否有一样的字符串存在了,是则共享,直接返回,否则新建
  11. for(o=G(L)->strt.hash[lmod(h,G(L)->strt.size)];
  12. o!=NULL;
  13. o=o->gch.next){
  14. TString*ts=rawgco2ts(o);
  15. if(ts->tsv.len==l&&(memcmp(str,getstr(ts),l)==0)){
  16. if(isdead(G(L),o))changewhite(o);
  17. returnts;
  18. }
  19. }
  20. //找不到则新建
  21. returnnewlstr(L,str,l,h);/*notfound*/
  22. }

我们看到lua的变量存储设计中是一种树状结构,通过上层的变量类型,再进行不同的存取操作。从而我们也可以了解到动态语言在变量存储中的一些设计思想。

后记:这是周末学习lua的一些内容,后续有时间会继续更新,lua是一个非常有意思的项目。

原文链接:https://mp.weixin.qq.com/s/hl3Kv5vsP95Qn9KTjlTYTg

如果您对该产品感兴趣,请填写办理(客服微信:xiaoxiongyidong)

为您推荐:

发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。