微信号:codedumpnote

介绍:写作是最好的记录,分享是最好的学习.

Lua语言的前世今生

2016-05-25 18:13 codedump

由于编写Lua源码分析一书的缘故,需要查看一下Lua语言的发展史。其中两个问题在这之前我就很好奇:

  1. Lua于1993年发布,为什么过了20年,仍然能保持非常精简的语言特性以及实现,作者是如何取舍的?

  2. Lua诞生于巴西,一个第三世界的发展中国家,经历这些年成长为世界级别的开源项目,这背后有什么故事以及启发?


Lua语言于1993年诞生于巴西里约热内卢Pontifical Catholic大学(简称PUC-Rio)的Tecgraf实验室,作者是Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and Waldemar Celes三人。Tecgraf实验室创立于1987年,实验室主要专注与图形图像相关的工具研发上。在创立之后,该实验室的工作就是向客户提供基本的图形相关的软件工具,比如图形库、图形终端等。


从1977年到1992年,巴西政府对电脑软硬件进行了严格的管制措施,要求生产和提供本国出品的软硬件。在这种大环境下,Tecgraf实验室的很多客户由于政治和经济上的原因,都不能向国外公司购买定制化的软件。这些原因都驱使Tecgraf实验室的工作人员从头开始构建面向本国用户的软件工具。


Petrobras(巴西石油公司)是Tecgraf的最大客户之一,Tecgraf为其开发了两门语言,分别是DEL和SOL,这两门语言是Lua语言的前身。


Petrobras的工程师每天要处理的一个问题,是输入大量的数据文件到数值模拟器上,每个文件有行和列,有点类似今天的Excel文件。这个过程琐碎且烦杂,因为模拟程序会对数据进行严格的检查,人工输入数据时常会导致错误。Petrobras向Tecgraf实验室提出需求,要求他们开发用于输入这类型数据的图形终端给工程师们使用,这些终端的输入是可交互的,可以由使用者定义规则,然后自动化的生成正确的模拟器能识别的数据。除了能按照规则生成数据之外,这个终端还需要能提供数据校验等功能。


为了简化这个终端的开发,Luiz Henrique de Figueiredo和Luiz Cristovao Gomes Coelho决定为这个终端开发一种称为DEL(data-entry language)的语言,DEL更像现在的DSL(domain-specific language)。


DEL在Petrobras获得了广泛的使用和成功,Petrobras对它提出了更高的要求,要求能够提供控制处理等特性,这更像一门程序语言了。


几乎在DEL被创建的同一时间,由Roberto Ierusalimschy和Waldemar Celes领导的另一个团队,开始在PGM上面工作,这是一个可配置的用于生成岩石属性文件的生成器,这个产品的客户同样也是Petrobras公司。


PGM生成的报告包含多个列,其中的数据是高度可配置的:用户可以选择颜色、字体、标签等等,同时这些配置信息还可以保存下来重用。于是这个开发团队决定为PGM开发一门语言,称为SOL(Simple Object Language)。


因为PGM会处理很多不同的对象,每个对象都可能有许多不同的属性,因为开发团队决定为这门语言加上类型声明的特性,比如:


type @track{ x:number, y:number=23, id=0 }

type @line{ t:@track=@track{x=8}, z:number* }

T = @track{ y=9, x=10, id="1992-34" }

L = @line{ t=@track{x=T.y, y=T.x}, z=[2,3,4] }


这段代码定义了两种类型,track和line,创建了两个对象,分别是track的类型对象T以及line类型的对象L。track类型有两个数值属性,分别是x和y,以及一个没有类型的属性id。line类型有一个track类型的属性t,其中x的默认值是8,以及一个number列表的类型z。


SOL团队在1993年完成了初期的开发,但是并没有发布这个版本,原因是此时PGM要求在这门语言中支持过程编程的一些特性,这要求SOL需要进行扩展了。


