技术说明 2

原文:https://www.lua.org/notes/ltn002.html

Technical Note 2 技术说明 2

Last update: Mon Aug 20 14:35:00 EST 2001

Minimal Lua 4.0 installations - Lua 4.0 最小安装

This note explains how to build Lua for environments that do not have much memory, such as embedded systems. This is a version of Technical Note 1, updated for Lua 4.0.

​ 本说明解释了如何在内存不足的环境(例如嵌入式系统)中构建 Lua。这是技术说明 1 的版本,已针对 Lua 4.0 更新。

As explicitly stated in the “about” page, one of the goals of our Lua implementation is low embedding cost. This means two things: first, it should be easy to embed Lua in an application; second, the additional code for Lua should not be too large.

​ 正如“关于”页面中明确指出的,我们的 Lua 实现目标之一是降低嵌入成本。这意味着两件事:首先,应该很容易将 Lua 嵌入到应用程序中;其次,Lua 的附加代码不应太大。

The first requirement is fulfilled by the simplicity of Lua’s C API. The second requirement is fulfilled by demonstration, as follows.

​ 第一个要求由 Lua 的 C API 的简单性来满足。第二个要求由演示来满足,如下所示。

Here are a few numbers for Lua 4.0, compiled with the default options in an Intel machine running Linux (the numbers for other platforms will be different, but probably roughly the same in relative terms):

​ 以下是一些针对 Lua 4.0 的数字,这些数字是在运行 Linux 的 Intel 机器中使用默认选项编译的(其他平台的数字会有所不同,但相对而言可能大致相同):

% size liblua.a
   text	   data	    bss	    dec	    hex	filename
   3328	      0	      0	   3328	    d00	lapi.o (ex liblua.a)
   4054	      0	      0	   4054	    fd6	lcode.o (ex liblua.a)
   3031	      0	      0	   3031	    bd7	ldebug.o (ex liblua.a)
   2372	      0	      0	   2372	    944	ldo.o (ex liblua.a)
    574	      0	      0	    574	    23e	lfunc.o (ex liblua.a)
   1874	      0	      0	   1874	    752	lgc.o (ex liblua.a)
   4909	      0	      0	   4909	   132d	llex.o (ex liblua.a)
    225	      0	      0	    225	     e1	lmem.o (ex liblua.a)
    734	      0	      0	    734	    2de	lobject.o (ex liblua.a)
   7634	      0	      0	   7634	   1dd2	lparser.o (ex liblua.a)
    598	      0	      0	    598	    256	lstate.o (ex liblua.a)
    953	      0	      0	    953	    3b9	lstring.o (ex liblua.a)
   1651	      0	      0	   1651	    673	ltable.o (ex liblua.a)
      0	      0	      0	      0	      0	ltests.o (ex liblua.a)
   1495	      0	      0	   1495	    5d7	ltm.o (ex liblua.a)
   2491	      0	      0	   2491	    9bb	lundump.o (ex liblua.a)
   5487	      0	      0	   5487	   156f	lvm.o (ex liblua.a)
    336	      0	      0	    336	    150	lzio.o (ex liblua.a)
% size liblualib.a
   text	   data	    bss	    dec	    hex	filename
   1437	      0	      0	   1437	    59d	lauxlib.o (ex liblualib.a)
   5619	      0	      0	   5619	   15f3	lbaselib.o (ex liblualib.a)
   1674	      0	      2	   1676	    68c	ldblib.o (ex liblualib.a)
   5288	      0	      0	   5288	   14a8	liolib.o (ex liblualib.a)
   2301	      0	      0	   2301	    8fd	lmathlib.o (ex liblualib.a)
   6209	      0	      0	   6209	   1841	lstrlib.o (ex liblualib.a)

In this listing, text actually is the size of the code in bytes. We conclude that the Lua core (liblua.a) takes 41746 bytes and that the Lua standard libraries (liblualib.a) take 22528 bytes. So, the whole Lua code takes 64274 bytes, or less than 63K. In other words, the impact of Lua in an application is 63K of additional code, which is quite small. (Of course, Lua will use memory at run-time – but how much depends on the application.)

​ 在此列表中, text 实际上是以字节为单位的代码大小。我们得出结论,Lua 核心( liblua.a )占用 41746 字节,Lua 标准库( liblualib.a )占用 22528 字节。因此,整个 Lua 代码占用 64274 字节,或不到 63K。换句话说,Lua 在应用程序中的影响是 63K 的附加代码,这非常小。(当然,Lua 在运行时会使用内存——但具体使用多少取决于应用程序。)

63K seem very little in these days that machines have many megabytes of main memory, but they might make a difference for someone trying to use Lua in a microwave oven or in a robot. So, let’s see how to reduce these 63K to even less. (Even if you’re not using Lua in embedded systems, you might learn something from the description below.)

​ 如今,机器拥有许多兆字节的主内存,63K 似乎非常少,但对于尝试在微波炉或机器人中使用 Lua 的人来说,它们可能会有所不同。因此,让我们看看如何将这 63K 减少得更少。(即使您不在嵌入式系统中使用 Lua,您也可能会从以下说明中学到一些东西。)

The first thing is to remove any standard libraries that are not needed. For instance, ldblib.o will probably not be needed in most applications, and liolib.o will probably not make sense for a microwave oven. But removing standard libraries will not get you very far, because they are small already. So, let’s look at the numbers for the core again, but now sorted by size:

​ 首先要删除任何不需要的标准库。例如, ldblib.o 可能在大多数应用程序中都不需要,而 liolib.o 对于微波炉来说可能没有意义。但删除标准库并不能让您走得太远,因为它们已经很小了。因此,让我们再次查看内核的数字,但现在按大小排序:

text    %core   %whole   filename
   0     0%      0%      ltests.o
 225     1%      0%      lmem.o
 336     1%      1%      lzio.o
 574     1%      1%      lfunc.o
 598     1%      1%      lstate.o
 734     2%      1%      lobject.o
 953     2%      1%      lstring.o
1495     4%      2%      ltm.o
1651     4%      3%      ltable.o
1874     4%      3%      lgc.o
2372     6%      4%      ldo.o
2491     6%      4%      lundump.o
3031     7%      5%      ldebug.o
3328     8%      5%      lapi.o
4054    10%      6%      lcode.o
4909    12%      8%      llex.o
5487    13%      9%      lvm.o
7634    18%     12%      lparser.o

This listing tells us that the parsing modules – the lexer llex.o, the parser lparser.o, and the code generator lcode.o – represent 40% of the core (and 26% of the whole). So, they are the main candidates for removal. An application that does not need to compile Lua code at run-time does not need the parsing modules.

​ 此列表告诉我们解析模块——词法分析器 llex.o 、解析器 lparser.o 和代码生成器 lcode.o ——占核心部分的 40%(占整体的 26%)。因此,它们是移除的主要候选对象。不需要在运行时编译 Lua 代码的应用程序不需要解析模块。

We have designed the code so that it is easy to remove these three modules. Only one module (ldo.o) calls the parser, which has only one public function (luaY_parser). The only module that calls the lexer is the parser, except for an initialization function (luaX_init) used in lua_open. The only module that calls the code generator is the parser, except that ldebug.o uses the array luaK_opproperties from lcode.o. So, to remove the parsing modules you only need to add the code below to your application (you can extract it from lua/src/luac/stubs.c, where it is disabled by default):

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

#include "llex.h"
#include "lparser.h"
void luaX_init(lua_State *L) {
  UNUSED(L);
}
Proto *luaY_parser(lua_State *L, ZIO *z) {
  UNUSED(z);
  lua_error(L,"parser not loaded");
  return NULL;
}

To remove the code generator too, you need to add #include "lcode.h" and copy luaK_opproperties from lcode.c into this code.

​ 要移除代码生成器,您需要添加 #include "lcode.h" 并从 lcode.c 中将 luaK_opproperties 复制到此代码中。

An application that contains the code above will not link the parsing modules and trying to load Lua source code will generate an error. But then, you ask, how does the application load Lua code at all? The answer is: by loading precompiled chunks instead of source code. Precompiled chunks are created with luac, which will contain the parsing modules, but which is an external application. The module that loads precompiled chunks is lundump.o, which is small enough.

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

Although lua_dofile and dofile automatically detect precompiled chunks, one convenient way is to use lua_dobuffer with precompiled chunks statically linked to your application (you will find lua/etc/bin2c.c useful for this), because embedded systems don’t even have filesystems. (This is a quick solution, but it will increase the size of your application and it may be too inflexible for you.)

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

Removing the parsing modules leaves us with a core of just 25296 bytes, a little more than 24K. This is tiny indeed for a powerful language like Lua! Note also that this reduction was done without sacrificing any language features and without touching the source code; we just need a little help from the linker.

​ 去掉解析模块后,只剩下一个 25296 字节的核心,略多于 24K。对于像 Lua 这样强大的语言来说,这确实很小!还要注意,这种缩减是在不牺牲任何语言特性和不触碰源代码的情况下完成的;我们只需要链接器提供一点帮助即可。

This note has focused on reducing the amount of code added to the application by the Lua library. Applications that need this will probably also prefer to use integers instead of floating-point numbers for the numbers in Lua. (Does a microwave oven need floating-point?) This should be simple to do, as described in lua/config, but the details will probably be discussed in another LTN.

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

最后修改 January 31, 2024: 更新 (e0896df)