Lua 5.4中lua_resume触发段错误的原因排查请求
Lua 5.4 C API lua_resume段错误排查与修复
核心错误原因
局部变量误当全局变量访问
Lua代码中test函数里的co是局部变量,并非全局变量。在finishpcall中调用lua_getglobal(L, "co")会将nil压入栈,后续lua_tothread(L, 2)返回NULL,调用lua_resume(NULL, ...)直接触发段错误——这是导致崩溃的直接原因。续延函数调用逻辑错误
luaB_pcall中在调用lua_pcallk后主动调用了finishpcall,违背了Lua C API中lua_pcallk的设计逻辑:续延函数finishpcall只会在lua_pcallk执行过程中发生yield时由Lua虚拟机自动调用,手动调用会导致栈状态异常,进一步加剧错误。lua_resume参数无效
两次lua_resume调用都使用了无效的线程指针:第一次取栈索引2的元素是nil,第二次取栈顶元素也是nil,传入NULL给lua_resume必然触发段错误。
修正方案
步骤1:修正Lua代码,将co转为全局变量
修改Lua字符串中的test函数,把局部co改为全局:
function test() co = coroutine.create(function() print('me') coroutine.yield() print('me me') end) return co end pcall(test)
步骤2:修正luaB_pcall的逻辑,移除主动调用finishpcall
lua_pcallk的返回值已经是执行状态,不需要手动调用续延函数:
static int luaB_pcall (lua_State *L) { int status; luaL_checkany(L, 1); // 直接返回lua_pcallk的结果,续延函数会在yield时自动被调用 return lua_pcallk(L, lua_gettop(L) - 1, LUA_MULTRET, 0, 0, finishpcall); }
步骤3:确保lua_resume使用有效线程指针
在finishpcall中先检查线程指针是否有效:
static int finishpcall (lua_State *L, int status, intptr_t ctx) { (void)ctx; // 正确判断状态:status为LUA_OK或LUA_YIELD时表示成功 int success = (status == LUA_OK || status == LUA_YIELD); lua_pushboolean(L, success); lua_insert(L, 1); printf("From lua, %d\n", lua_gettop(L)); lua_getglobal(L, "co"); // 检查是否为有效协程线程 lua_State *co_thread = lua_tothread(L, 2); if (co_thread != NULL) { // 第一次resume协程,执行到yield lua_resume(co_thread, NULL, 0, 0); // 第二次resume,执行剩余代码 lua_resume(co_thread, NULL, 0, 0); } return lua_gettop(L); }
修正后执行流程
- Lua代码中创建全局协程
co,调用pcall(test)触发C层的luaB_pcall。 lua_pcallk执行test函数,返回协程对象到栈,状态为LUA_OK,续延函数finishpcall被调用。finishpcall中获取全局协程,两次调用lua_resume:第一次输出me并yield,第二次输出me me。- 最终程序正常执行,无段错误。
内容的提问来源于stack exchange,提问作者kos
相关产品推荐
相关产品推荐