与此同时,在前面提到的DEL语言也遇到了类似的需求。于是在1993年中,Luiz Henrique和Waldemar坐在一起讨论了这两门语言面对的问题和挑战,它需要满足以下在当时考虑到的需求:


  1. 需要是一门真正的编程语言,提供赋值、控制结构、子函数等编程语言的特性,而不仅仅是一门数据描述语言。

  2. 与SOL一样,对数据描述提供便利。

  3. 因为面向的用户,很多都是没有编程经验的人,所以这门语言需要足够的简单和易于上手。

  4. 因为Petrobras公司的很多设备,运行在不同的平台上,所以要求这门语言的可移植性和便携性要足够的好。


在当时,满足这几个需求的语言还不存在,Tcl和Perl语言只能运行在Unix平台,另外语法对外行人也不够友好。


于是他们决定创造一门更强大的编程语言来代替它们。因为这门语言的前身之一是SOL语言,在葡萄牙语中这个单词的意思是“太阳”,他们决定给这门新的语言起名为“Lua”,葡萄牙语的意思是“月亮”。Lua语言就这样诞生了。


Lua语言继承了SOL语言对列表和记录的表示方式,但是从一开始就将两者统一起来,这就是到现在都能看到的,Lua的表既能做为Hash表,也可以做为数组。


1996年对Lua来说是很重要的一年,这一年Lua在国际上获得了关注,开始迎来国际用户。在这一年,作者在《Software: Practice & Experience》杂志上发表了一篇关于Lua的论文,吸引来了不少的关注。这一年的12月,Lua2.5版本发布,《Dr. Dobb’s Journal》杂志也专门针对Lua做了报告,由于这本杂志在程序员圈子里受众非常的多,吸引到了软件业中不少从业者的注意。这其中就包括当时任职于Lucas艺术旗下Grim Fandango游戏项目的主管Bret Mogilefsky。他在自己的项目中,使用Lua替换掉了原来项目用的的脚本语言,后来又在Game Developers’s Conference(简称为GDC,是游戏程序员最重要的会议之一)分享了自己使用Lua的成功经验。从此,Lua在游戏圈就开始流行起来了。这其中包括了后来大获成功的WOW等,如今Lua语言已经是游戏领域使用最广泛的脚本语言之一。


Lua虽然起源于巴西,也是从巴西公司的项目中受驱动而开发的,但是从一开始这门语言的设计者就把眼光投向世界。在很长一段时间里,Lua的文档只有英语版本,而不是作者的母语葡萄牙语。前面提到的1996年发表的论文,同样也可以看做是Lua作者们国际化视野的一个标志。


Lua语言从一开始就将自己定位成一个“嵌入式的脚本语言”,提供了如下的特性:

  1. 可移植性:使用clean C编写的解释器,可以在Mac、Unix、Windows等多个平台轻松编译通过。

  2. 良好的嵌入性:Lua提供了非常丰富的API,可供宿主程序与Lua脚本之间进行通信和交换数据。

  3. 非常小的尺寸:Lua5.1版本的压缩包,仅有208K的大小,解压缩之后也不过是835K,一张软盘就可以装下。Lua解释器的源代码只有17000多行的C代码,编译之后的二进制库文件仅有143K,这些都决定了使用Lua的设备并不会因为添加了它导致非常明显的空间占用。

  4. Lua的效率很高,几乎是脚本语言中速度最快的语言。为了提高Lua的性能,作者们将最初的使用Lex、Yacc等工具自动生成的代码都变成了自己手写的词法分析器和解析器。


这意味着,用户使用C、C++等语言进行主要功能的开发,同时一些需要扩展、配置等会频繁动态变化的部分使用Lua语言来进行开发,Lua语言的以上几个特性,都决定了它能很好的完成这些辅助作用。Lua的作者甚至戏称这门语言是一门能穿过针孔的语言(Passing a Language through the Eye of a Needle),”小而精“大概是对Lua语言最好的描述了。


