一个链接 lua 引起的 bug , 事不过三
今天花了将近 3 个小时帮同事看一个崩在 lua VM 中的 bug 结果打乱了进度,没有在年前把预想的东西做完。其实说起来这不是个大问题,以前也碰到过。我检讨自己没有在看到出错时的调用栈时去看一眼 lua 相关的代码。如果是那样,因为以前遇到过同样的问题,所以就可以条件反射出问题原因,而不用荒废宝贵了数小时时间了。
唉,这下整合的进度没接上,过年不能自己一个人接着做下面的活了。
下面记录一下这个 bug ,提醒自己第三次遇到时不用再花时间找问题:
问题的缘由是因为的不同的 lua 扩展库链接的时候多链接了一份 lua 库,导致进程内有超过一份 lua 库。这是在写 Makefile 时不小心造成的。知道原因后自然很容易解决。要么把 lua 编译成 .so ;要么只有嵌入的主框架使用 -E 参数静态链接 lua 的静态库。这里就不展开说了。
用了 lua 十年,绝对不是第一次帮其他用 lua 的同学指出这个问题。下面要讨论的是错误产生后,程序是怎么崩溃的。
lua 的代码中几乎没有使用全局变量。就是说,lua 的 api 的相关状态仅存在于参数 L 中,而和库无关。这点 lua 实现的是很漂亮的。对于一个无外部状态的 api ,理论上无论代码段在进程内被链接再多次,都不会有副作用的。但副作用的确又发生了,为何?
问题出在 ltable.c 中引用了一个静态全局变量 dummynode_ 。这个东西是做什么用的呢?
在 lua table 实现中,为了不让 hash part 为空的时候不引用 NULL 指针(一种优化),而引用了这个 dummynode ,这样就可以减少操作表时的判断操作。
由于这个 dummynode 是静态分配出来的特殊节点,所以是不能调用内存管理函数去释放它的。lua 另外用一个宏来判断一个节点是否为 dummynode 。
#define isdummy(n) ((n) == dummynode)
当进程中链接了多份 lua 库时,就出现了多份 dummynode 对象,自然 isdummy 的行为就可能不正确了。那么接下来的释放工作则会崩掉,看起来段错误会发生在 lua_Alloc 中。
问题多出现在运行过程中,向空 table 插入第一个 hash 值时,也就是 luaH_resize 函数的最后一行:
if (!isdummy(nold)) luaM_freearray(L, nold, cast(size_t, twoto(oldhsize))); /* free old array */
或是关闭 lua state 时,释放 table 时,的 luaH_free 函数最前面:
if (!isdummy(t->node)) luaM_freearray(L, t->node, cast(size_t, sizenode(t)));
之所以我把代码和出错位置列出来,是因为我今天不是第一次专门分析这个点了。如果我下午在刚出问题时,能够在看到 stack traceback 信息的函数名)luaH_resize 打开 lua 的源代码看一眼的话,我相信我能立刻回想起上次分析这个问题的情景,从而节省掉后来浪费在查别的地方上的无尽时间。
希望经过这次,下次有人向我反应类似问题时,我能直接对应到问题。
建议继续学习:
- Nginx与Lua (阅读:4723)
- Lua GC 的源码剖析 (2) (阅读:3931)
- Ameba , 一个简单的 lua 多线程实现 (阅读:3620)
- Lua GC 的源码剖析 (4) (阅读:3469)
- 使用 luajit 的 ffi 绑定 zeromq (阅读:3388)
- Proto Buffers in Lua (阅读:3173)
- Lua GC 的源码剖析 (1) (阅读:3094)
- 一个 Lua 内存泄露检查工具 (阅读:3062)
- Lua GC 的源码剖析 (6) 完结 (阅读:2671)
- Lua GC 的源码剖析 (3) (阅读:2365)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:云风的 BLOG 来源: 云风的 BLOG
- 标签: lua
- 发布时间:2012-01-24 13:38:13
- [2528] 代理的加密部分
- [1329] 创业笔记 | 从0到1开公司是什么体验
- [649] vimgtd-在vim(gvim)中实现GT
- [574] 查找第K小的元素
- [73] Oracle MTS模式下 进程地址与会话信
- [65] Go Reflect 性能
- [64] 【社会化设计】自我(self)部分――欢迎区
- [64] 如何拿下简短的域名
- [63] 图书馆的世界纪录
- [61] android 开发入门