Lua 技术说明 1

Lua 3.2 最小安装

本说明解释了如何在内存不足的环境中构建 Lua。

正如在 欢迎页 中明确指出的,我们 Lua 实现的目标之一是低嵌入成本。这意味着两点:首先,将 Lua 嵌入应用程序应该很容易;其次,Lua 的附加代码不应该太大。

第一个要求通过 Lua 的 C API 的简单性来满足。第二个要求通过以下演示来满足。

以下是运行 Linux 的英特尔计算机上编译的 Lua 3.2 的一些数字(其他平台的数字将不同,但相对而言可能大致相同)

% size liblua.a liblualib.a
   text    data     bss     dec     hex filename
   4483     121       0    4604    11fc lapi.o (ex liblua.a)
   1037       0       0    1037     40d lauxlib.o (ex liblua.a)
    345       0       0     345     159 lbuffer.o (ex liblua.a)
   5193     272       0    5465    1559 lbuiltin.o (ex liblua.a)
   3183       0       0    3183     c6f ldo.o (ex liblua.a)
    381       0       0     381     17d lfunc.o (ex liblua.a)
   1363       0       0    1363     553 lgc.o (ex liblua.a)
   5429     108       0    5537    15a1 llex.o (ex liblua.a)
    222       0       0     222      de lmem.o (ex liblua.a)
    686     156       0     842     34a lobject.o (ex liblua.a)
   8560     244       0    8804    2264 lparser.o (ex liblua.a)
    446       4       0     450     1c2 lstate.o (ex liblua.a)
   1845      36       0    1881     759 lstring.o (ex liblua.a)
   1109       0       0    1109     455 ltable.o (ex liblua.a)
   1293     202       0    1495     5d7 ltm.o (ex liblua.a)
   2035       0       0    2035     7f3 lundump.o (ex liblua.a)
   4864       8       0    4872    1308 lvm.o (ex liblua.a)
    336       0       0     336     150 lzio.o (ex liblua.a)
     25       0       0      25      19 linit.o (ex liblualib.a)
   1489      56       0    1545     609 ldblib.o (ex liblualib.a)
   4236     264       0    4500    1194 liolib.o (ex liblualib.a)
   1651     184       0    1835     72b lmathlib.o (ex liblualib.a)
   5277      88       0    5365    14f5 lstrlib.o (ex liblualib.a)
在此列表中,text 实际上是以字节为单位的代码大小。我们得出结论,Lua 核心(liblua.a)占用 42810 字节,Lua 标准库(liblualib.a)占用 12678 字节。因此,整个 Lua 代码占用 55488 字节,或不到 54K。换句话说,Lua 在应用程序中的影响是 54K 的附加代码,这非常小。(当然,Lua 将在运行时使用内存——但具体使用多少取决于应用程序。)

54K 在当今计算机拥有大量主内存的时代似乎非常少,但对于尝试在微波炉或机器人中使用 Lua 的人来说,它们可能会产生差异。因此,让我们看看如何将这 54K 减少到更少。(即使您没有在嵌入式系统中使用 Lua,您也可能从以下描述中学到一些东西。)

首先要删除任何不需要的标准库。例如,大多数应用程序可能不需要 ldblib.o。但删除标准库并不能让你走得太远,因为它们已经很小了。因此,让我们再次查看内核的数字,但现在按大小排序

text     %core   %whole filename
 222     1%      0%     lmem.o
 336     1%      1%     lzio.o
 345     1%      1%     lbuffer.o
 381     1%      1%     lfunc.o
 446     1%      1%     lstate.o
 686     2%      1%     lobject.o
1037     2%      2%     lauxlib.o
1109     3%      2%     ltable.o
1293     3%      2%     ltm.o
1363     3%      2%     lgc.o
1845     4%      3%     lstring.o
2035     5%      4%     lundump.o
3183     7%      6%     ldo.o
4483    10%      8%     lapi.o
4864    11%      9%     lvm.o
5193    12%      9%     lbuiltin.o
5429    13%     10%     llex.o
8560    20%     15%     lparser.o
此列表告诉我们词法分析器 (llex.o) 和解析器 (lparser.o) 占内核的 33%(占整体的 25%)。因此,它们是删除的主要候选者。不需要在运行时编译 Lua 代码的应用程序不需要词法分析器和解析器。

我们设计了代码,以便轻松删除这两个模块。只有一个模块 (ldo.o) 调用解析器,该解析器只有一个公共函数 (luaY_parser)。调用词法分析器的唯一模块是解析器,除了 lua_open 中使用的初始化函数 (luaX_init)。因此,要删除词法分析器和解析器,您只需要将以下代码添加到您的应用程序中(您可以从 lua/src/luac/stubs.c 中提取它,默认情况下它被禁用)

#include "llex.h"
#include "lparser.h"

void luaX_init(void){}

TProtoFunc* luaY_parser(ZIO *z) {
 lua_error("parser not loaded");
 return NULL;
}

包含此代码的应用程序将不会链接 llex.olparser.o,并且尝试加载 Lua 源代码将生成错误。但是,您可能会问,应用程序如何加载 Lua 代码?答案是:通过加载预编译块而不是源代码。预编译块使用 luac 创建,其中包含词法分析器和解析器,但它是一个外部应用程序。加载预编译块的模块是 lundump.o,它足够小。

尽管 lua_dofiledofile 会自动检测预编译块,但一种便捷的方法是将 lua_dobuffer 与静态链接到应用程序的预编译块一起使用(您会发现 lua/etc/bin2c.c 对此很有用),因为嵌入式系统甚至没有文件系统。(这是一个快速的解决方案,但会增加应用程序的大小,并且可能对您来说过于死板。)

移除词法分析器和解析器后,我们只剩下一个仅有 28821 字节的核心,略大于 28K。对于像 Lua 这样强大的语言来说,这确实很小!还要注意,此缩减是在不触及源代码的情况下完成的;我们只需要链接器提供一些帮助。

另一个可以移除的候选对象是 lbuiltin.o,其中包含内置函数。与标准库一样,需要精简 Lua 的应用程序应考虑它真正需要的内置函数。遍历 lbuiltin.c 并移除不需要的函数很容易。它们被分成块,在注释中用匹配的 {...} 标记,因此很容易识别。如果不需要任何内置函数,那么最简单的方法是将

#include "lbuiltin.h"
void luaB_predefine(void){}
添加到上面的存根代码中,并依靠链接器不加载 lbuiltin.o

此说明重点介绍了减少 Lua 库添加到应用程序中的代码量。需要此功能的应用程序可能还更喜欢对 Lua 中的数字使用整数而不是浮点数。(微波炉需要浮点数吗?)这应该很容易做到,如 lua/config 中所述,但详细信息可能会在另一篇 LTN 中讨论。


上次更新:2001 年 2 月 11 日星期日美国东部时间 18:45:01,作者:lhf