做为一门从发展中国家起源的语言,从一开始的选择和定位上,Lua都做了在现在看来正确的选择:面向国际,老老实实做好辅助作用。在一个点上做精做细,而不是走大而全的路线去与类似背景的语言进行竞争,这是Lua后来取得巨大成功的原因。这也能理解为什么过去了这么多年,至今Lua解释器的代码只有非常少的代码量(以本书中分析的5.1.4版本来看,全部C代码只有17193行,如果只算核心部分就更少了),这对一门有世界影响的工业级脚本语言,实在不算多的。


这也是我一直很推崇Lua解释器源码,并且决定将这门语言进行分析的原因:Lua解释器的代码是殿堂级的C语言代码范本,Lua作者对语言特性、设计目标、受众的取舍值得我们学习。从一万多行的源码中,就能学习到一门工业级脚本语言的实现,性价比是极高的。


除了在游戏领域的广泛使用,Lua在其他领域也获得了运用:


  • OpenResty使用Lua来扩展Nginx服务器的功能,使用者仅需要编写Lua代码就能轻松完成业务逻辑。值得一提的是,这个项目的作者是中国人章亦春。

  • Redis服务提供Lua脚本。

  • Adobe的Lightroom项目使用Lua来编写插件。




还有很多很多非游戏领域的成功项目,在此不一一列举了。


那么,如何使用Lua语言在你的项目中呢,以笔者比较熟悉的游戏服务器领域来说,一般是这样组织和分工的:

  1. C\C++语言实现的服务器引擎内核,其中包括最核心的功能,包括网络收发,数据库查询,游戏主逻辑循环等。以下将这一层简称为引擎层。

  2. 向引擎层注册一个Lua主逻辑脚本,当接收到用户数据时,将数据包放到Lua脚本中进行处理,主逻辑脚本主要是一个大的函数表,可以根据接收到的协议包的类型,调用相关的函数进行处理。以下将这一层简称为脚本层。

  3. 引擎层向脚本层提供很多API,能方便的调用引擎层的操作,比如脚本层处理完逻辑之后调用引擎层的接口应答数据等等。


可以看到,在这个架构中,引擎层实现了游戏服务的核心功能,这部分的变动相对而言不那么频繁;而游戏的逻辑、玩法是变动很频繁的,这部分使用脚本来完成,这个组合架构的优势在于:


  1. 编码效率高:由于引擎层相对稳定,而脚本不需要进行编译就能直接运行,省去了很多编译的时间。

  2. 开发效率高:大部分脚本,包括Lua在内都支持热更新功能,这意味着在调试开发期间,可以不用停服务器就能调试新的脚本代码,这省去了重启服务的时间,比如加载数据库数据、静态配置文件等等的耗时。

  3. 对人员素质要求相对低:一般的游戏服务器团队配置,都是由主程级别的人来把控引擎的质量,其他的成员负责编写脚本玩法逻辑,即使出错大部分时候并不会导致服务器宕机等严重问题。


参考资料:

《The Evolution of Lua》 Lua作者亲自写的关于Lua语言历史的论文


《From Brazil to Wikipedia》 https://www.foreignaffairs.com/articles/2013-04-21/brazil-wikipedia 


笔者写的Lua源码分析的github开源地址:

https://github.com/lichuang/Lua-Source-Internal


需要说明的是,正式成书的内容会与当前开源的内容有一些修改和补充,比如这里关于Lua历史的描述不在之前的开源内容中。这本书仍然在编写中,希望能在年内正式面市。



 
Codedump的技术笔记 更多文章 微信自研生产级paxos类库PhxPaxos实现原理介绍 分布式网游服务器架构中的对象管理
猜您喜欢 如何获取手机上的已安装的所有应用 Filmie 一次动效和交互的实验 到2030年 科技将彻底改变你的性生活 9月12日微信支付退款规则升级剖析 【ES6入门系列(8)】-函数的扩展