kevx's Writing

架构 / 云计算 / 技术管理 | kevx@outlook.com 未经授权请勿转载©

Contact Me

LuaJIT在64位环境下的潜在问题

问题背景和描述

我们生产环境有一个数据库支持服务端Lua脚本,近段时间在两台不同机器上出现了总共两次出现进程异常退出,好在生成了coredump,得以有机会窥视内在原因

错误时的异常堆栈信息如下图所有:

结合代码分析,lua库函数lua_open(其实是一个宏,实际函数是luaL_newstate)返回了NULL,这是一个极不寻常的事情,根据官方极为有限的文档可知,lua_open只可能在内存分配失败的时候返回NULL。因此我们首先考虑到机器内存有问题,运维同学检查后并未发现。

问题分析(TL;DR)

此时只能继续深入分析Lua库函数本身的实现,以下代码均来自于LuaJIT的库源代码。

#define lua_open()  luaL_newstate()
...

/**
 * Creates a new context (lua_State) populating it with default values.
 *
 * @return a new lua_State
 */
static lua_State * create_state(context * ctx, const char * filename) {
    lua_State * l   = NULL;

    l = lua_open();
    // 这里返回了NULL
    luaL_openlibs(l);

    ...
}

LUALIB_API lua_State *luaL_newstate(void)
{
    lua_State *L;
    void *ud = lj_alloc_create(); // 实际上是因为这里返回NULL
    if (ud == NULL) return NULL;
    ...
}


void *lj_alloc_create(void)
{
  size_t tsize = DEFAULT_GRANULARITY;
  char *tbase;
  INIT_MMAP();
  tbase = (char *)(CALL_MMAP(tsize));
  if (tbase != CMFAIL) {
    ...
  }
  return NULL;
}

经过GDB调试,最终发现mmap返回了-1,errno错误码12,查阅内核文档表示ENOMEM 12 Cannot allocate memory,因此这个原因是非常清晰明确的,这里的mmap启用了MAP_32BIT标记,此时内核将只在低32位地址空间内分配内存,尽管32位地址最大寻址允许4GB,但Linux内核当前设计决定了这里实际只有1G地址可用且没有可调性

Lua JIT为什么要这么设计?

根据官方说法,Lua针对的是小而美场景,不适合运行大型复杂脚本,更不适合创建大量对象,在64位环境下,若不启用MAP_32BIT则Lua的GC性能会受到严重影响

返回主页