有时候,我想在分配一些资源之后在地方使用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分配太多内存,因此假设这不是整个系统内存不足的情况。
答案 0 :(得分:2)
除非您在创建Lua状态时使用Lua注册了用户定义的内存处理程序,否则出现内存不足错误意味着整个应用程序内存不足。从这种状态恢复通常是不可能的。或者至少,在很多情况下都不可行。它可能取决于您的应用程序,但可能不是。
简而言之,如果它出现了,你就会关注更大的事情;)
应该影响您的唯一类型的清理是针对应用程序外部的事情。如果你有一些进程全局内存,你需要释放或设置一些状态。你正在进行进程间通信,你有一些你正在谈论的内存映射文件。或类似的东西。
否则,杀死你的过程可能会更好。
您可以将Lua构建为C ++库。执行此操作时,错误将成为实际异常,您可以捕获或仅使用RAII对象来处理。
如果你坚持使用C ......那么你就无能为力了。
我特别感兴趣的是一个自定义分配器,它会在更早的时候内存不足以避免Lua吃太多内存。
然后你应该以另一种方式处理它。发出内存不足错误的信号基本上就是说,“我希望Lua立即终止。”
阻止Lua吃掉记忆的方法是定期检查Lua状态的内存,如果它使用太多则垃圾收集它。如果这没有释放足够的内存,那么你应该手动终止Lua状态,但只有在安全的情况下才能这样做。
答案 1 :(得分:0)
lua_atpanic()可能是您的一种解决方案,具体取决于您需要进行的清理工作。它永远不会抛出错误。
在您的具体示例中,您还可以将blubb创建为userdata。然后Lua会在离开堆栈时自动释放它。
答案 2 :(得分:0)
我最近再次进入了一些Lua沙盒,现在我认为我之前接受的答案是一个坏主意。我已经多考虑了一下:
为什么定期检查还不够
如果你认为单个巨大的表可能会占用大量的内存而一个VM,那么定期检查大内存消耗并“仅在安全的情况下终止Lua”似乎是一个坏主意指令关于它只能在它发生之后才能找到 - 你的程序可能已经从中死了,然后你确实有更大的问题你可以避免完全如果你在第一时间及时停止了这个分配。
由于Lua已经内置了一个很好的内存不足异常,我只想使用那个,因为这样可以让我做所需的最小事情(防止脚本分配更多的东西,同时可能让它恢复)没有我的C代码破坏它。
因此,我目前对具有内存限制的Lua沙盒的计划是:
使用返回NULL且带限制的自定义分配器
设计所有C函数,以便能够在没有内存泄漏或其他破坏的情况下处理此问题
但如何安全地设计C函数?
如何做到这一点,因为lua_pushstring和其他人总是可以设置错误而不知道这是否会提前发生? (这原本是我的问题)
我认为我找到了一种有效的方法:
我在分配指针时添加了一个注册指针的工具,并在我完成它之后取消注册它们。这意味着如果Lua在没有机会清理的情况下突然将我从我的C代码中解脱出来,我将所有内容放在一个全局列表中,我需要在我重新掌控之后清理那些混乱。
那是丑陋还是什么?
是的,这完全是黑客攻击。但是,它很可能会起作用,而且与“定期检查”不同,它实际上允许我有一个真正的硬限制,并避免由于攻击性攻击而使应用程序本身出现问题。