C/C++和Lua是如何进行通信的? http://www.luachina.cn/?post=38

2015-12-28

为了实现Lua和其他语言之间的通信,Lua虚拟机为C/C++提供了两个特性:

一,Lua_State状态机

lua_State主要是管理一个lua虚拟机的执行环境, 一个lua虚拟机可以有多个执行环境。Lua虚拟机通过维护这样一个虚拟栈来实现两种之间的通信,lua_State定义如下:

1,虚拟栈的管理, 包括管理整个栈和当前函数使用的栈的情况
        2,CallInfo的管理, 包括管理整个CallInfo数组和当前函数的CallInfo
        3,hook相关的, 包括hookmask, hookcount, hook函数等
        4,global_State是全局唯一的,存放多个lua_State之间的一些共享数据
        5,gc的一些管理和当前栈中upvalue的管理
        6,错误处理的支持等等
        C/C++和Lua拥有不同的数据类型,要实现两者之间的数据通信怎么办?Lua虚拟机提供Lua_State这样一种数据结构。任何一种数据从C/C++传入Lua虚拟机中,Lua都会将这类数据转换为一种通用的结构lua_TValue,并且将数据复制一份,将其压入虚拟栈中。lua_TValue定义如下:
 
 
Lua有自己的GC,C/C++由自己申请和释放内存,所以两者之间的内存管理是独立的。从C/C++中传递数据到Lua虚拟机会发生数据拷贝,从Lua虚拟机中传递出来是直接从虚拟栈中取值或者地址,所以数据从虚拟栈中pop之后,是否依然是有效引用需要额外注意。
二,C API

Lua脚本实现交互提供了一系列的C API,常用API有:

luaL_newstate函数用于初始化一个lua_State实例

luaL_openlibs函数用于打开Lua中的所有标准库,如io库、string库等。

luaL_loadbuffer编译了buff中的Lua代码,如果没有错误,则返回0,同时将编译后的程序块压入虚拟栈中。

lua_pcall函数会将程序块从栈中弹出,并在保护模式下运行该程序块。执行成功返回0,否则将错误信息压入栈中。

lua_tostring函数中的-1,表示栈顶的索引值,栈底的索引值为1,以此类推。该函数将返回栈顶的错误信息,但是不会将其从栈中弹出。

lua_pop是一个宏,用于从虚拟栈中弹出指定数量的元素,这里的1表示仅弹出栈顶的元素。

lua_close用于释放状态指针所引用的资源。

入栈操作:

Lua针对每种C类型,都有一个C API函数与之对应,如:

void lua_pushnil(lua_State* L);  --nil值

void lua_pushboolean(lua_State* L, int b); --布尔值

void lua_pushnumber(lua_State* L, lua_Number n); --浮点数

void lua_pushinteger(lua_State* L, lua_Integer n);  --整型

void lua_pushlstring(lua_State* L, const char* s, size_t len); --指定长度的内存数据

void lua_pushstring(lua_State* L, const char* s);  --以零结尾的字符串,其长度可由strlen得出。

出栈操作:

API使用“索引”来引用栈中的元素,第一个压入栈的为1,第二个为2,依此类推。我们也可以使用负数作为索引值,其中-1表示为栈顶元素,-2为栈顶下面的元素,同样依此类推。

Lua提供了一组特定的函数用于检查返回元素的类型,如:

int lua_isboolean (lua_State *L, int index);

int lua_iscfunction (lua_State *L, int index);

int lua_isfunction (lua_State *L, int index);

int lua_isnil (lua_State *L, int index);

int lua_islightuserdata (lua_State *L, int index);

int lua_isnumber (lua_State *L, int index);

int lua_isstring (lua_State *L, int index);

int lua_istable (lua_State *L, int index);

int lua_isuserdata (lua_State *L, int index);

以上函数,成功返回1,否则返回0。需要特别指出的是,对于lua_isnumber而言,不会检查值是否为数字类型,而是检查值是否能转换为数字类型。

如何在Lua与C/C++之间实现table数据的交换 http://www.luachina.cn/?post=37

之前在《C/C++和Lua是如何进行通信的?》一文中简单的介绍了lua与宿主之间的通信。简单的说两种不同的语言之间数据类型不一样又如何进行数据交换呢?那就是lua_State虚拟栈,通过栈操作和lua库函数,我们很轻松就能完成两者之间的数据交换。

开始之前,明确几个问题,lua中的虚拟栈的索引编号问题(我们假设栈大小为n),编号1是栈底,n视栈顶,编号-1是栈顶,-n是栈底。lua中的库函数需要访问和操作栈上的数据都是通过索引编号定位的。但是我们需要明确一点,有些API并没有使用索引编号作为参数,意味着默认对栈顶进行操作。如lua_pushnumber(L, 66)将数值66压入栈顶,lua_tonumber(L, -1)取编号-1(栈顶)元素等等,如果这些基本知识和API的都已经熟悉了,那么lua与宿主之间的数据交换就很容易理解了。

