目录

lua底层原理浅析

table底层原理浅析

Lua表的C语言定义

原理和实现

userdata

lua和C#的交互机制(更新中)

基本介绍

Lua 与 C/C++ 的数据交互

Lua 调用 C/C++ 函数

C/C++ 调用 Lua 函数

基元类型传递

对象类型传递

Lua 调用 C#

总结

网上有很多文章写和C#的交互原理,但大多比较复杂,截止到目前为止笔者也只是了解了其中的一部分,目前还在边学边做笔记的阶段,所以说是浅析,后续会做更深入的研究。

lua底层原理浅析

Lua的底层实现是基于C语言,这使得它非常轻量级且高效,同时具有很好的跨平台特性。

虚拟机(VM):

Lua使用基于寄存器的虚拟机来执行编译后的字节码。这与基于堆栈的虚拟机相比,可以减少指令数量和执行时间。Lua的字节码是平台无关的,这意味着在一台机器上编译的Lua代码可以在任何其他平台上运行,只要那里有Lua虚拟机。 解释器和编译器:

Lua源代码首先被一个解释器读取,解释器是用C语言编写的。源代码被解析成抽象语法树(AST),然后转换成中间表示,最终编译为字节码。 内存管理:

Lua使用自动垃圾回收机制来管理内存。它主要使用标记-清扫(mark-and-sweep)算法来回收不再使用的内存。

垃圾回收部分具体可以看我的另外一篇文章

Lua的垃圾回收机制详解-CSDN博客

table底层原理浅析

Lua的表(table)是其最强大的特性之一,它们是动态的关联数组,可以用作普通数组、字典、对象等。在C语言中,Lua的表是通过一种复杂的数据结构实现的,这种结构使得表既能高效地存储和访问序列元素,也能高效地处理散列键值对。

Lua表的C语言定义

在Lua的C源码中,表是通过struct定义的,主要是Table结构。这个结构包括指向数组部分和散列部分的指针,以及这些部分的大小等信息。核心结构大致如下(简化版本,用于说明):

typedef struct lua_Table {

CommonHeader;

lu_byte flags; /* 一些标志位 */

lu_byte lsizenode; /* 散列表部分的大小 */

unsigned int sizearray; /* 数组部分的大小 */

TValue *array; /* 数组部分的指针 */

Node *node; /* 散列表部分的指针 */

Node *lastfree; /* 散列表的一个自由位置 */

struct Table *metatable; /* 元表 */

GCObject *gclist;

} Table;

原理和实现

Lua表的实现基于以下原理:

双重结构:

Lua表由两部分组成:一个数组(Array)部分和一个散列(Hash)部分。数组部分用于存储以数字为键的元素,而散列部分用于存储其他类型的键值对。 动态调整:

当你向表中添加或移除元素时,Lua会动态调整这两部分的大小和存储方式,以保持操作的高效性。例如,如果你主要使用数字键,Lua会倾向于扩展数组部分,而减少散列部分。 散列算法:

对于非数字键,Lua使用散列算法来快速定位和存储键值对。Lua的散列算法旨在减少冲突并快速处理查找、插入和删除操作。 数组和散列表的动态重分配:

Lua会根据表的使用情况动态地重分配内部数组和散列结构,以优化内存使用和访问速度。 元表支持:

Lua表还可以有一个元表(metatable),用于定义该表的特殊行为,比如操作符重载或自定义访问方式。

userdata

在Lua编程语言中,userdata 是一种特殊的数据类型,用于表示任何由应用程序或者C语言代码创建的数据对象。userdata 提供了一种将C中的数据和对象暴露给Lua代码的方式,同时保持类型安全和内存管理的控制。

userdata 在Lua中主要有两种形式:

全用户数据(Full userdata):

这是一个指向C数据的指针,Lua只负责存储和传递这个指针,不尝试理解或操作其指向的数据。它是一个黑盒,Lua不知道其内部结构,只是简单地通过指针来引用它。Lua负责管理这些对象的内存生命周期,通常是通过垃圾回收机制。 轻量用户数据(Light userdata):

这也是一个指针,但Lua不管理其指向的内存。轻量用户数据相当于一个裸指针,其生命周期由外部代码控制。它适用于表示轻量级的、生命周期由应用程序控制的对象。

userdata 的主要用途包括:

将C中的对象传递到Lua代码,使得Lua代码可以间接地操作这些对象。在C和Lua之间共享数据,尤其是当需要在Lua脚本中操作C语言中创建的复杂数据结构时。实现C和Lua之间的接口调用,尤其是在嵌入Lua到C/C++应用程序时。

使用 userdata 时,通常会配合元表(metatable)来提供对象的方法和属性,这样Lua代码就可以像操作普通的Lua对象那样操作C中的数据。这是实现面向对象编程风格的一种常用手段,特别是在Lua绑定到C/C++库的上下文中。

lua和C#的交互机制(更新中)

参考文章:

C#与XLua交互原理 - 知乎 (zhihu.com)

基本介绍

Lua 虚拟机:Lua 是由 C/C++ 实现的,因此它可以直接与宿主程序(如Unity)进行通信。C# 与 Lua 交互:C# 通过 P/Invoke(平台调用)方式调用 Lua 虚拟机函数。这意味着 C# 可以通过 C/C++ 层与 Lua 进行数据交互。xLua 中的 P/Invoke:xLua 提供的 P/Invoke 调用接口主要在 LuaDLL.cs 文件中。

Lua 与 C/C++ 的数据交互

虚拟栈:Lua 提供了一个虚拟栈用于数据交换。所有类型的数据交换都通过这个栈完成。栈索引:Lua 有两种索引方式操作虚拟栈 — 正数索引(1表示栈底)和反向索引(-1表示栈顶)。

Lua 调用 C/C++ 函数

包装函数:将C++函数包装成可供Lua调用的格式,通常是接收一个Lua状态机指针的静态方法。注册函数:在Lua环境中注册这些包装好的函数。调用过程:Lua调用时,通过Lua栈获取参数,执行函数,然后将结果压栈返回。

C/C++ 调用 Lua 函数

获取函数:使用 lua_getglobal 获取Lua函数,并将其压入栈。压栈参数:将函数的参数压入栈。执行函数:调用 lua_pcall 执行函数。处理结果:如果无误,从Lua虚拟栈中取出结果。

基元类型传递

直接通过 C API 传递,如 lua_pushboolean, lua_pushnumber 等。

对象类型传递

过程:C# 对象在Lua中通过表(table)模拟,传递的是索引,同时需要将C#类型信息注册到Lua。userdata:C# 对象在 Lua 中对应的是一个 userdata,用于保持与C#对象的联系。元表:为 userdata 设置的元表包含了对象的类型信息,如成员方法、属性等。

Lua 调用 C#

Lua 通过调用 C# 包装好的静态方法来实现调用,这些方法转换Lua的调用为C#函数的调用。使用元表信息来确定要调用的C#方法和属性。函数通过Lua的栈接收参数,参数按顺序入栈。

总结

在 Unity 中,xLua 框架通过 C API 层实现 C# 和 Lua 的交互。Lua 和 C# 之间的调用主要是通过虚拟栈来传递数据和参数。C# 对象在Lua中通过 userdata 表示,而 C# 与 Lua 的函数调用则是通过预先包装好的静态方法来实现。这个过程涉及多个层面的数据转换和类型匹配,但最终实现了两种语言间高效的互操作性。

参考阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: