4

有时,在分配了一些资源后,我想在某些地方使用 lua_pushstring,以防万一失败。但是,正如文档似乎暗示的那样,lua_push* 函数总是会以内存不足异常结束。但是该异常会立即退出我的 C 范围,并且不允许我清理我可能临时分配的任何内容,这些内容可能必须在出现错误时释放。

示例代码来说明情况:

void* blubb = malloc(20);
...some other things happening here...
lua_pushstring(L, "test"); //how to do this call safely so I can still take care of blubb?
...possibly more things going on here...
free(blubb);

有没有一种方法可以预先检查是否会发生这种异常,然后在我安全地清理自己的资源后避免推送和执行我自己的错误触发?或者我可以以某种方式简单地停用 setjmp,然后在推送后检查一些“魔术变量”以查看它是否真的有效或触发了错误?

我考虑过 pcall'ing 我自己的函数,但即使只是将我想通过 pcall 安全调用的函数推入堆栈也可能让我内存不足,不是吗?

为了澄清问题,我特别要求将其与自定义内存分配器结合使用,以防止 Lua 分配过多内存,因此假设这不是整个系统内存不足的情况。

4

3 回答 3

2

除非您在创建 Lua 状态时向 Lua 注册了用户定义的内存处理程序,否则出现内存不足错误意味着您的整个应用程序内存不足。从这种状态恢复通常是不可能的。或者至少,在很多情况下是不可行的。这可能取决于您的应用程序,但可能不是。

简而言之,如果它出现了,你有更大的事情需要担心;)

唯一会影响您的清理方式是针对应用程序外部的内容。如果您有一些需要释放或设置某些状态的进程全局内存。您正在进行进程间通信并且您有一些您正在谈论的内存映射文件。或类似的东西。

否则,最好只杀死你的进程。


您可以将 Lua 构建为 C++ 库。当您这样做时,错误将成为实际的异常,您可以捕获或仅使用 RAII 对象来处理。

如果你被 C 困住了……好吧,你无能为力。

我对一个自定义分配器特别感兴趣,它会更早地耗尽内存,以避免 Lua 吃太多内存。

那么你应该以另一种方式处理它。发出内存不足错误的信号基本上是说“我希望 Lua 立即终止”。

阻止 Lua 吃掉内存的方法是定期检查 Lua 状态的内存,如果它使用过多,则进行垃圾回收。如果这不能释放足够的内存,那么你应该手动终止 Lua 状态,但前提是这样做是安全的。

于 2012-04-21T23:12:08.180 回答
0

lua_atpanic()可能是您的一种解决方案,具体取决于您需要进行的清理类型。它永远不会抛出错误。

在您的具体示例中,您还可以将 blubb 创建为userdata。然后 Lua 会在它离开堆栈时自动释放它。

于 2012-04-21T23:01:25.750 回答
0

我最近再次进入了一些 Lua 沙盒,现在我认为我之前接受的答案是一个坏主意。我对此进行了更多思考:

为什么定期检查是不够的

定期检查大量内存消耗并“仅在安全的情况下”终止 Lua 似乎是个坏主意,如果您认为单个巨大的表可以用一条您将要处理的 VM 指令占用大量内存只有在它发生之后才发现——你的程序可能已经死了,然后你确实有更大的问题,如果你一开始就及时停止了分配,你可以完全避免这些问题。

由于 Lua 已经内置了一个很好的内存不足异常,我只想使用它,因为这允许我做最少需要的事情(防止脚本分配更多的东西,同时可能允许它恢复)没有我C 代码从中中断。

因此,我目前对带有内存限制的 Lua 沙盒的计划是:

  • 使用返回 NULL 且有限制的自定义分配器

  • 设计所有 C 函数,使其能够在没有内存泄漏或其他损坏的情况下处理此问题

但是如何安全地设计 C 函数呢?

如何做到这一点,因为 lua_pushstring 和其他人总是可以在我不知道这是否会提前发生的情况下将 jmp 设置为错误?(这原本是我的问题)

我想我找到了一种可行的方法:

我添加了一个工具来在分配指针时注册它们,并在我完成它们后取消注册它们。这意味着如果 Lua 在我没有机会清理的情况下突然将我从我的 C 代码中设置出来,我将在全局列表中拥有所有内容,我需要稍后在我重新控制时清理这些混乱。

是丑还是什么?

是的,这很 hack。但是,它很可能会起作用,并且与“定期检查”不同,它实际上可以让我拥有真正的硬限制,并避免由于攻击性攻击而使应用程序本身陷入困境。

于 2014-09-08T02:02:42.670 回答