Lua编程

文章目录

概述

这次是skynet,需要一些lua/c相关的。写一篇博客,记录下。希望有所收获。

lua数据类型

  • boolean , number , string , nil , function , table , userdata , lightuserdata , thread ;
    boolean 为 true 、 false ;其中 false 可以解决 table 作为 array 时,元素为 nil 时造成
    table 取长度未定义的行为;
  • number 为 integer 和 double 的总称;
  • string 常量字符串;这样 lua 中字符串比较只需要进行地址比较就行了;
  • nil 通常表示未定义或者不存在两种语义;
  • function 函数;与其他语言不同的是,lua 中 function 为第一类型;注意 lua 中的匿名函
    数,lua 文件可视为一个匿名函数;加载 lua 文件,可视为执行该匿名函数;
  • table 表;lua 中唯一的数据结构;既可以表示 hashtable 也可表示为 array;配合元表可以定制
    表复杂的功能(如实现面对对象编程中的类以及相应继承的功能);
  • userdata 完全用户数据;指向一块内存的指针,通过为 userdata 设置元表,lua 层可以使用
    该 userdata 提供的功能; userdata 为 lua 补充了数据结构,解决了 lua 数据结构单一的问
    题;可以在 c 中实现复杂的数据结构,生成库继而导出给 lua 使用;注意: userdata 指向的内存
    需要由 lua 创建,同时 userdata 的销毁也交由 lua gc 来自动回收;
  • lightuserdata 轻量用户数据;也是指向一块内存的指针,但是该内存由 c 创建,同时它的销毁
    也由 c 来完成;不能为它创建元表,轻量用户数据只有类型元表;通常用于 lua 想使用 c 的结构,但是不能让 lua 来释放的结构;在游戏客户端中用的比较多;
  • thread 线程;lua 中的协程和虚拟机都是 thread 类型;

元表

常用的有:

  • __index :索引 table[key] 。 当 table 不是表或是表 table 中不存在 key 这个键时,这个
    事件被触发。 此时,会读出 table 相应的元方法。
  • __newindex :索引赋值 table[key] = value 。 和索引事件类似,它发生在 table 不是表或
    是表 table 中不存在 key 这个键的时候。 此时,会读出 table 相应的元方法。
  • __gc :元表中用一个以字符串 " __gc " 为索引的域,那么就标记了这个对象需要触发终结器;

这些常用,是语言层次的,不区分客户端,服务器。

注意

  • 只有 table 和 userdata 对象有独自的元表,其他类型只有类型元表;
  • 只有 table 可以在 lua 中修改设置元表;
  • userdata 只能在 c 中修改设置 元表,lua 中不能修改 userdata 元表;

闭包

表现

  • 函数内部可以访问函数外部的变量;
  • lua 文件是一个匿名函数;
    lua内部函数可以访问文件中函数体外的变量;

实现

  • C 函数以及绑定在 C 函数上的上值(upvalues);

lua/c 接口编程

skynet、openresty 都是深度使用 lua 语言的典范;学习 lua 不仅仅要学习基本用法,还要学会使

用 c 与 lua 交互,这样才学会了 lua 作为胶水语言的精髓;

skynet中调用层次

虚拟栈

  • 栈中只能存放 lua 类型的值,如果想用 c 的类型存储在栈中,需要将 c 类型转换为 lua 类型;
  • lua 调用 c 的函数都得到一个新的栈,独立于之前的栈;
  • c 调用 lua,每一个协程都有一个栈;
  • c 创建虚拟机时,伴随创建了一个主协程,默认创建一个虚拟栈;
  • 无论何时 Lua 调用 C , 它都只保证至少有 LUA_MINSTACK 这么多的堆栈空间可以使用。
    LUA_MINSTACK 一般被定义为 20 , 因此,只要你不是不断的把数据压栈, 通常你不用关心堆栈大小。

C闭包

  • 通过 lua_pushcclosure 用来创建 C 闭包;
  • 通过 lua_upvalueindex 伪索引来获取上值(lua 值);
  • 可以为多个导出函数(c 导出函数给 lua 使用)共享上值,这样可以少传递一个参数;

注册表

可以用来在多个 c 库中共享 lua 数据(包括 userdata 和 lightuserdata );

  • 一张预定义的表,用来保存任何 c 代码想保存的 lua 值;
  • 使用 LUA_REGISTRYINDEX 来索引;

userdata

  • userdata 是指向一块内存的指针,该内存由 lua 来创建,通过 void
    *lua_newuserdatauv(lua_State *L, size_t sz, int nuvalue) 这个函数来创建;注意:这
    块内存大小必须是固定的,不能动态增加,但是这块内存中的指针指向的数据可以动态增加;还有
    就是 userdata 可以绑定若干个 lua 值(又称uservalue)(在 lua 5.3 中只能绑定一个 lua 值,
    lua 5.4 可以绑定多个); userdata 与 uservalue 的关系是引用关系,也就是 uservalue 的生命周
    期与 userdata 的生命周期一致, userdata gc 时,uservalue 也会被释放;通常这个特性可以
    用来绑定一个 lua table 结构,因为 c 中没有 hash 结构,辅助 lua table 结构实现复杂的功
    能;也可以用来实现延迟 gc,如果某个 userdata 希望晚点 gc,在 userdata 的 __gc 元表中
    生成一个临时的 userdata ,然后将那个希望晚点 gc 的 userdata 绑定在这个临时 userdata
    的 uservalue 上;
  • int lua_getiuservalue (lua_State *L, int idx, int n) 来获取绑定在 userdata 上的
    uservalue;
  • int lua_setiuservalue (lua_State *L, int idx, int n) 来设置 userdata 上的
    uservalue;

lightuserdata

轻量用户数据也是指向一块内存的指针,但是该内存由 c 来创建和销毁;通常这块内存的生命周期

由 c 宿主语言来控制;可以将 lightuserdata 绑定在注册表中,让多个 lua 库共享该数据;在

skynet 中, lightuserdata 可以指向同一块数据,在多个 Actor 中传递这个 lightuserdata ,

然后分别为这个 lightuserdata 创建一个 userdata ; 在 userdata 中的 __gc 来释放这个

lightuserdata ;注意:为了避免这块内存多次释放,需要为这块内存加上引用计数;同时

skynet 中 actor 是多线程环境下运行,所以需要为该 lightuserdata 加上锁;这个锁必须是自

旋锁或者原子操作,因为 actor 调度是自旋锁,必须使用比它更小的粒度的锁;如果

lightuserdata 操作粒度过大,应该改成只在一个 actor 中加载,其他 actor 通过消息来共享数

据;

小结

这一篇lua编程不同于之前的lua源码阅读,写那一篇的时候,主要是在写一些源码中的内容;这一篇,包括各种数据结构,c,以及lua的。感兴趣,都可以看看,也可以一起学习学习。OK,这篇结束。

相关推荐
Swift社区2 小时前
在 Swift 中实现字符串分割问题:以字典中的单词构造句子
开发语言·ios·swift
没头脑的ht2 小时前
Swift内存访问冲突
开发语言·ios·swift
没头脑的ht2 小时前
Swift闭包的本质
开发语言·ios·swift
wjs20243 小时前
Swift 数组
开发语言
stm 学习ing4 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
湫ccc4 小时前
《Python基础》之字符串格式化输出
开发语言·python
mqiqe5 小时前
Python MySQL通过Binlog 获取变更记录 恢复数据
开发语言·python·mysql
AttackingLin5 小时前
2024强网杯--babyheap house of apple2解法
linux·开发语言·python