姿势准备好了,那么问题来了。需求:我们现在要设计一个UI界面,我们希望这个UI是可以重用的。为了满足这个需求,显然我们必须将UI界面与显示数据分离。使用lua初始化数据后,将数据传递给UI界面然后显示。这样如果需求变更(游戏开发中经常产生这样的需求),我们也只需改变lua脚本就能重用UI界面,听上去真是程序猿的福音啊~~

用于显示UI的数据必定很多,需要使用lua中的table来封装这些数据,现在给定如下lua table数据:

我们的脚本将调用一个程序封装好的c API(TestTable函数),然后将tTest作为参数,压入虚拟栈中,如下:

 
虽然tTest table已经传给了程序,我们还需要对TestTable这个c API进行定制,使它能够正确的理解这个table中的数据,实现代码如下(LuaTestTable函数类型是lua_CFuntion类型,注册到lua虚拟机中的函数名为TestTable):
 
 

需要说明的是在lua中tTest["gdp"]和tTest.gdp的调用形式是一样的,这是lua的语法糖。当然操作栈中的table方法除了lua_gettable和lua_settable还有其它方法,请参看lua_rawget和lua_rawset。理解栈中的元素变化是非常重要的。

LuaTestTable函数API的后面部分介绍了构造一个任意table作为返回值,返回给lua脚本。首先使用lua_newtable库函数新建一个table类型的数据,并压入栈。然后将键值对key-value依次压入栈,调用lua_settable(L, index)将key-value设置到table中,子table操作也是一样的(这里的index指的是要设置的table在栈中的索引编号)。

好了,基本介绍完了,最后来编写脚本,看看效果(以下是程序调用的脚本):

openresty/lua-nginx-module: Embed the Power of Lua into NGINX HTTP servers https://github.com/openresty/lua-nginx-module#description

Lua | NGINX https://www.nginx.com/resources/wiki/modules/lua/

This module embeds Lua, via LuaJIT 2.0/2.1, into Nginx and by leveraging Nginx's subrequests, allows the integration of the powerful Lua threads (Lua coroutines) into the Nginx event model.

Unlike Apache's mod_lua and Lighttpd's mod_magnet, Lua code executed using this module can be 100% non-blocking on network traffic as long as the Nginx API for Lua provided by this module is used to handle requests to upstream services such as MySQL, PostgreSQL, Memcached, Redis, or upstream HTTP web services.

用lua扩展你的Nginx(整理) - gccbuaa - 博客园 https://www.cnblogs.com/gccbuaa/p/7018287.html

原理
ngx_lua将Lua嵌入Nginx,能够让Nginx运行Lua脚本,而且高并发、非堵塞的处理各种请求。Lua内建协程。这样就能够非常好的将异步回调转换成顺序调用的形式。ngx_lua在Lua中进行的IO操作都会托付给Nginx的事件模型。从而实现非堵塞调用。开发人员能够採用串行的方式编敲代码,ngx_lua会自己主动的在进行堵塞的IO操作时中断。保存上下文;然后将IO操作托付给Nginx事件处理机制。在IO操作完毕后,ngx_lua会恢复上下文,程序继续运行,这些操作都是对用户程序透明的。

每一个NginxWorker进程持有一个Lua解释器或者LuaJIT实例,被这个Worker处理的全部请求共享这个实例。

每一个请求的Context会被Lua轻量级的协程切割,从而保证各个请求是独立的。 ngx_lua採用“one-coroutine-per-request”的处理模型。对于每一个用户请求,ngx_lua会唤醒一个协程用于执行用户代码处理请求,当请求处理完毕这个协程会被销毁。

每一个协程都有一个独立的全局环境(变量空间),继承于全局共享的、仅仅读的“comman data”。所以。被用户代码注入全局空间的不论什么变量都不会影响其它请求的处理。而且这些变量在请求处理完毕后会被释放,这样就保证全部的用户代码都执行在一个“sandbox”(沙箱),这个沙箱与请求具有同样的生命周期。 得益于Lua协程的支持。ngx_lua在处理10000个并发请求时仅仅须要非常少的内存。依据測试,ngx_lua处理每一个请求仅仅须要2KB的内存,假设使用LuaJIT则会更少。所以ngx_lua非常适合用于实现可扩展的、高并发的服务。

C/C++ Lua通信的更多相关文章

  1. 将硬件规定的通信协议用Lua实现(涉及到很多Lua通信的数据转换)

    1:这次处理的是大唐的gps通信协议,先简单介绍一下他规定的通信规则: 信息结构: 传输说明: 信息结构中的各个字节书写时都是以十六进制标识,两位数组成.传输时,SOI和EOI(SOI=7EH,EOI ...

  2. cocos2d-x 使用Lua

    转自:http://www.benmutou.com/blog/archives/49 1. Lua的堆栈和全局表 我们来简单解释一下Lua的堆栈和全局表,堆栈大家应该会比较熟悉,它主要是用来让C++ ...

  3. Lua与C

    [前言] 对于Lua的基础总结总算告一段落了,从这篇博文开始,我们才真正的进入Lua的世界,一个无聊而又有趣的世界.来吧. Lua语言是一种嵌入式语言,它本身的威力有限:当Lua遇见了C,那它就展示了 ...

  4. lua 5.3 英文手册 google机器翻译版

    LUA Lua 5.3参考手册作者:Roberto Ierusalimschy,Luiz Henrique de Figueiredo,Waldemar Celes 版权所有©2015-2018 Lu ...

  5. 高速掌握Lua 5.3 —— Lua与C之间的交互概览

    Q:什么是Lua的虚拟栈? A:C与Lua之间通信关键内容在于一个虚拟的栈.差点儿全部的调用都是对栈上的值进行操作,全部C与Lua之间的数据交换也都通过这个栈来完毕.另外,你也能够使用栈来保存暂时变量 ...

  6. lua 与 c 的相互调用

    Lua是一个嵌入式的语言,意味着Lua不仅可以是一个独立运行的程序包也可以是一个用来嵌入其他应用的程序库. Lua可以作为程序库用来扩展应用的功能,也就是Lua可以作为扩展性语言的原因所在.同时,Lu ...

  7. 怎样在Cocos2d-x中使用Lua脚本

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/u013321328/article/details/25699545 笔者使用的是Cocos2d-x ...

  8. Lua C++交互 应用实例步骤(UserData使用)

    一.配置Lua C++交互环境 1.下载Lua 包环境 地址: https://www.lua.org/download.html ,我们这里用的是5.4.2版本. 2.新建C++ 控制台应用程序 3 ...

  9. fatal error LNK1104: 无法打开文件“lua51.lib”

    今天学习C++与Lua通信,遇到了问题:fatal error LNK1104: 无法打开文件"lua51.lib" 开发环境: VS2012 cocos版本:cocos2d-x- ...

随机推荐

  1. Kafka Eagle 管理平台

    Kafka-Eagle简介 源代码地址:https://github.com/smartloli/kafka-eagle Kafka Eagle是什么 Kafka Eagle是一款用于监控和管理Apa ...

  2. .Net Core 审计日志实现

    前言: 近日在项目协同开发过程中出现了问题,数据出现了异常:其他人员怀疑项目数据丢失程序存在问题.于是通过排查程序提供的审计日志最终还原了当时操作及原因. 可见审计日志在排查.定位问题是相当有用的,那 ...

  3. Spring Data JPA简介 Spring Data JPA特点

    Spring Data JPA 是Spring基于ORM框架.JPA规范的基础上封装的一套JPA 应用框架,底层使用了Hibernate 的JPA技术实现,可使开发者用极简的代码即可实现对数据的访问和 ...

  4. java 深拷贝与浅拷贝机制详解

    概要: 在Java中,拷贝分为深拷贝和浅拷贝两种.java在公共超类Object中实现了一种叫做clone的方法,这种方法clone出来的新对象为浅拷贝,而通过自己定义的clone方法为深拷贝. (一 ...

  5. SQL Server中模式(schema)、数据库(database)、表(table)、用户(user)之间的关系

    数据库的初学者往往会对关系型数据库模式(schema).数据库(database).表(table).用户(user)之间感到迷惘,总感觉他们的关系千丝万缕,但又不知道他们的联系和区别在哪里,对一些问 ...

  6. 移动端学习之理解WEB APP、Native APP、Hybrid APP以及React Native/uniapp包括H5、小程序等的区别与共通之处

    因为工作需要,需要进一步了解移动端的开发,遂返回复习移动端的知识点,在开始学习之前,产生了疑惑WEB APP .Native APP .Hybrid APP.React Native.Uniapp.H ...

  7. day119:MoFang:宠物的状态改动&宠物粮道具的使用&宠物死亡处理

    目录 1.宠物的状态改动 2.宠物粮道具的使用 3.宠物死亡处理 1.宠物的状态改动 1.在setting表中为每个宠物配置生命周期时间 因为宠物有多个,每个宠物会有不同的初始生命的饥饿时间,所以我们 ...

  8. 用python做youtube自动化下载器 思路

    目录 0. 思路 1.准备 i.savfrom.net 2. 探索并规划获取方式 i.总览 ii. 获取该网页取到下载url的请求 iii. 在本地获取请求 iv.解析请求结果 v.解析解密后的结果 ...

  9. 【JDBC核心】commons-dbutils

    commons-dbutils 简介 commons-dbutils 是 Apache 组织提供的一个开源 JDBC 工具类库,它是对 JDBC 的简单封装,学习成本极低,并且使用 commons-d ...

  10. 从Java的字符串池、常量池理解String的intern()

    前言 逛知乎遇到一个刚学Java就会接触的字符串比较问题: 通常,根据"==比较的是地址,equals比较的是值"介个定理就能得到结果.但是String有些特殊,通过new Str ...